Skip to content

Commit 88962fb

Browse files
authored
Add EIP7702Utils (#5587)
1 parent a6ae04a commit 88962fb

File tree

4 files changed

+82
-3
lines changed

4 files changed

+82
-3
lines changed

.changeset/full-ways-help.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'openzeppelin-solidity': minor
3+
---
4+
5+
`EIP7702Utils`: Add a library for checking if an address has an EIP-7702 delegation in place.
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
// SPDX-License-Identifier: MIT
2+
3+
pragma solidity ^0.8.20;
4+
5+
/**
6+
* @dev Library with common EIP-7702 utility functions.
7+
*
8+
* See https://eips.ethereum.org/EIPS/eip-7702[ERC-7702].
9+
*/
10+
library EIP7702Utils {
11+
bytes3 internal constant EIP7702_PREFIX = 0xef0100;
12+
13+
/**
14+
* @dev Returns the address of the delegate if `account` as an EIP-7702 delegation setup, or address(0) otherwise.
15+
*/
16+
function fetchDelegate(address account) internal view returns (address) {
17+
bytes23 delegation = bytes23(account.code);
18+
return bytes3(delegation) == EIP7702_PREFIX ? address(bytes20(delegation << 24)) : address(0);
19+
}
20+
}

contracts/mocks/Stateless.sol

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,13 +19,14 @@ import {Clones} from "../proxy/Clones.sol";
1919
import {Create2} from "../utils/Create2.sol";
2020
import {DoubleEndedQueue} from "../utils/structs/DoubleEndedQueue.sol";
2121
import {ECDSA} from "../utils/cryptography/ECDSA.sol";
22+
import {EIP7702Utils} from "../account/utils/EIP7702Utils.sol";
2223
import {EnumerableMap} from "../utils/structs/EnumerableMap.sol";
2324
import {EnumerableSet} from "../utils/structs/EnumerableSet.sol";
24-
import {ERC1155Holder} from "../token/ERC1155/utils/ERC1155Holder.sol";
2525
import {ERC165} from "../utils/introspection/ERC165.sol";
2626
import {ERC165Checker} from "../utils/introspection/ERC165Checker.sol";
27-
import {ERC1967Utils} from "../proxy/ERC1967/ERC1967Utils.sol";
2827
import {ERC721Holder} from "../token/ERC721/utils/ERC721Holder.sol";
28+
import {ERC1155Holder} from "../token/ERC1155/utils/ERC1155Holder.sol";
29+
import {ERC1967Utils} from "../proxy/ERC1967/ERC1967Utils.sol";
2930
import {ERC4337Utils} from "../account/utils/draft-ERC4337Utils.sol";
3031
import {ERC7579Utils} from "../account/utils/draft-ERC7579Utils.sol";
3132
import {Heap} from "../utils/structs/Heap.sol";
@@ -35,8 +36,8 @@ import {MessageHashUtils} from "../utils/cryptography/MessageHashUtils.sol";
3536
import {Nonces} from "../utils/Nonces.sol";
3637
import {NoncesKeyed} from "../utils/NoncesKeyed.sol";
3738
import {P256} from "../utils/cryptography/P256.sol";
38-
import {Panic} from "../utils/Panic.sol";
3939
import {Packing} from "../utils/Packing.sol";
40+
import {Panic} from "../utils/Panic.sol";
4041
import {RSA} from "../utils/cryptography/RSA.sol";
4142
import {SafeCast} from "../utils/math/SafeCast.sol";
4243
import {SafeERC20} from "../token/ERC20/utils/SafeERC20.sol";
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
const { ethers, config } = require('hardhat');
2+
const { expect } = require('chai');
3+
const { loadFixture } = require('@nomicfoundation/hardhat-network-helpers');
4+
5+
// [NOTE]
6+
//
7+
// ethers.getSigners() returns object than cannot currently send type-4 transaction, or sign authorization. Therefore,
8+
// we have to instantiate the eoa AND the relayer manually using ethers 6.14.0 wallets. This can be improved when
9+
// @nomicfoundation/hardhat-ethers starts instantiating signers with 7702 support.
10+
const relayAuthorization = authorization =>
11+
ethers.Wallet.fromPhrase(config.networks.hardhat.accounts.mnemonic, ethers.provider).sendTransaction({
12+
to: ethers.ZeroAddress,
13+
authorizationList: [authorization],
14+
gasLimit: 46_000n,
15+
});
16+
17+
const fixture = async () => {
18+
const eoa = ethers.Wallet.createRandom(ethers.provider);
19+
const mock = await ethers.deployContract('$EIP7702Utils');
20+
return { eoa, mock };
21+
};
22+
23+
describe('EIP7702Utils', function () {
24+
beforeEach(async function () {
25+
Object.assign(this, await loadFixture(fixture));
26+
});
27+
28+
describe('fetchDelegate', function () {
29+
it('EOA without delegation', async function () {
30+
await expect(this.mock.$fetchDelegate(this.eoa)).to.eventually.equal(ethers.ZeroAddress);
31+
});
32+
33+
it('EOA with delegation', async function () {
34+
// set delegation
35+
await this.eoa.authorize({ address: this.mock }).then(relayAuthorization);
36+
37+
await expect(this.mock.$fetchDelegate(this.eoa)).to.eventually.equal(this.mock);
38+
});
39+
40+
it('EOA with revoked delegation', async function () {
41+
// set delegation
42+
await this.eoa.authorize({ address: this.mock }).then(relayAuthorization);
43+
// reset delegation
44+
await this.eoa.authorize({ address: ethers.ZeroAddress }).then(relayAuthorization);
45+
46+
await expect(this.mock.$fetchDelegate(this.eoa)).to.eventually.equal(ethers.ZeroAddress);
47+
});
48+
49+
it('other smart contract', async function () {
50+
await expect(this.mock.$fetchDelegate(this.mock)).to.eventually.equal(ethers.ZeroAddress);
51+
});
52+
});
53+
});

0 commit comments

Comments
 (0)