123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145 |
- const { ethers, predeploy } = require('hardhat');
- const { expect } = require('chai');
- const { CALL_TYPE_BATCH, encodeMode, encodeBatch } = require('../../helpers/erc7579');
- function shouldBehaveLikeERC7821({ deployable = true } = {}) {
- describe('supports ERC-7821', function () {
- beforeEach(async function () {
- // give eth to the account (before deployment)
- await this.other.sendTransaction({ to: this.mock.target, value: ethers.parseEther('1') });
- // account is not initially deployed
- await expect(ethers.provider.getCode(this.mock)).to.eventually.equal('0x');
- this.encodeUserOpCalldata = (...calls) =>
- this.mock.interface.encodeFunctionData('execute', [
- encodeMode({ callType: CALL_TYPE_BATCH }),
- encodeBatch(...calls),
- ]);
- });
- it('should revert if the caller is not the canonical entrypoint or the account itself', async function () {
- await this.mock.deploy();
- await expect(
- this.mock.connect(this.other).execute(
- encodeMode({ callType: CALL_TYPE_BATCH }),
- encodeBatch({
- target: this.target,
- data: this.target.interface.encodeFunctionData('mockFunctionExtra'),
- }),
- ),
- )
- .to.be.revertedWithCustomError(this.mock, 'AccountUnauthorized')
- .withArgs(this.other);
- });
- if (deployable) {
- describe('when not deployed', function () {
- it('should be created with handleOps and increase nonce', async function () {
- const operation = await this.mock
- .createUserOp({
- callData: this.encodeUserOpCalldata({
- target: this.target,
- value: 17,
- data: this.target.interface.encodeFunctionData('mockFunctionExtra'),
- }),
- })
- .then(op => op.addInitCode())
- .then(op => this.signUserOp(op));
- // Can't call the account to get its nonce before it's deployed
- await expect(predeploy.entrypoint.v08.getNonce(this.mock.target, 0)).to.eventually.equal(0);
- await expect(predeploy.entrypoint.v08.handleOps([operation.packed], this.beneficiary))
- .to.emit(predeploy.entrypoint.v08, 'AccountDeployed')
- .withArgs(operation.hash(), this.mock, this.helper.factory, ethers.ZeroAddress)
- .to.emit(this.target, 'MockFunctionCalledExtra')
- .withArgs(this.mock, 17);
- await expect(this.mock.getNonce()).to.eventually.equal(1);
- });
- it('should revert if the signature is invalid', async function () {
- const operation = await this.mock
- .createUserOp({
- callData: this.encodeUserOpCalldata({
- target: this.target,
- value: 17,
- data: this.target.interface.encodeFunctionData('mockFunctionExtra'),
- }),
- })
- .then(op => op.addInitCode());
- operation.signature = '0x00';
- await expect(predeploy.entrypoint.v08.handleOps([operation.packed], this.beneficiary)).to.be.reverted;
- });
- });
- }
- describe('when deployed', function () {
- beforeEach(async function () {
- await this.mock.deploy();
- });
- it('should increase nonce and call target', async function () {
- const operation = await this.mock
- .createUserOp({
- callData: this.encodeUserOpCalldata({
- target: this.target,
- value: 42,
- data: this.target.interface.encodeFunctionData('mockFunctionExtra'),
- }),
- })
- .then(op => this.signUserOp(op));
- await expect(this.mock.getNonce()).to.eventually.equal(0);
- await expect(predeploy.entrypoint.v08.handleOps([operation.packed], this.beneficiary))
- .to.emit(this.target, 'MockFunctionCalledExtra')
- .withArgs(this.mock, 42);
- await expect(this.mock.getNonce()).to.eventually.equal(1);
- });
- it('should support sending eth to an EOA', async function () {
- const operation = await this.mock
- .createUserOp({ callData: this.encodeUserOpCalldata({ target: this.other, value: 42 }) })
- .then(op => this.signUserOp(op));
- await expect(this.mock.getNonce()).to.eventually.equal(0);
- await expect(predeploy.entrypoint.v08.handleOps([operation.packed], this.beneficiary)).to.changeEtherBalance(
- this.other,
- 42,
- );
- await expect(this.mock.getNonce()).to.eventually.equal(1);
- });
- it('should support batch execution', async function () {
- const value1 = 43374337n;
- const value2 = 69420n;
- const operation = await this.mock
- .createUserOp({
- callData: this.encodeUserOpCalldata(
- { target: this.other, value: value1 },
- {
- target: this.target,
- value: value2,
- data: this.target.interface.encodeFunctionData('mockFunctionExtra'),
- },
- ),
- })
- .then(op => this.signUserOp(op));
- await expect(this.mock.getNonce()).to.eventually.equal(0);
- const tx = predeploy.entrypoint.v08.handleOps([operation.packed], this.beneficiary);
- await expect(tx).to.changeEtherBalances([this.other, this.target], [value1, value2]);
- await expect(tx).to.emit(this.target, 'MockFunctionCalledExtra').withArgs(this.mock, value2);
- await expect(this.mock.getNonce()).to.eventually.equal(1);
- });
- });
- });
- }
- module.exports = {
- shouldBehaveLikeERC7821,
- };
|