Skip to content

Commit

Permalink
Allow SimpleAccount to transfer native token with executeBatch (#281
Browse files Browse the repository at this point in the history
)

* Allow `SimpleAccount` to transfer native token with `executeBatch`

* Allow value-free transfers using zero-length array of value
  • Loading branch information
kaiix authored Jul 22, 2023
1 parent ecb25e2 commit 0a9d55c
Show file tree
Hide file tree
Showing 4 changed files with 66 additions and 17 deletions.
15 changes: 11 additions & 4 deletions contracts/samples/SimpleAccount.sol
Original file line number Diff line number Diff line change
Expand Up @@ -61,12 +61,19 @@ contract SimpleAccount is BaseAccount, TokenCallbackHandler, UUPSUpgradeable, In

/**
* execute a sequence of transactions
* @dev to reduce gas consumption for trivial case (no value), use a zero-length array to mean zero value
*/
function executeBatch(address[] calldata dest, bytes[] calldata func) external {
function executeBatch(address[] calldata dest, uint256[] calldata value, bytes[] calldata func) external {
_requireFromEntryPointOrOwner();
require(dest.length == func.length, "wrong array lengths");
for (uint256 i = 0; i < dest.length; i++) {
_call(dest[i], 0, func[i]);
require(dest.length == func.length && (value.length == 0 || value.length == func.length), "wrong array lengths");
if (value.length == 0) {
for (uint256 i = 0; i < dest.length; i++) {
_call(dest[i], 0, func[i]);
}
} else {
for (uint256 i = 0; i < dest.length; i++) {
_call(dest[i], value[i], func[i]);
}
}
}

Expand Down
24 changes: 12 additions & 12 deletions reports/gas-checker.txt
Original file line number Diff line number Diff line change
Expand Up @@ -12,28 +12,28 @@
║ │ │ │ (delta for │ (compared to ║
║ │ │ │ one UserOp) │ account.exec()) ║
╟────────────────────────────────┼───────┼───────────────┼────────────────┼─────────────────────╢
║ simple │ 1 │ 81901 │ │ ║
║ simple │ 1 │ 81867 │ │ ║
╟────────────────────────────────┼───────┼───────────────┼────────────────┼─────────────────────╢
║ simple - diff from previous │ 2 │ │ 4421215198
║ simple - diff from previous │ 2 │ │ 4416615152
╟────────────────────────────────┼───────┼───────────────┼────────────────┼─────────────────────╢
║ simple │ 10 │ 479854 │ │ ║
║ simple │ 10 │ 479598 │ │ ║
╟────────────────────────────────┼───────┼───────────────┼────────────────┼─────────────────────╢
║ simple - diff from previous │ 11 │ │ 4423615222
║ simple - diff from previous │ 11 │ │ 4422615212
╟────────────────────────────────┼───────┼───────────────┼────────────────┼─────────────────────╢
║ simple paymaster │ 1 │ 88172 │ │ ║
║ simple paymaster │ 1 │ 88150 │ │ ║
╟────────────────────────────────┼───────┼───────────────┼────────────────┼─────────────────────╢
║ simple paymaster with diff │ 2 │ │ 4316514151
║ simple paymaster with diff │ 2 │ │ 4316714153
╟────────────────────────────────┼───────┼───────────────┼────────────────┼─────────────────────╢
║ simple paymaster │ 10 │ 476994 │ │ ║
║ simple paymaster │ 10 │ 476858 │ │ ║
╟────────────────────────────────┼───────┼───────────────┼────────────────┼─────────────────────╢
║ simple paymaster with diff │ 11 │ │ 4326014246
║ simple paymaster with diff │ 11 │ │ 4316614152
╟────────────────────────────────┼───────┼───────────────┼────────────────┼─────────────────────╢
║ big tx 5k │ 1 │ 182958 │ │ ║
║ big tx 5k │ 1 │ 182936 │ │ ║
╟────────────────────────────────┼───────┼───────────────┼────────────────┼─────────────────────╢
║ big tx - diff from previous │ 2 │ │ 14472319463
║ big tx - diff from previous │ 2 │ │ 14466519405
╟────────────────────────────────┼───────┼───────────────┼────────────────┼─────────────────────╢
║ big tx 5k │ 10 │ 1485438 │ │ ║
║ big tx 5k │ 10 │ 1485218 │ │ ║
╟────────────────────────────────┼───────┼───────────────┼────────────────┼─────────────────────╢
║ big tx - diff from previous │ 11 │ │ 14471219452
║ big tx - diff from previous │ 11 │ │ 14475019490
╚════════════════════════════════╧═══════╧═══════════════╧════════════════╧═════════════════════╝

2 changes: 1 addition & 1 deletion test/deposit-paymaster.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ describe.skip('DepositPaymaster', () => {
const paymasterWithdraw = await paymaster.populateTransaction.withdrawTokensTo(token.address, AddressZero, 1).then(tx => tx.data!)

await expect(
account.executeBatch([paymaster.address, paymaster.address], [paymasterUnlock, paymasterWithdraw])
account.executeBatch([paymaster.address, paymaster.address], [], [paymasterUnlock, paymasterWithdraw])
).to.be.revertedWith('DepositPaymaster: must unlockTokenDeposit')
})
it('should succeed to withdraw after unlock', async () => {
Expand Down
42 changes: 42 additions & 0 deletions test/simple-wallet.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import {
SimpleAccount,
SimpleAccountFactory__factory,
SimpleAccount__factory,
TestCounter,
TestCounter__factory,
TestUtil,
TestUtil__factory
} from '../typechain'
Expand Down Expand Up @@ -56,6 +58,46 @@ describe('SimpleAccount', function () {
expect(await testUtil.packUserOp(op)).to.equal(packed)
})

describe('#executeBatch', () => {
let account: SimpleAccount
let counter: TestCounter
before(async () => {
({ proxy: account } = await createAccount(ethersSigner, await ethersSigner.getAddress(), entryPoint))
counter = await new TestCounter__factory(ethersSigner).deploy()
})

it('should allow zero value array', async () => {
const counterJustEmit = await counter.populateTransaction.justemit().then(tx => tx.data!)
const rcpt = await account.executeBatch(
[counter.address, counter.address],
[],
[counterJustEmit, counterJustEmit]
).then(async t => await t.wait())
const targetLogs = await counter.queryFilter(counter.filters.CalledFrom(), rcpt.blockHash)
expect(targetLogs.length).to.eq(2)
})

it('should allow transfer value', async () => {
const counterJustEmit = await counter.populateTransaction.justemit().then(tx => tx.data!)
const target = createAddress()
await ethersSigner.sendTransaction({ from: accounts[0], to: account.address, value: parseEther('2') })
const rcpt = await account.executeBatch(
[target, counter.address],
[ONE_ETH, 0],
['0x', counterJustEmit]
).then(async t => await t.wait())
expect(await ethers.provider.getBalance(target)).to.equal(ONE_ETH)
const targetLogs = await counter.queryFilter(counter.filters.CalledFrom(), rcpt.blockHash)
expect(targetLogs.length).to.eq(1)
})

it('should fail with wrong array length', async () => {
const counterJustEmit = await counter.populateTransaction.justemit().then(tx => tx.data!)
await expect(account.executeBatch([counter.address, counter.address], [0], [counterJustEmit, counterJustEmit]))
.to.be.revertedWith('wrong array lengths')
})
})

describe('#validateUserOp', () => {
let account: SimpleAccount
let userOp: UserOperation
Expand Down

0 comments on commit 0a9d55c

Please sign in to comment.