diff --git a/contracts/account/utils/draft-ERC7579Utils.sol b/contracts/account/utils/draft-ERC7579Utils.sol index 28aa64d5c..c0b02edb3 100644 --- a/contracts/account/utils/draft-ERC7579Utils.sol +++ b/contracts/account/utils/draft-ERC7579Utils.sol @@ -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); } diff --git a/test/account/utils/draft-ERC7579Utils.test.js b/test/account/utils/draft-ERC7579Utils.test.js index 14ca889e3..b0ca86c46 100644 --- a/test/account/utils/draft-ERC7579Utils.test.js +++ b/test/account/utils/draft-ERC7579Utils.test.js @@ -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(