Skip to content

Commit

Permalink
Merge branch 'main' into lyova-fix-factory-proxy
Browse files Browse the repository at this point in the history
  • Loading branch information
ly0va authored Dec 12, 2024
2 parents 82178c4 + 4628172 commit f79b46f
Show file tree
Hide file tree
Showing 15 changed files with 118 additions and 148 deletions.
3 changes: 1 addition & 2 deletions src/AAFactory.sol
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,6 @@ contract AAFactory {
bytes32 _salt,
string calldata _uniqueAccountId,
bytes[] calldata _initialValidators,
bytes[] calldata _initialHooks,
address[] calldata _initialK1Owners
) external returns (address accountAddress) {
require(accountMappings[_uniqueAccountId] == address(0), "Account already exists");
Expand All @@ -60,7 +59,7 @@ contract AAFactory {
(accountAddress) = abi.decode(returnData, (address));

// Initialize the newly deployed account with validators, hooks and K1 owners.
ISsoAccount(accountAddress).initialize(_initialValidators, _initialHooks, _initialK1Owners);
ISsoAccount(accountAddress).initialize(_initialValidators, _initialK1Owners);

accountMappings[_uniqueAccountId] = accountAddress;

Expand Down
38 changes: 14 additions & 24 deletions src/SsoAccount.sol
Original file line number Diff line number Diff line change
Expand Up @@ -41,22 +41,12 @@ contract SsoAccount is Initializable, HookManager, ERC1271Handler, TokenCallback
/// @dev Sets passkey and passkey validator within account storage
/// @param _initialValidators An array of module validator addresses and initial validation keys
/// in an ABI encoded format of `abi.encode(validatorAddr,validationKey))`.
/// @param _initialValidationHooks An array of hook module validator addresses and initial validation keys
/// in an ABI encoded format of `abi.encode(validatorAddr,validationKey))`.
/// @param _initialK1Owners An array of addresses with full control over the account.
function initialize(
bytes[] calldata _initialValidators,
bytes[] calldata _initialValidationHooks,
address[] calldata _initialK1Owners
) external initializer {
function initialize(bytes[] calldata _initialValidators, address[] calldata _initialK1Owners) external initializer {
for (uint256 i = 0; i < _initialValidators.length; ++i) {
(address validatorAddr, bytes memory validationKey) = abi.decode(_initialValidators[i], (address, bytes));
_addModuleValidator(validatorAddr, validationKey);
}
for (uint256 i = 0; i < _initialValidationHooks.length; ++i) {
(address validatorAddr, bytes memory validationKey) = abi.decode(_initialValidationHooks[i], (address, bytes));
_installHook(validatorAddr, validationKey, true);
}
for (uint256 i = 0; i < _initialK1Owners.length; ++i) {
_k1AddOwner(_initialK1Owners[i]);
}
Expand Down Expand Up @@ -153,7 +143,6 @@ contract SsoAccount is Initializable, HookManager, ERC1271Handler, TokenCallback

emit FeePaid();
}

/// @notice This function is called by the system if the transaction has a paymaster
/// and prepares the interaction with the paymaster.
/// @param _transaction The transaction data.
Expand All @@ -176,23 +165,24 @@ contract SsoAccount is Initializable, HookManager, ERC1271Handler, TokenCallback
/// @param _transaction The transaction data.
/// @return The magic value if the validation was successful and bytes4(0) otherwise.
function _validateTransaction(bytes32 _signedHash, Transaction calldata _transaction) internal returns (bytes4) {
if (_transaction.signature.length == 65) {
(address signer, ) = ECDSA.tryRecover(_signedHash, _transaction.signature);
return _k1IsOwner(signer) ? ACCOUNT_VALIDATION_SUCCESS_MAGIC : bytes4(0);
}

// Extract the signature, validator address and hook data from the _transaction.signature
(bytes memory signature, address validator, bytes[] memory hookData) = SignatureDecoder.decodeSignature(
_transaction.signature
);

// Run validation hooks
bool hookSuccess = runValidationHooks(_signedHash, _transaction, hookData);
bool hookSuccess = runValidationHooks(_signedHash, _transaction);
if (!hookSuccess) {
return bytes4(0);
}

bool validationSuccess = _handleValidation(validator, _signedHash, signature);
if (_transaction.signature.length == 65) {
(address signer, ECDSA.RecoverError error) = ECDSA.tryRecover(_signedHash, _transaction.signature);
return
signer == address(0) || error != ECDSA.RecoverError.NoError || !_k1IsOwner(signer)
? bytes4(0)
: ACCOUNT_VALIDATION_SUCCESS_MAGIC;
}

// Extract the signature, validator address and hook data from the _transaction.signature
(bytes memory signature, address validator, ) = SignatureDecoder.decodeSignature(_transaction.signature);

bool validationSuccess = _handleValidation(validator, _signedHash, signature, _transaction);
return validationSuccess ? ACCOUNT_VALIDATION_SUCCESS_MAGIC : bytes4(0);
}

Expand Down
7 changes: 5 additions & 2 deletions src/handlers/ERC1271Handler.sol
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@
pragma solidity ^0.8.24;

import { IERC1271Upgradeable } from "@openzeppelin/contracts-upgradeable/interfaces/IERC1271Upgradeable.sol";
import { Transaction } from "@matterlabs/zksync-contracts/l2/system-contracts/libraries/TransactionHelper.sol";

import { SignatureDecoder } from "../libraries/SignatureDecoder.sol";
import { IModuleValidator } from "../interfaces/IModuleValidator.sol";
import { ValidationHandler } from "./ValidationHandler.sol";
import { EIP712 } from "@openzeppelin/contracts/utils/cryptography/EIP712.sol";

Expand Down Expand Up @@ -34,9 +36,10 @@ abstract contract ERC1271Handler is IERC1271Upgradeable, EIP712("Sso1271", "1.0.

bytes32 eip712Hash = _hashTypedDataV4(_ssoMessageHash(SsoMessage(signedHash)));

bool valid = _handleValidation(validator, eip712Hash, signature);
bool isValid = _isModuleValidator(validator) &&
IModuleValidator(validator).validateSignature(eip712Hash, signature);

magicValue = valid ? _ERC1271_MAGIC : bytes4(0);
magicValue = isValid ? _ERC1271_MAGIC : bytes4(0);
}

/**
Expand Down
9 changes: 6 additions & 3 deletions src/handlers/ValidationHandler.sol
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.24;

import { Transaction } from "@matterlabs/zksync-contracts/l2/system-contracts/libraries/TransactionHelper.sol";

import { SignatureDecoder } from "../libraries/SignatureDecoder.sol";
import { BytesLinkedList } from "../libraries/LinkedList.sol";
import { OwnerManager } from "../managers/OwnerManager.sol";
Expand All @@ -17,10 +19,11 @@ abstract contract ValidationHandler is OwnerManager, ValidatorManager {
function _handleValidation(
address validator,
bytes32 signedHash,
bytes memory signature
) internal view returns (bool) {
bytes memory signature,
Transaction calldata transaction
) internal returns (bool) {
if (_isModuleValidator(validator)) {
return IModuleValidator(validator).handleValidation(signedHash, signature);
return IModuleValidator(validator).validateTransaction(signedHash, signature, transaction);
}

return false;
Expand Down
2 changes: 1 addition & 1 deletion src/helpers/TimestampAsserterLocator.sol
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ library TimestampAsserterLocator {
return ITimestampAsserter(address(0x00000000000000000000000000000000808012));
}
if (block.chainid == 300) {
revert("Timestamp asserter is not deployed on ZKsync Sepolia testnet yet");
return ITimestampAsserter(address(0xa64EC71Ee812ac62923c85cf0796aA58573c4Cf3));
}
if (block.chainid == 324) {
revert("Timestamp asserter is not deployed on ZKsync mainnet yet");
Expand Down
5 changes: 4 additions & 1 deletion src/interfaces/IHook.sol
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,11 @@ import { Transaction } from "@matterlabs/zksync-contracts/l2/system-contracts/li
import { IInitable } from "../interfaces/IInitable.sol";
import { IERC165 } from "@openzeppelin/contracts/utils/introspection/IERC165.sol";

// Validation hooks are a non-standard way to always perform validation,
// They can't expect any specific transaction data or signature, but can be used to enforce
// additional restrictions on the account during the validation phase
interface IValidationHook is IInitable, IERC165 {
function validationHook(bytes32 signedHash, Transaction calldata transaction, bytes calldata hookData) external;
function validationHook(bytes32 signedHash, Transaction calldata transaction) external;
}

interface IExecutionHook is IInitable, IERC165 {
Expand Down
2 changes: 1 addition & 1 deletion src/interfaces/IHookManager.sol
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ interface IHookManager {

/**
* @notice Add a hook to the list of hooks and call it's init function
* @dev Can only be called by self or a module
* @dev Can only be called by self
* @param hookAndData bytes calldata - Address of the hook and data to initialize it with
* @param isValidation bool - True if the hook is a validation hook, false otherwise
*/
Expand Down
9 changes: 8 additions & 1 deletion src/interfaces/IModuleValidator.sol
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,20 @@ pragma solidity ^0.8.24;

import { IInitable } from "../interfaces/IInitable.sol";
import { IERC165 } from "@openzeppelin/contracts/utils/introspection/IERC165.sol";
import { Transaction } from "@matterlabs/zksync-contracts/l2/system-contracts/libraries/TransactionHelper.sol";

/**
* @title Modular validator interface for native AA
* @dev Add signature to module or validate existing signatures for acccount
*/
interface IModuleValidator is IInitable, IERC165 {
function handleValidation(bytes32 signedHash, bytes memory signature) external view returns (bool);
function validateTransaction(
bytes32 signedHash,
bytes memory signature,
Transaction calldata transaction
) external returns (bool);

function validateSignature(bytes32 signedHash, bytes memory signature) external view returns (bool);

function addValidationKey(bytes memory key) external returns (bool);
}
6 changes: 1 addition & 5 deletions src/interfaces/ISsoAccount.sol
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,5 @@ interface ISsoAccount is
{
event FeePaid();

function initialize(
bytes[] calldata initialValidators,
bytes[] calldata initialHooks,
address[] calldata k1Owners
) external;
function initialize(bytes[] calldata initialValidators, address[] calldata k1Owners) external;
}
4 changes: 2 additions & 2 deletions src/libraries/SignatureDecoder.sol
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ library SignatureDecoder {
// Decode transaction.signature into signature, validator and hook data
function decodeSignature(
bytes calldata txSignature
) internal pure returns (bytes memory signature, address validator, bytes[] memory hookData) {
(signature, validator, hookData) = abi.decode(txSignature, (bytes, address, bytes[]));
) internal pure returns (bytes memory signature, address validator, bytes memory validatorData) {
(signature, validator, validatorData) = abi.decode(txSignature, (bytes, address, bytes));
}

// Decode signature into signature and validator
Expand Down
17 changes: 2 additions & 15 deletions src/managers/HookManager.sol
Original file line number Diff line number Diff line change
Expand Up @@ -109,23 +109,13 @@ abstract contract HookManager is IHookManager, Auth {
}

// Runs the validation hooks that are enabled by the account and returns true if none reverts
function runValidationHooks(
bytes32 signedHash,
Transaction calldata transaction,
bytes[] memory hookData
) internal returns (bool) {
function runValidationHooks(bytes32 signedHash, Transaction calldata transaction) internal returns (bool) {
mapping(address => address) storage validationHooks = _validationHooksLinkedList();

address cursor = validationHooks[AddressLinkedList.SENTINEL_ADDRESS];
uint256 idx;
// Iterate through hooks
while (cursor > AddressLinkedList.SENTINEL_ADDRESS) {
// Call it with corresponding hookData
bool success = _call(
cursor,
abi.encodeCall(IValidationHook.validationHook, (signedHash, transaction, hookData[idx]))
);
++idx;
bool success = _call(cursor, abi.encodeCall(IValidationHook.validationHook, (signedHash, transaction)));

if (!success) {
return false;
Expand All @@ -134,9 +124,6 @@ abstract contract HookManager is IHookManager, Auth {
cursor = validationHooks[cursor];
}

// Ensure that hookData is not tampered with
if (hookData.length != idx) return false;

return true;
}

Expand Down
Loading

0 comments on commit f79b46f

Please sign in to comment.