Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 23 additions & 22 deletions gas/ecdsa/report.txt
Original file line number Diff line number Diff line change
@@ -1,39 +1,40 @@
No files changed, compilation skipped

Running 8 tests for test/foundry/Kernel.t.sol:KernelTest
[PASS] test_disable_mode() (gas: 162572)
[PASS] test_external_call_default() (gas: 28889)
[PASS] test_external_call_execution() (gas: 453074)
[PASS] test_initialize_twice() (gas: 20907)
[PASS] test_set_default_validator() (gas: 361045)
[PASS] test_set_execution() (gas: 411379)
[PASS] test_validate_signature() (gas: 163724)
[PASS] test_validate_userOp() (gas: 1704261)
Test result: ok. 8 passed; 0 failed; 0 skipped; finished in 2.89ms
Running 9 tests for test/foundry/Kernel.t.sol:KernelTest
[PASS] test_disable_mode() (gas: 162594)
[PASS] test_external_call_default() (gas: 28911)
[PASS] test_external_call_execution() (gas: 453201)
[PASS] test_initialize_twice() (gas: 20929)
[PASS] test_set_default_validator() (gas: 361067)
[PASS] test_set_execution() (gas: 411392)
[PASS] test_shoul_return_address_if_deployed() (gas: 8937393460516732963)
[PASS] test_validate_signature() (gas: 163650)
[PASS] test_validate_userOp() (gas: 1687848)
Test result: ok. 9 passed; 0 failed; 0 skipped; finished in 2.60ms
| src/Kernel.sol:Kernel contract | | | | | |
|--------------------------------|-----------------|-------|--------|-------|---------|
| Deployment Cost | Deployment Size | | | | |
| 1561763 | 8209 | | | | |
| 1545342 | 8127 | | | | |
| Function Name | min | avg | median | max | # calls |
| disableMode | 3765 | 3765 | 3765 | 3765 | 1 |
| getDefaultValidator | 341 | 341 | 341 | 341 | 1 |
| getDisabledMode | 577 | 577 | 577 | 577 | 1 |
| getExecution | 1249 | 1249 | 1249 | 1249 | 2 |
| initialize | 3046 | 43982 | 48253 | 50753 | 10 |
| isValidSignature | 6047 | 6047 | 6047 | 6047 | 1 |
| initialize | 3046 | 44370 | 48253 | 50753 | 11 |
| isValidSignature | 5764 | 5764 | 5764 | 5764 | 1 |
| setDefaultValidator | 7870 | 7870 | 7870 | 7870 | 1 |
| setExecution | 49874 | 49874 | 49874 | 49874 | 2 |
| setExecution | 49865 | 49865 | 49865 | 49865 | 2 |
| validateUserOp | 45773 | 45967 | 45989 | 46119 | 4 |


| src/factory/KernelFactory.sol:KernelFactory contract | | | | | |
|------------------------------------------------------|-----------------|--------|--------|--------|---------|
| Deployment Cost | Deployment Size | | | | |
| 564969 | 2862 | | | | |
| Function Name | min | avg | median | max | # calls |
| createAccount | 130989 | 131766 | 130989 | 137989 | 9 |
| setImplementation | 22862 | 22862 | 22862 | 22862 | 8 |
| src/factory/KernelFactory.sol:KernelFactory contract | | | | | |
|------------------------------------------------------|-----------------|--------------------|--------|---------------------|---------|
| Deployment Cost | Deployment Size | | | | |
| 599207 | 3033 | | | | |
| Function Name | min | avg | median | max | # calls |
| createAccount | 131176 | 812490314592548788 | 131176 | 8937393460516717918 | 11 |
| setImplementation | 22852 | 22852 | 22852 | 22852 | 9 |



Ran 1 test suites: 8 tests passed, 0 failed, 0 skipped (8 total tests)
Ran 1 test suites: 9 tests passed, 0 failed, 0 skipped (9 total tests)
38 changes: 21 additions & 17 deletions src/Kernel.sol
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ pragma solidity ^0.8.0;
// Importing external libraries and contracts
import "solady/utils/EIP712.sol";
import "solady/utils/ECDSA.sol";
import "account-abstraction/core/Helpers.sol";
import "account-abstraction/interfaces/IEntryPoint.sol";
import "./abstract/Compatibility.sol";
import "./abstract/KernelStorage.sol";
Expand All @@ -16,6 +15,7 @@ import "src/common/Enum.sol";
/// @title Kernel
/// @author taek<leekt216@gmail.com>
/// @notice wallet kernel for extensible wallet functionality

contract Kernel is EIP712, Compatibility, KernelStorage {
string public constant name = KERNEL_NAME;

Expand Down Expand Up @@ -59,17 +59,17 @@ contract Kernel is EIP712, Compatibility, KernelStorage {
if (msg.sender != address(entryPoint) && !_checkCaller()) {
revert NotAuthorizedCaller();
}
if (operation == Operation.DelegateCall) {
if (operation == Operation.Call) {
assembly {
let success := delegatecall(gas(), to, add(data, 0x20), mload(data), 0, 0)
let success := call(gas(), to, value, add(data, 0x20), mload(data), 0, 0)
returndatacopy(0, 0, returndatasize())
switch success
case 0 { revert(0, returndatasize()) }
default { return(0, returndatasize()) }
}
} else {
assembly {
let success := call(gas(), to, value, add(data, 0x20), mload(data), 0, 0)
let success := delegatecall(gas(), to, add(data, 0x20), mload(data), 0, 0)
returndatacopy(0, 0, returndatasize())
switch success
case 0 { revert(0, returndatasize()) }
Expand All @@ -87,7 +87,7 @@ contract Kernel is EIP712, Compatibility, KernelStorage {
function validateUserOp(UserOperation memory userOp, bytes32 userOpHash, uint256 missingAccountFunds)
external
payable
returns (uint256 validationData)
returns (ValidationData validationData)
{
if (msg.sender != address(entryPoint)) {
revert NotEntryPoint();
Expand Down Expand Up @@ -129,7 +129,7 @@ contract Kernel is EIP712, Compatibility, KernelStorage {
}
}
userOpSignature = userOpSignature[4:];
validationData = (uint256(detail.validAfter) << 208) | (uint256(detail.validUntil) << 160);
validationData = packValidationData(detail.validAfter, detail.validUntil);
} else if (mode == 0x00000002) {
bytes calldata userOpCallData;
assembly {
Expand Down Expand Up @@ -159,7 +159,7 @@ contract Kernel is EIP712, Compatibility, KernelStorage {

function _approveValidator(bytes4 sig, bytes calldata signature)
internal
returns (IKernelValidator validator, uint256 validationData, bytes calldata validationSig)
returns (IKernelValidator validator, ValidationData validationData, bytes calldata validationSig)
{
unchecked {
validator = IKernelValidator(address(bytes20(signature[16:36])));
Expand Down Expand Up @@ -188,16 +188,19 @@ contract Kernel is EIP712, Compatibility, KernelStorage {
);
validationData = _intersectValidationData(
getKernelStorage().defaultValidator.validateSignature(enableDigest, signature[cursor:cursor + length]),
uint256(bytes32(signature[4:36])) & 0xffffffffffffffffffffffff0000000000000000000000000000000000000000
ValidationData.wrap(
uint256(bytes32(signature[4:36]))
& 0xffffffffffffffffffffffff0000000000000000000000000000000000000000
)
);
assembly {
cursor := add(cursor, length)
validationSig.offset := add(signature.offset, cursor)
validationSig.length := sub(signature.length, cursor)
}
getKernelStorage().execution[sig] = ExecutionDetail({
validAfter: uint48(bytes6(signature[4:10])),
validUntil: uint48(bytes6(signature[10:16])),
validAfter: ValidAfter.wrap(uint48(bytes6(signature[4:10]))),
validUntil: ValidUntil.wrap(uint48(bytes6(signature[10:16]))),
executor: address(bytes20(signature[36:56])),
validator: IKernelValidator(address(bytes20(signature[16:36])))
});
Expand All @@ -211,15 +214,15 @@ contract Kernel is EIP712, Compatibility, KernelStorage {
/// @param signature The signature to be validated
/// @return The magic value 0x1626ba7e if the signature is valid, otherwise returns 0xffffffff.
function isValidSignature(bytes32 hash, bytes calldata signature) external view returns (bytes4) {
uint256 validationData = getKernelStorage().defaultValidator.validateSignature(hash, signature);
ValidationData memory data = _parseValidationData(validationData);
if (data.validAfter > block.timestamp) {
ValidationData validationData = getKernelStorage().defaultValidator.validateSignature(hash, signature);
(ValidAfter validAfter, ValidUntil validUntil, address result) = parseValidationData(validationData);
if (ValidAfter.unwrap(validAfter) > block.timestamp) {
return 0xffffffff;
}
if (data.validUntil < block.timestamp) {
if (ValidUntil.unwrap(validUntil) < block.timestamp) {
return 0xffffffff;
}
if (data.aggregator != address(0)) {
if (result != address(0)) {
return 0xffffffff;
}

Expand All @@ -233,8 +236,9 @@ contract Kernel is EIP712, Compatibility, KernelStorage {
bytes4 sig = msg.sig;
ExecutionDetail storage detail = getKernelStorage().execution[sig];
if (
address(detail.validator) == address(0) || (detail.validUntil != 0 && detail.validUntil < block.timestamp)
|| detail.validAfter > block.timestamp
address(detail.validator) == address(0)
|| (ValidUntil.unwrap(detail.validUntil) != 0 && ValidUntil.unwrap(detail.validUntil) < block.timestamp)
|| ValidAfter.unwrap(detail.validAfter) > block.timestamp
) {
return false;
} else {
Expand Down
4 changes: 2 additions & 2 deletions src/abstract/KernelStorage.sol
Original file line number Diff line number Diff line change
Expand Up @@ -110,8 +110,8 @@ contract KernelStorage {
getKernelStorage().execution[_selector] = ExecutionDetail({
executor: _executor,
validator: _validator,
validUntil: _validUntil,
validAfter: _validAfter
validUntil: ValidUntil.wrap(_validUntil),
validAfter: ValidAfter.wrap(_validAfter)
});
_validator.enable(_enableData);
emit ExecutionChanged(_selector, _executor, address(_validator));
Expand Down
6 changes: 3 additions & 3 deletions src/common/Constants.sol
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
pragma solidity ^0.8.0;

// constants for kernel metadata
string constant KERNEL_NAME="Kernel";
string constant KERNEL_VERSION="0.2.1";
string constant KERNEL_NAME = "Kernel";
string constant KERNEL_VERSION = "0.2.1";

// ERC4337 constants
uint256 constant SIG_VALIDATION_FAILED = 1;
uint256 constant SIG_VALIDATION_FAILED_UINT = 1;

// STRUCT_HASH
bytes32 constant VALIDATOR_APPROVED_STRUCT_HASH = 0x3ce406685c1b3551d706d85a68afdaa49ac4e07b451ad9b8ff8b58c3ee964176;
Expand Down
9 changes: 5 additions & 4 deletions src/common/Structs.sol
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,12 @@ pragma solidity ^0.8.0;

import "src/interfaces/IValidator.sol";
import "src/common/Enum.sol";
import "src/common/Types.sol";

// Defining a struct for execution details
struct ExecutionDetail {
uint48 validAfter; // Until what time is this execution valid
uint48 validUntil; // After what time is this execution valid
ValidAfter validAfter; // Until what time is this execution valid
ValidUntil validUntil; // After what time is this execution valid
address executor; // Who is the executor of this execution
IKernelValidator validator; // The validator for this execution
}
Expand Down Expand Up @@ -37,8 +38,8 @@ struct Permission {

struct SessionData {
bytes32 merkleRoot;
uint48 validAfter;
uint48 validUntil;
ValidAfter validAfter;
ValidUntil validUntil;
address paymaster; // address(0) means accept userOp without paymaster, address(1) means reject userOp with paymaster, other address means accept userOp with paymaster with the address
bool enabled;
}
30 changes: 30 additions & 0 deletions src/common/Types.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
pragma solidity ^0.8.9;

import "src/common/Constants.sol";

type ValidAfter is uint48;

type ValidUntil is uint48;

type ValidationData is uint256;

ValidationData constant SIG_VALIDATION_FAILED = ValidationData.wrap(SIG_VALIDATION_FAILED_UINT);

function packValidationData(ValidAfter validAfter, ValidUntil validUntil) pure returns (ValidationData) {
return ValidationData.wrap(
uint256(ValidAfter.unwrap(validAfter)) << 208 | uint256(ValidUntil.unwrap(validUntil)) << 160
);
}

function parseValidationData(ValidationData validationData)
pure
returns (ValidAfter validAfter, ValidUntil validUntil, address result)
{
assembly {
result := validationData
validUntil := and(shr(160, validationData), 0xffffffffffff)
switch iszero(validUntil)
case 1 { validUntil := 0xffffffffffff }
validAfter := shr(208, validationData)
}
}
18 changes: 13 additions & 5 deletions src/factory/AdminLessERC1967Factory.sol
Original file line number Diff line number Diff line change
Expand Up @@ -98,12 +98,20 @@ contract AdminLessERC1967Factory {
switch useSalt
case 0 { proxy := create(0, add(m, 0x13), 0x89) }
default { proxy := create2(0, add(m, 0x13), 0x89, salt) }
// Revert if the creation fails.
if iszero(proxy) {
mstore(0x00, _DEPLOYMENT_FAILED_ERROR_SELECTOR)
revert(0x1c, 0x04)
}
}

if (proxy == address(0)) {
proxy = predictDeterministicAddress(salt);
assembly {
if iszero(extcodesize(proxy)) {
// Revert if the creation fails.
mstore(0x00, _DEPLOYMENT_FAILED_ERROR_SELECTOR)
revert(0x1c, 0x04)
}
}
return proxy;
}
assembly {
// Set up the calldata to set the implementation of the proxy.
mstore(m, implementation)
mstore(add(m, 0x20), _IMPLEMENTATION_SLOT)
Expand Down
7 changes: 4 additions & 3 deletions src/interfaces/IValidator.sol
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import "account-abstraction/interfaces/UserOperation.sol";
import {UserOperation} from "account-abstraction/interfaces/UserOperation.sol";
import "src/common/Types.sol";

interface IKernelValidator {
function enable(bytes calldata _data) external payable;
Expand All @@ -11,9 +12,9 @@ interface IKernelValidator {
function validateUserOp(UserOperation calldata userOp, bytes32 userOpHash, uint256 missingFunds)
external
payable
returns (uint256);
returns (ValidationData);

function validateSignature(bytes32 hash, bytes calldata signature) external view returns (uint256);
function validateSignature(bytes32 hash, bytes calldata signature) external view returns (ValidationData);

function validCaller(address caller, bytes calldata data) external view returns (bool);
}
Expand Down
9 changes: 5 additions & 4 deletions src/test/TestValidator.sol
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
pragma solidity ^0.8.0;

import "src/interfaces/IValidator.sol";
import "src/common/Types.sol";

contract TestValidator is IKernelValidator {
event TestValidateUserOp(bytes32 indexed opHash);
Expand All @@ -14,18 +15,18 @@ contract TestValidator is IKernelValidator {
caller[_kernel] = _caller;
}

function validateSignature(bytes32, bytes calldata) external pure override returns (uint256) {
return 0;
function validateSignature(bytes32, bytes calldata) external pure override returns (ValidationData) {
return ValidationData.wrap(0);
}

function validateUserOp(UserOperation calldata, bytes32 userOpHash, uint256)
external
payable
override
returns (uint256)
returns (ValidationData)
{
emit TestValidateUserOp(userOpHash);
return 0;
return ValidationData.wrap(0);
}

function enable(bytes calldata data) external payable override {
Expand Down
7 changes: 4 additions & 3 deletions src/utils/KernelHelper.sol
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import {SIG_VALIDATION_FAILED} from "src/common/Constants.sol";
import {SIG_VALIDATION_FAILED_UINT} from "src/common/Constants.sol";
import {ValidationData} from "src/common/Types.sol";

function _intersectValidationData(uint256 a, uint256 b) pure returns (uint256 validationData) {
function _intersectValidationData(ValidationData a, ValidationData b) pure returns (ValidationData validationData) {
assembly {
// xor(a,b) == shows only matching bits
// and(xor(a,b), 0x000000000000000000000000ffffffffffffffffffffffffffffffffffffffff) == filters out the validAfter and validUntil bits
Expand All @@ -21,6 +22,6 @@ function _intersectValidationData(uint256 a, uint256 b) pure returns (uint256 va
if iszero(until) { until := 0x000000000000ffffffffffff0000000000000000000000000000000000000000 }
validationData := or(validationData, until)
}
default { validationData := SIG_VALIDATION_FAILED }
default { validationData := SIG_VALIDATION_FAILED_UINT }
}
}
Loading