Implement ERC-7821 calldata compression in ERC7579Utils (#5602)

This commit is contained in:
Hadrien Croubois
2025-03-27 09:27:34 +01:00
committed by GitHub
parent a31b4a438a
commit 952775e4f1
2 changed files with 49 additions and 4 deletions

View File

@ -218,7 +218,9 @@ library ERC7579Utils {
uint256 value,
bytes calldata data
) private returns (bytes memory) {
(bool success, bytes memory returndata) = target.call{value: value}(data);
(bool success, bytes memory returndata) = (target == address(0) ? address(this) : target).call{value: value}(
data
);
return _validateExecutionMode(index, execType, success, returndata);
}
@ -229,7 +231,7 @@ library ERC7579Utils {
address target,
bytes calldata data
) private returns (bytes memory) {
(bool success, bytes memory returndata) = target.delegatecall(data);
(bool success, bytes memory returndata) = (target == address(0) ? address(this) : target).delegatecall(data);
return _validateExecutionMode(index, execType, success, returndata);
}

View File

@ -2,13 +2,14 @@ const { ethers } = require('hardhat');
const { expect } = require('chai');
const { loadFixture } = require('@nomicfoundation/hardhat-network-helpers');
const {
CALL_TYPE_CALL,
CALL_TYPE_BATCH,
CALL_TYPE_DELEGATE,
EXEC_TYPE_DEFAULT,
EXEC_TYPE_TRY,
encodeSingle,
encodeBatch,
encodeDelegate,
CALL_TYPE_CALL,
CALL_TYPE_BATCH,
encodeMode,
} = require('../../helpers/erc7579');
const { selector } = require('../../helpers/methods');
@ -29,6 +30,14 @@ describe('ERC7579Utils', function () {
Object.assign(this, await loadFixture(fixture));
});
it('constants', async function () {
await expect(this.utils.$CALLTYPE_SINGLE()).to.eventually.equal(CALL_TYPE_CALL);
await expect(this.utils.$CALLTYPE_BATCH()).to.eventually.equal(CALL_TYPE_BATCH);
await expect(this.utils.$CALLTYPE_DELEGATECALL()).to.eventually.equal(CALL_TYPE_DELEGATE);
await expect(this.utils.$EXECTYPE_DEFAULT()).to.eventually.equal(EXEC_TYPE_DEFAULT);
await expect(this.utils.$EXECTYPE_TRY()).to.eventually.equal(EXEC_TYPE_TRY);
});
describe('execSingle', function () {
it('calls the target with value', async function () {
const value = 0x012;
@ -54,6 +63,18 @@ describe('ERC7579Utils', function () {
await expect(ethers.provider.getBalance(this.target)).to.eventually.equal(value);
});
it('default to calling self is target is address(0) (ERC-7821 calldata compression)', async function () {
const data = encodeSingle(
ethers.ZeroAddress, // address(0)
0,
this.utils.interface.encodeFunctionData('$CALLTYPE_SINGLE', []),
);
await expect(this.utils.$execSingle(data, EXEC_TYPE_DEFAULT))
.to.emit(this.utils, 'return$execSingle')
.withArgs([ethers.zeroPadBytes(CALL_TYPE_CALL, 32)]);
});
it('reverts when target reverts in default ExecType', async function () {
const value = 0x012;
const data = encodeSingle(
@ -131,6 +152,17 @@ describe('ERC7579Utils', function () {
await expect(ethers.provider.getBalance(this.anotherTarget)).to.eventually.equal(value2);
});
it('default to calling self is target is address(0) (ERC-7821 calldata compression)', async function () {
const data = encodeBatch(
[ethers.ZeroAddress, 0, this.utils.interface.encodeFunctionData('$CALLTYPE_SINGLE', [])],
[ethers.ZeroAddress, 0, this.utils.interface.encodeFunctionData('$CALLTYPE_BATCH', [])],
);
await expect(this.utils.$execBatch(data, EXEC_TYPE_DEFAULT))
.to.emit(this.utils, 'return$execBatch')
.withArgs([ethers.zeroPadBytes(CALL_TYPE_CALL, 32), ethers.zeroPadBytes(CALL_TYPE_BATCH, 32)]);
});
it('reverts when any target reverts in default ExecType', async function () {
const value1 = 0x012;
const value2 = 0x234;
@ -193,6 +225,17 @@ describe('ERC7579Utils', function () {
await expect(ethers.provider.getStorage(this.utils.target, slot)).to.eventually.equal(value);
});
it('default to calling self is target is address(0) (ERC-7821 calldata compression)', async function () {
const data = encodeDelegate(
ethers.ZeroAddress,
this.utils.interface.encodeFunctionData('$CALLTYPE_DELEGATECALL', []),
);
await expect(this.utils.$execDelegateCall(data, EXEC_TYPE_DEFAULT))
.to.emit(this.utils, 'return$execDelegateCall')
.withArgs([ethers.zeroPadBytes(CALL_TYPE_DELEGATE, 32)]);
});
it('reverts when target reverts in default ExecType', async function () {
const data = encodeDelegate(this.target, this.target.interface.encodeFunctionData('mockFunctionRevertsReason'));
await expect(this.utils.$execDelegateCall(data, EXEC_TYPE_DEFAULT)).to.be.revertedWith(