Skip to content

Commit

Permalink
Merge branch 'develop' into SimpleAccount-factory-optim
Browse files Browse the repository at this point in the history
  • Loading branch information
drortirosh committed Aug 29, 2023
2 parents feadca6 + da37cc2 commit 90eb7bb
Show file tree
Hide file tree
Showing 5 changed files with 87 additions and 14 deletions.
15 changes: 14 additions & 1 deletion contracts/core/EntryPoint.sol
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,16 @@ import "./StakeManager.sol";
import "./SenderCreator.sol";
import "./Helpers.sol";
import "./NonceManager.sol";

// we also require '@gnosis.pm/safe-contracts' and both libraries have 'IERC165.sol', leading to conflicts
import "@openzeppelin/contracts/utils/introspection/ERC165.sol" as OpenZeppelin;
import "@openzeppelin/contracts/security/ReentrancyGuard.sol";

/*
* Account-Abstraction (EIP-4337) singleton EntryPoint implementation.
* Only one instance required on each chain.
*/
contract EntryPoint is IEntryPoint, StakeManager, NonceManager, ReentrancyGuard {
contract EntryPoint is IEntryPoint, StakeManager, NonceManager, ReentrancyGuard, OpenZeppelin.ERC165 {

using UserOperationLib for UserOperation;

Expand All @@ -39,6 +42,16 @@ contract EntryPoint is IEntryPoint, StakeManager, NonceManager, ReentrancyGuard
*/
uint256 public constant SIG_VALIDATION_FAILED = 1;

/// @inheritdoc OpenZeppelin.IERC165
function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
// note: solidity "type(IEntryPoint).interfaceId" is without inherited methods but we want to check everything
return interfaceId == (type(IEntryPoint).interfaceId ^ type(IStakeManager).interfaceId ^ type(INonceManager).interfaceId) ||
interfaceId == type(IEntryPoint).interfaceId ||
interfaceId == type(IStakeManager).interfaceId ||
interfaceId == type(INonceManager).interfaceId ||
super.supportsInterface(interfaceId);
}

/**
* Compensate the caller's beneficiary address with the collected fees of all UserOperations.
* @param beneficiary - The address to receive the fees.
Expand Down
1 change: 1 addition & 0 deletions eip/EIPS/eip-4337.md
Original file line number Diff line number Diff line change
Expand Up @@ -382,6 +382,7 @@ While simulating `userOp` validation, the client should make sure that:
4. cannot call EntryPoint's methods, except `depositFor` (to avoid recursion)
5. `EXTCODEHASH` of every address accessed (by any opcode) does not change between first and second simulations of the op.
6. `EXTCODEHASH`, `EXTCODELENGTH`, `EXTCODECOPY` may not access address with no code.
The exception is accessing the `sender` address from the `factory` contract as it is guaranteed to deploy the `sender`.
7. If `op.initcode.length != 0` , allow only one `CREATE2` opcode call (in the first (deployment) block), otherwise forbid `CREATE2`.
Transient Storage slots defined in [EIP-1153](./eip-1153.md) and accessed using `TLOAD` (`0x5c`) and `TSTORE` (`0x5d`) opcodes
Expand Down
26 changes: 13 additions & 13 deletions reports/gas-checker.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,36 +4,36 @@
╔══════════════════════════╤════════╗
║ gas estimate "simple" │ 28989 ║
╟──────────────────────────┼────────╢
║ gas estimate "big tx 5k" │ 125234
║ gas estimate "big tx 5k" │ 125222
╚══════════════════════════╧════════╝

╔════════════════════════════════╤═══════╤═══════════════╤════════════════╤═════════════════════╗
║ handleOps description │ count │ total gasUsed │ per UserOp gas │ per UserOp overhead ║
║ │ │ │ (delta for │ (compared to ║
║ │ │ │ one UserOp) │ account.exec()) ║
╟────────────────────────────────┼───────┼───────────────┼────────────────┼─────────────────────╢
║ simple │ 1 │ 81874 │ │ ║
║ simple │ 1 │ 81918 │ │ ║
╟────────────────────────────────┼───────┼───────────────┼────────────────┼─────────────────────╢
║ simple - diff from previous │ 2 │ │ 4416115172
║ simple - diff from previous │ 2 │ │ 4417115182
╟────────────────────────────────┼───────┼───────────────┼────────────────┼─────────────────────╢
║ simple │ 10 │ 479464 │ │ ║
║ simple │ 10 │ 479718 │ │ ║
╟────────────────────────────────┼───────┼───────────────┼────────────────┼─────────────────────╢
║ simple - diff from previous │ 11 │ │ 4420915220
║ simple - diff from previous │ 11 │ │ 4419515206
╟────────────────────────────────┼───────┼───────────────┼────────────────┼─────────────────────╢
║ simple paymaster │ 1 │ 88155 │ │ ║
║ simple paymaster │ 1 │ 88199 │ │ ║
╟────────────────────────────────┼───────┼───────────────┼────────────────┼─────────────────────╢
║ simple paymaster with diff │ 2 │ │ 4314814159
║ simple paymaster with diff │ 2 │ │ 4317014181
╟────────────────────────────────┼───────┼───────────────┼────────────────┼─────────────────────╢
║ simple paymaster │ 10 │ 476668 │ │ ║
║ simple paymaster │ 10 │ 476886 │ │ ║
╟────────────────────────────────┼───────┼───────────────┼────────────────┼─────────────────────╢
║ simple paymaster with diff │ 11 │ │ 4317114182
║ simple paymaster with diff │ 11 │ │ 4322914240
╟────────────────────────────────┼───────┼───────────────┼────────────────┼─────────────────────╢
║ big tx 5k │ 1 │ 182919 │ │ ║
║ big tx 5k │ 1 │ 182939 │ │ ║
╟────────────────────────────────┼───────┼───────────────┼────────────────┼─────────────────────╢
║ big tx - diff from previous │ 2 │ │ 14468419450
║ big tx - diff from previous │ 2 │ │ 14465819436
╟────────────────────────────────┼───────┼───────────────┼────────────────┼─────────────────────╢
║ big tx 5k │ 10 │ 1485024 │ │ ║
║ big tx 5k │ 10 │ 1485278 │ │ ║
╟────────────────────────────────┼───────┼───────────────┼────────────────┼─────────────────────╢
║ big tx - diff from previous │ 11 │ │ 14473319499
║ big tx - diff from previous │ 11 │ │ 14474319521
╚════════════════════════════════╧═══════╧═══════════════╧════════════════╧═════════════════════╝

16 changes: 16 additions & 0 deletions src/Utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { Interface, JsonFragment } from '@ethersproject/abi'

export function getERC165InterfaceID (abi: JsonFragment[]): string {
let interfaceId =
abi
.filter(it => it.type === 'function' && it.name != null)
.map(it => {
const iface = new Interface([it])
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
return iface.getSighash(it.name!)
})
.map((x) => parseInt(x, 16))
.reduce((x, y) => x ^ y)
interfaceId = interfaceId > 0 ? interfaceId : 0xFFFFFFFF + interfaceId + 1
return '0x' + interfaceId.toString(16).padStart(8, '0')
}
43 changes: 43 additions & 0 deletions test/entrypoint.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,11 @@ import {
TestSignatureAggregator__factory,
MaliciousAccount__factory,
TestWarmColdAccount__factory,
IEntryPoint__factory,
SimpleAccountFactory__factory,
IStakeManager__factory,
INonceManager__factory,
EntryPoint__factory,
TestPaymasterRevertCustomError__factory
} from '../typechain'
import {
Expand Down Expand Up @@ -53,6 +58,7 @@ import { arrayify, defaultAbiCoder, hexConcat, hexZeroPad, parseEther } from 'et
import { debugTransaction } from './debugTx'
import { BytesLike } from '@ethersproject/bytes'
import { toChecksumAddress } from 'ethereumjs-util'
import { getERC165InterfaceID } from '../src/Utils'

describe('EntryPoint', function () {
let entryPoint: EntryPoint
Expand Down Expand Up @@ -1362,4 +1368,41 @@ describe('EntryPoint', function () {
})
})
})

describe('ERC-165', function () {
it('should return true for IEntryPoint interface ID', async function () {
const iepInterface = IEntryPoint__factory.createInterface()
const iepInterfaceID = getERC165InterfaceID([...iepInterface.fragments])
expect(await entryPoint.supportsInterface(iepInterfaceID)).to.equal(true)
})

it('should return true for pure EntryPoint, IStakeManager and INonceManager interface IDs', async function () {
const epInterface = EntryPoint__factory.createInterface()
const smInterface = IStakeManager__factory.createInterface()
const nmInterface = INonceManager__factory.createInterface()
// note: manually generating "pure", solidity-like "type(IEntryPoint).interfaceId" without inherited methods
const epPureInterfaceFunctions = [
...epInterface.fragments.filter(it => [
'handleOps',
'handleAggregatedOps',
'getUserOpHash',
'getSenderAddress',
'simulateValidation',
'simulateHandleOp'
].includes(it.name))
]
const epPureInterfaceID = getERC165InterfaceID(epPureInterfaceFunctions)
const smInterfaceID = getERC165InterfaceID([...smInterface.fragments])
const nmInterfaceID = getERC165InterfaceID([...nmInterface.fragments])
expect(await entryPoint.supportsInterface(smInterfaceID)).to.equal(true)
expect(await entryPoint.supportsInterface(nmInterfaceID)).to.equal(true)
expect(await entryPoint.supportsInterface(epPureInterfaceID)).to.equal(true)
})

it('should return false for a wrong interface', async function () {
const saInterface = SimpleAccountFactory__factory.createInterface()
const entryPointInterfaceID = getERC165InterfaceID([...saInterface.fragments])
expect(await entryPoint.supportsInterface(entryPointInterfaceID)).to.equal(false)
})
})
})

0 comments on commit 90eb7bb

Please sign in to comment.