Merge account abstraction work into master (#5274)

Co-authored-by: Ernesto García <ernestognw@gmail.com>
Co-authored-by: Elias Rad <146735585+nnsW3@users.noreply.github.com>
Co-authored-by: cairo <cairoeth@protonmail.com>
Co-authored-by: Arr00 <13561405+arr00@users.noreply.github.com>
This commit is contained in:
Hadrien Croubois
2024-10-23 09:19:13 +02:00
committed by GitHub
parent 2fa4d103fe
commit 28aed34dc5
21 changed files with 2494 additions and 95 deletions

95
test/helpers/erc4337.js Normal file
View File

@ -0,0 +1,95 @@
const { ethers } = require('hardhat');
const SIG_VALIDATION_SUCCESS = '0x0000000000000000000000000000000000000000';
const SIG_VALIDATION_FAILURE = '0x0000000000000000000000000000000000000001';
function getAddress(account) {
return account.target ?? account.address ?? account;
}
function pack(left, right) {
return ethers.solidityPacked(['uint128', 'uint128'], [left, right]);
}
function packValidationData(validAfter, validUntil, authorizer) {
return ethers.solidityPacked(
['uint48', 'uint48', 'address'],
[
validAfter,
validUntil,
typeof authorizer == 'boolean'
? authorizer
? SIG_VALIDATION_SUCCESS
: SIG_VALIDATION_FAILURE
: getAddress(authorizer),
],
);
}
function packPaymasterData(paymaster, verificationGasLimit, postOpGasLimit) {
return ethers.solidityPacked(
['address', 'uint128', 'uint128'],
[getAddress(paymaster), verificationGasLimit, postOpGasLimit],
);
}
/// Represent one user operation
class UserOperation {
constructor(params) {
this.sender = getAddress(params.sender);
this.nonce = params.nonce;
this.initCode = params.initCode ?? '0x';
this.callData = params.callData ?? '0x';
this.verificationGas = params.verificationGas ?? 10_000_000n;
this.callGas = params.callGas ?? 100_000n;
this.preVerificationGas = params.preVerificationGas ?? 100_000n;
this.maxPriorityFee = params.maxPriorityFee ?? 100_000n;
this.maxFeePerGas = params.maxFeePerGas ?? 100_000n;
this.paymasterAndData = params.paymasterAndData ?? '0x';
this.signature = params.signature ?? '0x';
}
get packed() {
return {
sender: this.sender,
nonce: this.nonce,
initCode: this.initCode,
callData: this.callData,
accountGasLimits: pack(this.verificationGas, this.callGas),
preVerificationGas: this.preVerificationGas,
gasFees: pack(this.maxPriorityFee, this.maxFeePerGas),
paymasterAndData: this.paymasterAndData,
signature: this.signature,
};
}
hash(entrypoint, chainId) {
const p = this.packed;
const h = ethers.keccak256(
ethers.AbiCoder.defaultAbiCoder().encode(
['address', 'uint256', 'bytes32', 'bytes32', 'uint256', 'uint256', 'uint256', 'uint256'],
[
p.sender,
p.nonce,
ethers.keccak256(p.initCode),
ethers.keccak256(p.callData),
p.accountGasLimits,
p.preVerificationGas,
p.gasFees,
ethers.keccak256(p.paymasterAndData),
],
),
);
return ethers.keccak256(
ethers.AbiCoder.defaultAbiCoder().encode(['bytes32', 'address', 'uint256'], [h, getAddress(entrypoint), chainId]),
);
}
}
module.exports = {
SIG_VALIDATION_SUCCESS,
SIG_VALIDATION_FAILURE,
packValidationData,
packPaymasterData,
UserOperation,
};

58
test/helpers/erc7579.js Normal file
View File

@ -0,0 +1,58 @@
const { ethers } = require('hardhat');
const MODULE_TYPE_VALIDATOR = 1;
const MODULE_TYPE_EXECUTOR = 2;
const MODULE_TYPE_FALLBACK = 3;
const MODULE_TYPE_HOOK = 4;
const EXEC_TYPE_DEFAULT = '0x00';
const EXEC_TYPE_TRY = '0x01';
const CALL_TYPE_CALL = '0x00';
const CALL_TYPE_BATCH = '0x01';
const CALL_TYPE_DELEGATE = '0xff';
const encodeMode = ({
callType = '0x00',
execType = '0x00',
selector = '0x00000000',
payload = '0x00000000000000000000000000000000000000000000',
} = {}) =>
ethers.solidityPacked(
['bytes1', 'bytes1', 'bytes4', 'bytes4', 'bytes22'],
[callType, execType, '0x00000000', selector, payload],
);
const encodeSingle = (target, value = 0n, data = '0x') =>
ethers.solidityPacked(['address', 'uint256', 'bytes'], [target.target ?? target.address ?? target, value, data]);
const encodeBatch = (...entries) =>
ethers.AbiCoder.defaultAbiCoder().encode(
['(address,uint256,bytes)[]'],
[
entries.map(entry =>
Array.isArray(entry)
? [entry[0].target ?? entry[0].address ?? entry[0], entry[1] ?? 0n, entry[2] ?? '0x']
: [entry.target.target ?? entry.target.address ?? entry.target, entry.value ?? 0n, entry.data ?? '0x'],
),
],
);
const encodeDelegate = (target, data = '0x') =>
ethers.solidityPacked(['address', 'bytes'], [target.target ?? target.address ?? target, data]);
module.exports = {
MODULE_TYPE_VALIDATOR,
MODULE_TYPE_EXECUTOR,
MODULE_TYPE_FALLBACK,
MODULE_TYPE_HOOK,
EXEC_TYPE_DEFAULT,
EXEC_TYPE_TRY,
CALL_TYPE_CALL,
CALL_TYPE_BATCH,
CALL_TYPE_DELEGATE,
encodeMode,
encodeSingle,
encodeBatch,
encodeDelegate,
};