Skip to content

Commit

Permalink
Fix conflicts (?)
Browse files Browse the repository at this point in the history
  • Loading branch information
ernestognw committed Nov 25, 2024
2 parents 00a4489 + 0df841d commit 33bbf24
Show file tree
Hide file tree
Showing 11 changed files with 179 additions and 49 deletions.
29 changes: 26 additions & 3 deletions contracts/account/utils/draft-ERC4337Utils.sol
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,16 @@ library ERC4337Utils {
return result;
}

/// @dev Returns `factory` from the {PackedUserOperation}, or address(0) if the initCode is empty or not properly formatted.
function factory(PackedUserOperation calldata self) internal pure returns (address) {
return self.initCode.length < 20 ? address(0) : address(bytes20(self.initCode[0:20]));
}

/// @dev Returns `factoryData` from the {PackedUserOperation}, or empty bytes if the initCode is empty or not properly formatted.
function factoryData(PackedUserOperation calldata self) internal pure returns (bytes calldata) {
return self.initCode.length < 20 ? _emptyCalldataBytes() : self.initCode[20:];
}

/// @dev Returns `verificationGasLimit` from the {PackedUserOperation}.
function verificationGasLimit(PackedUserOperation calldata self) internal pure returns (uint256) {
return uint128(self.accountGasLimits.extract_32_16(0));
Expand Down Expand Up @@ -130,16 +140,29 @@ library ERC4337Utils {

/// @dev Returns the first section of `paymasterAndData` from the {PackedUserOperation}.
function paymaster(PackedUserOperation calldata self) internal pure returns (address) {
return address(bytes20(self.paymasterAndData[0:20]));
return self.paymasterAndData.length < 52 ? address(0) : address(bytes20(self.paymasterAndData[0:20]));
}

/// @dev Returns the second section of `paymasterAndData` from the {PackedUserOperation}.
function paymasterVerificationGasLimit(PackedUserOperation calldata self) internal pure returns (uint256) {
return uint128(bytes16(self.paymasterAndData[20:36]));
return self.paymasterAndData.length < 52 ? 0 : uint128(bytes16(self.paymasterAndData[20:36]));
}

/// @dev Returns the third section of `paymasterAndData` from the {PackedUserOperation}.
function paymasterPostOpGasLimit(PackedUserOperation calldata self) internal pure returns (uint256) {
return uint128(bytes16(self.paymasterAndData[36:52]));
return self.paymasterAndData.length < 52 ? 0 : uint128(bytes16(self.paymasterAndData[36:52]));
}

/// @dev Returns the forth section of `paymasterAndData` from the {PackedUserOperation}.
function paymasterData(PackedUserOperation calldata self) internal pure returns (bytes calldata) {
return self.paymasterAndData.length < 52 ? _emptyCalldataBytes() : self.paymasterAndData[52:];
}

// slither-disable-next-line write-after-write
function _emptyCalldataBytes() private pure returns (bytes calldata result) {
assembly ("memory-safe") {
result.offset := 0
result.length := 0
}
}
}
5 changes: 4 additions & 1 deletion contracts/account/utils/draft-ERC7579Utils.sol
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,10 @@ library ERC7579Utils {
/// @dev Execution type that does not revert on failure.
ExecType internal constant EXECTYPE_TRY = ExecType.wrap(0x01);

/// @dev Emits when an {EXECTYPE_TRY} execution fails.
/**
* @dev Emits when an {EXECTYPE_TRY} execution fails.
* @param batchExecutionIndex The index of the failed transaction in the execution batch.
*/
event ERC7579TryExecuteFail(uint256 batchExecutionIndex, bytes result);

/// @dev The provided {CallType} is not supported.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,10 @@ abstract contract GovernorCountingOverridable is GovernorVotes {
mapping(address voter => VoteReceipt) voteReceipt;
}

event VoteReduced(address indexed voter, uint256 proposalId, uint8 support, uint256 weight);
/// @dev The votes casted by `delegate` were reduced by `weight` after an override vote was casted by the original token holder
event VoteReduced(address indexed delegate, uint256 proposalId, uint8 support, uint256 weight);

/// @dev A delegated vote on `proposalId` was overridden by `weight`
event OverrideVoteCast(address indexed voter, uint256 proposalId, uint8 support, uint256 weight, string reason);

error GovernorAlreadyOverridenVote(address account);
Expand Down
21 changes: 11 additions & 10 deletions contracts/governance/utils/Votes.sol
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,15 @@ abstract contract Votes is Context, EIP712, Nonces, IERC5805 {
return "mode=blocknumber&from=default";
}

/**
* @dev Validate that a timepoint is in the past, and return it as a uint48.
*/
function _validateTimepoint(uint256 timepoint) internal view returns (uint48) {
uint48 currentTimepoint = clock();
if (timepoint >= currentTimepoint) revert ERC5805FutureLookup(timepoint, currentTimepoint);
return SafeCast.toUint48(timepoint);
}

/**
* @dev Returns the current amount of votes that `account` has.
*/
Expand All @@ -87,11 +96,7 @@ abstract contract Votes is Context, EIP712, Nonces, IERC5805 {
* - `timepoint` must be in the past. If operating using block numbers, the block must be already mined.
*/
function getPastVotes(address account, uint256 timepoint) public view virtual returns (uint256) {
uint48 currentTimepoint = clock();
if (timepoint >= currentTimepoint) {
revert ERC5805FutureLookup(timepoint, currentTimepoint);
}
return _delegateCheckpoints[account].upperLookupRecent(SafeCast.toUint48(timepoint));
return _delegateCheckpoints[account].upperLookupRecent(_validateTimepoint(timepoint));
}

/**
Expand All @@ -107,11 +112,7 @@ abstract contract Votes is Context, EIP712, Nonces, IERC5805 {
* - `timepoint` must be in the past. If operating using block numbers, the block must be already mined.
*/
function getPastTotalSupply(uint256 timepoint) public view virtual returns (uint256) {
uint48 currentTimepoint = clock();
if (timepoint >= currentTimepoint) {
revert ERC5805FutureLookup(timepoint, currentTimepoint);
}
return _totalCheckpoints.upperLookupRecent(SafeCast.toUint48(timepoint));
return _totalCheckpoints.upperLookupRecent(_validateTimepoint(timepoint));
}

/**
Expand Down
39 changes: 26 additions & 13 deletions contracts/governance/utils/VotesExtended.sol
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,30 @@ import {SafeCast} from "../../utils/math/SafeCast.sol";

/**
* @dev Extension of {Votes} that adds checkpoints for delegations and balances.
*
* WARNING: While this contract extends {Votes}, valid uses of {Votes} may not be compatible with
* {VotesExtended} without additional considerations. This implementation of {_transferVotingUnits} must
* run AFTER the voting weight movement is registered, such that it is reflected on {_getVotingUnits}.
*
* Said differently, {VotesExtended} MUST be integrated in a way that calls {_transferVotingUnits} AFTER the
* asset transfer is registered and balances are updated:
*
* ```solidity
* contract VotingToken is Token, VotesExtended {
* function transfer(address from, address to, uint256 tokenId) public override {
* super.transfer(from, to, tokenId); // <- Perform the transfer first ...
* _transferVotingUnits(from, to, 1); // <- ... then call _transferVotingUnits.
* }
*
* function _getVotingUnits(address account) internal view override returns (uint256) {
* return balanceOf(account);
* }
* }
* ```
*
* {ERC20Votes} and {ERC721Votes} follow this pattern and are thus safe to use with {VotesExtended}.
*/
abstract contract VotesExtended is Votes {
using SafeCast for uint256;
using Checkpoints for Checkpoints.Trace160;
using Checkpoints for Checkpoints.Trace208;

Expand All @@ -25,11 +46,7 @@ abstract contract VotesExtended is Votes {
* - `timepoint` must be in the past. If operating using block numbers, the block must be already mined.
*/
function getPastDelegate(address account, uint256 timepoint) public view virtual returns (address) {
uint48 currentTimepoint = clock();
if (timepoint >= currentTimepoint) {
revert ERC5805FutureLookup(timepoint, currentTimepoint);
}
return address(_userDelegationCheckpoints[account].upperLookupRecent(timepoint.toUint48()));
return address(_userDelegationCheckpoints[account].upperLookupRecent(_validateTimepoint(timepoint)));
}

/**
Expand All @@ -41,11 +58,7 @@ abstract contract VotesExtended is Votes {
* - `timepoint` must be in the past. If operating using block numbers, the block must be already mined.
*/
function getPastBalanceOf(address account, uint256 timepoint) public view virtual returns (uint256) {
uint48 currentTimepoint = clock();
if (timepoint >= currentTimepoint) {
revert ERC5805FutureLookup(timepoint, currentTimepoint);
}
return _userVotingWeightCheckpoints[account].upperLookupRecent(timepoint.toUint48());
return _userVotingWeightCheckpoints[account].upperLookupRecent(_validateTimepoint(timepoint));
}

/// @inheritdoc Votes
Expand All @@ -60,10 +73,10 @@ abstract contract VotesExtended is Votes {
super._transferVotingUnits(from, to, amount);
if (from != to) {
if (from != address(0)) {
_userVotingWeightCheckpoints[from].push(clock(), _getVotingUnits(from).toUint208());
_userVotingWeightCheckpoints[from].push(clock(), SafeCast.toUint208(_getVotingUnits(from)));
}
if (to != address(0)) {
_userVotingWeightCheckpoints[to].push(clock(), _getVotingUnits(to).toUint208());
_userVotingWeightCheckpoints[to].push(clock(), SafeCast.toUint208(_getVotingUnits(to)));
}
}
}
Expand Down
2 changes: 2 additions & 0 deletions contracts/interfaces/draft-IERC4337.sol
Original file line number Diff line number Diff line change
Expand Up @@ -143,11 +143,13 @@ interface IEntryPoint is IEntryPointNonces, IEntryPointStake {

/**
* @dev Executes a batch of user operations.
* @param beneficiary Address to which gas is refunded up completing the execution.
*/
function handleOps(PackedUserOperation[] calldata ops, address payable beneficiary) external;

/**
* @dev Executes a batch of aggregated user operations per aggregator.
* @param beneficiary Address to which gas is refunded up completing the execution.
*/
function handleAggregatedOps(
UserOpsPerAggregator[] calldata opsPerAggregator,
Expand Down
28 changes: 28 additions & 0 deletions contracts/interfaces/draft-IERC7579.sol
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ uint256 constant MODULE_TYPE_EXECUTOR = 2;
uint256 constant MODULE_TYPE_FALLBACK = 3;
uint256 constant MODULE_TYPE_HOOK = 4;

/// @dev Minimal configuration interface for ERC-7579 modules
interface IERC7579Module {
/**
* @dev This function is called by the smart account during installation of the module
Expand All @@ -36,6 +37,11 @@ interface IERC7579Module {
function isModuleType(uint256 moduleTypeId) external view returns (bool);
}

/**
* @dev ERC-7579 Validation module (type 1).
*
* A module that implements logic to validate user operations and signatures.
*/
interface IERC7579Validator is IERC7579Module {
/**
* @dev Validates a UserOperation
Expand All @@ -44,6 +50,7 @@ interface IERC7579Validator is IERC7579Module {
*
* MUST validate that the signature is a valid signature of the userOpHash
* SHOULD return ERC-4337's SIG_VALIDATION_FAILED (and not revert) on signature mismatch
* See ERC-4337 for additional information on the return value
*/
function validateUserOp(PackedUserOperation calldata userOp, bytes32 userOpHash) external returns (uint256);

Expand All @@ -63,6 +70,12 @@ interface IERC7579Validator is IERC7579Module {
) external view returns (bytes4);
}

/**
* @dev ERC-7579 Hooks module (type 4).
*
* A module that implements logic to execute before and after the account executes a user operation,
* either individually or batched.
*/
interface IERC7579Hook is IERC7579Module {
/**
* @dev Called by the smart account before execution
Expand Down Expand Up @@ -93,6 +106,11 @@ struct Execution {
bytes callData;
}

/**
* @dev ERC-7579 Execution.
*
* Accounts should implement this interface so that the Entrypoint and ERC-7579 modules can execute operations.
*/
interface IERC7579Execution {
/**
* @dev Executes a transaction on behalf of the account.
Expand All @@ -119,6 +137,11 @@ interface IERC7579Execution {
) external returns (bytes[] memory returnData);
}

/**
* @dev ERC-7579 Account Config.
*
* Accounts should implement this interface to exposes information that identifies the account, supported modules and capabilities.
*/
interface IERC7579AccountConfig {
/**
* @dev Returns the account id of the smart account
Expand Down Expand Up @@ -148,6 +171,11 @@ interface IERC7579AccountConfig {
function supportsModule(uint256 moduleTypeId) external view returns (bool);
}

/**
* @dev ERC-7579 Module Config.
*
* Accounts should implement this interface to allows installing and uninstalling modules.
*/
interface IERC7579ModuleConfig {
event ModuleInstalled(uint256 moduleTypeId, address module);
event ModuleUninstalled(uint256 moduleTypeId, address module);
Expand Down
62 changes: 52 additions & 10 deletions test/account/utils/draft-ERC4337Utils.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,13 @@ const { ethers } = require('hardhat');
const { expect } = require('chai');
const { loadFixture } = require('@nomicfoundation/hardhat-network-helpers');

const { packValidationData, packPaymasterData, UserOperation } = require('../../helpers/erc4337');
const { packValidationData, UserOperation } = require('../../helpers/erc4337');
const { MAX_UINT48 } = require('../../helpers/constants');

const fixture = async () => {
const [authorizer, sender, entrypoint, paymaster] = await ethers.getSigners();
const [authorizer, sender, entrypoint, factory, paymaster] = await ethers.getSigners();
const utils = await ethers.deployContract('$ERC4337Utils');
return { utils, authorizer, sender, entrypoint, paymaster };
return { utils, authorizer, sender, entrypoint, factory, paymaster };
};

describe('ERC4337Utils', function () {
Expand Down Expand Up @@ -144,6 +144,33 @@ describe('ERC4337Utils', function () {
});

describe('userOp values', function () {
describe('intiCode', function () {
beforeEach(async function () {
this.userOp = new UserOperation({
sender: this.sender,
nonce: 1,
verificationGas: 0x12345678n,
factory: this.factory,
factoryData: '0x123456',
});

this.emptyUserOp = new UserOperation({
sender: this.sender,
nonce: 1,
});
});

it('returns factory', async function () {
expect(this.utils.$factory(this.userOp.packed)).to.eventually.equal(this.factory);
expect(this.utils.$factory(this.emptyUserOp.packed)).to.eventually.equal(ethers.ZeroAddress);
});

it('returns factoryData', async function () {
expect(this.utils.$factoryData(this.userOp.packed)).to.eventually.equal('0x123456');
expect(this.utils.$factoryData(this.emptyUserOp.packed)).to.eventually.equal('0x');
});
});

it('returns verificationGasLimit', async function () {
const userOp = new UserOperation({ sender: this.sender, nonce: 1, verificationGas: 0x12345678n });
expect(this.utils.$verificationGasLimit(userOp.packed)).to.eventually.equal(userOp.verificationGas);
Expand Down Expand Up @@ -176,28 +203,43 @@ describe('ERC4337Utils', function () {

describe('paymasterAndData', function () {
beforeEach(async function () {
this.verificationGasLimit = 0x12345678n;
this.postOpGasLimit = 0x87654321n;
this.paymasterAndData = packPaymasterData(this.paymaster, this.verificationGasLimit, this.postOpGasLimit);
this.userOp = new UserOperation({
sender: this.sender,
nonce: 1,
paymasterAndData: this.paymasterAndData,
paymaster: this.paymaster,
paymasterVerificationGasLimit: 0x12345678n,
paymasterPostOpGasLimit: 0x87654321n,
paymasterData: '0xbeefcafe',
});

this.emptyUserOp = new UserOperation({
sender: this.sender,
nonce: 1,
});
});

it('returns paymaster', async function () {
expect(this.utils.$paymaster(this.userOp.packed)).to.eventually.equal(this.paymaster);
expect(this.utils.$paymaster(this.userOp.packed)).to.eventually.equal(this.userOp.paymaster);
expect(this.utils.$paymaster(this.emptyUserOp.packed)).to.eventually.equal(ethers.ZeroAddress);
});

it('returns verificationGasLimit', async function () {
expect(this.utils.$paymasterVerificationGasLimit(this.userOp.packed)).to.eventually.equal(
this.verificationGasLimit,
this.userOp.paymasterVerificationGasLimit,
);
expect(this.utils.$paymasterVerificationGasLimit(this.emptyUserOp.packed)).to.eventually.equal(0n);
});

it('returns postOpGasLimit', async function () {
expect(this.utils.$paymasterPostOpGasLimit(this.userOp.packed)).to.eventually.equal(this.postOpGasLimit);
expect(this.utils.$paymasterPostOpGasLimit(this.userOp.packed)).to.eventually.equal(
this.userOp.paymasterPostOpGasLimit,
);
expect(this.utils.$paymasterPostOpGasLimit(this.emptyUserOp.packed)).to.eventually.equal(0n);
});

it('returns data', async function () {
expect(this.utils.$paymasterData(this.userOp.packed)).to.eventually.equal(this.userOp.paymasterData);
expect(this.utils.$paymasterData(this.emptyUserOp.packed)).to.eventually.equal('0x');
});
});
});
Expand Down
Loading

0 comments on commit 33bbf24

Please sign in to comment.