Skip to content

Commit

Permalink
Merge branch 'dev' into feat/remap-fix
Browse files Browse the repository at this point in the history
  • Loading branch information
Aboudjem authored Sep 30, 2024
2 parents 9e8e1e6 + be4ae46 commit 4c1f5bc
Show file tree
Hide file tree
Showing 77 changed files with 3,231 additions and 698 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

This repository serves as a comprehensive foundation for smart contract projects, streamlining the development process with a focus on best practices, security, and efficiency.

Documentation: https://github.com/bcnmy/nexus/wiki
Documentation: (https://github.com/bcnmy/nexus/wiki)

## 📚 Table of Contents

Expand Down
45 changes: 16 additions & 29 deletions contracts/Nexus.sol
Original file line number Diff line number Diff line change
Expand Up @@ -40,20 +40,15 @@ contract Nexus is INexus, BaseAccount, ExecutionHelper, ModuleManager, UUPSUpgra
using ExecLib for bytes;
using NonceLib for uint256;

/// @dev Precomputed `typeHash` used to produce EIP-712 compliant hash when applying the anti
/// cross-account-replay layer.
///
/// The original hash must either be:
/// - An EIP-191 hash: keccak256("\x19Ethereum Signed Message:\n" || len(someMessage) || someMessage)
/// - An EIP-712 hash: keccak256("\x19\x01" || someDomainSeparator || hashStruct(someStruct))
bytes32 private constant _MESSAGE_TYPEHASH = keccak256("BiconomyNexusMessage(bytes32 hash)");

/// @dev The timelock period for emergency hook uninstallation.
uint256 internal constant _EMERGENCY_TIMELOCK = 1 days;

/// @dev The event emitted when an emergency hook uninstallation is initiated.
event EmergencyHookUninstallRequest(address hook, uint256 timestamp);

/// @dev The event emitted when an emergency hook uninstallation request is reset.
event EmergencyHookUninstallRequestReset(address hook, uint256 timestamp);

/// @notice Initializes the smart account with the specified entry point.
constructor(address anEntryPoint) {
require(address(anEntryPoint) != address(0), EntryPointCanNotBeZero());
Expand Down Expand Up @@ -157,7 +152,8 @@ contract Nexus is INexus, BaseAccount, ExecutionHelper, ModuleManager, UUPSUpgra
/// @param module The address of the module to install.
/// @param initData Initialization data for the module.
/// @dev This function can only be called by the EntryPoint or the account itself for security reasons.
function installModule(uint256 moduleTypeId, address module, bytes calldata initData) external payable onlyEntryPointOrSelf withHook {
/// @dev This function goes through hook checks via withHook modifier through internal function _installModule.
function installModule(uint256 moduleTypeId, address module, bytes calldata initData) external payable onlyEntryPointOrSelf {
_installModule(moduleTypeId, module, initData);
emit ModuleInstalled(moduleTypeId, module);
}
Expand Down Expand Up @@ -187,6 +183,7 @@ contract Nexus is INexus, BaseAccount, ExecutionHelper, ModuleManager, UUPSUpgra
}

function emergencyUninstallHook(address hook, bytes calldata deInitData) external payable onlyEntryPoint {
require(_isModuleInstalled(MODULE_TYPE_HOOK, hook, deInitData), ModuleNotInstalled(MODULE_TYPE_HOOK, hook));
AccountStorage storage accountStorage = _getAccountStorage();
uint256 hookTimelock = accountStorage.emergencyUninstallTimelock[hook];

Expand All @@ -197,7 +194,7 @@ contract Nexus is INexus, BaseAccount, ExecutionHelper, ModuleManager, UUPSUpgra
} else if (block.timestamp >= hookTimelock + 3 * _EMERGENCY_TIMELOCK) {
// if the timelock has been left for too long, reset it
accountStorage.emergencyUninstallTimelock[hook] = block.timestamp;
emit EmergencyHookUninstallRequest(hook, block.timestamp);
emit EmergencyHookUninstallRequestReset(hook, block.timestamp);
} else if (block.timestamp >= hookTimelock + _EMERGENCY_TIMELOCK) {
// if the timelock expired, clear it and uninstall the hook
accountStorage.emergencyUninstallTimelock[hook] = 0;
Expand Down Expand Up @@ -232,7 +229,11 @@ contract Nexus is INexus, BaseAccount, ExecutionHelper, ModuleManager, UUPSUpgra
// First 20 bytes of data will be validator address and rest of the bytes is complete signature.
address validator = address(bytes20(signature[0:20]));
require(_isValidatorInstalled(validator), ValidatorNotInstalled(validator));
return IValidator(validator).isValidSignatureWithSender(msg.sender, hash, signature[20:]);
try IValidator(validator).isValidSignatureWithSender(msg.sender, hash, signature[20:]) returns (bytes4 res) {
return res;
} catch {
return bytes4(0xffffffff);
}
}

/// @notice Retrieves the address of the current implementation from the EIP-1967 slot.
Expand Down Expand Up @@ -305,7 +306,7 @@ contract Nexus is INexus, BaseAccount, ExecutionHelper, ModuleManager, UUPSUpgra
/// as Biconomy v2 Account (proxy) reads implementation from the slot that is defined by its address
/// @param newImplementation The address of the new contract implementation.
/// @param data The calldata to be sent to the new implementation.
function upgradeToAndCall(address newImplementation, bytes calldata data) public payable virtual override onlyEntryPointOrSelf withHook {
function upgradeToAndCall(address newImplementation, bytes calldata data) public payable virtual override withHook {
require(newImplementation != address(0), InvalidImplementationAddress());
bool res;
assembly {
Expand All @@ -319,30 +320,16 @@ contract Nexus is INexus, BaseAccount, ExecutionHelper, ModuleManager, UUPSUpgra
UUPSUpgradeable.upgradeToAndCall(newImplementation, data);
}

/// @dev For automatic detection that the smart account supports the nested EIP-712 workflow
/// with a specific validator.
///
/// Temporary implementation, not following ERC-7739 interface.
/// See https://ethereum-magicians.org/t/erc-7739-readable-typed-signatures-for-smart-accounts/20513/2
/// for discussions.
///
/// By default, it returns `bytes32(bytes4(keccak256("supportsNestedTypedDataSign()")))`,
/// denoting support for the default behavior, as implemented in
/// `_erc1271IsValidSignatureViaNestedEIP712`, which is called in `isValidSignature`.
/// Future extensions should return a different non-zero `result` to denote different behavior.
/// This method intentionally returns bytes32 to allow freedom for future extensions.
function supportsNestedTypedDataSignWithValidator(address validator) public view virtual returns (bytes32) {
return (IERC7739(validator).supportsNestedTypedDataSign());
}

/// @dev For automatic detection that the smart account supports the nested EIP-712 workflow
/// Offchain usage only
/// Iterates over all the validators
function supportsNestedTypedDataSign() public view virtual returns (bytes32) {
SentinelListLib.SentinelList storage validators = _getAccountStorage().validators;
address next = validators.entries[SENTINEL];
while (next != ZERO_ADDRESS && next != SENTINEL) {
if (IERC7739(next).supportsNestedTypedDataSign() == SUPPORTS_NESTED_TYPED_DATA_SIGN) return SUPPORTS_NESTED_TYPED_DATA_SIGN;
try IERC7739(next).supportsNestedTypedDataSign() returns (bytes32 res) {
if (res == SUPPORTS_NESTED_TYPED_DATA_SIGN) return SUPPORTS_NESTED_TYPED_DATA_SIGN;
} catch {}
next = validators.entries[next];
}
return bytes4(0xffffffff);
Expand Down
8 changes: 5 additions & 3 deletions contracts/base/BaseAccount.sol
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,6 @@ pragma solidity ^0.8.27;
// Learn more at https://biconomy.io. To report security issues, please contact us at: [email protected]

import { IEntryPoint } from "account-abstraction/interfaces/IEntryPoint.sol";

import { Storage } from "./Storage.sol";
import { IBaseAccount } from "../interfaces/base/IBaseAccount.sol";

/// @title Nexus - BaseAccount
Expand All @@ -25,10 +23,14 @@ import { IBaseAccount } from "../interfaces/base/IBaseAccount.sol";
/// @author @filmakarov | Biconomy | [email protected]
/// @author @zeroknots | Rhinestone.wtf | zeroknots.eth
/// Special thanks to the Solady team for foundational contributions: https://github.com/Vectorized/solady
contract BaseAccount is Storage, IBaseAccount {
contract BaseAccount is IBaseAccount {
/// @notice Identifier for this implementation on the network
string internal constant _ACCOUNT_IMPLEMENTATION_ID = "biconomy.nexus.1.0.0-beta";

/// @notice The canonical address for the ERC4337 EntryPoint contract, version 0.7.
/// This address is consistent across all supported networks.
address internal immutable _ENTRYPOINT;

/// @dev Ensures the caller is either the EntryPoint or this account itself.
/// Reverts with AccountAccessUnauthorized if the check fails.
modifier onlyEntryPointOrSelf() {
Expand Down
Loading

0 comments on commit 4c1f5bc

Please sign in to comment.