Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added Natspec comments #123

Merged
merged 2 commits into from
Oct 28, 2023
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
24 changes: 23 additions & 1 deletion contracts/SoulWallet.sol
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,10 @@ import "./base/FallbackManager.sol";
import "./base/UpgradeManager.sol";
import "./base/ValidatorManager.sol";

// Draft
/// @title SoulWallet
/// @author SoulWallet team
/// @notice logic contract of SoulWallet
/// @dev Draft contract - may be subject to changes
contract SoulWallet is
Initializable,
ISoulWallet,
Expand All @@ -31,13 +34,21 @@ contract SoulWallet is
ERC1271Handler,
ValidatorManager
{
/// @notice Creates a new SoulWallet instance
/// @param _EntryPoint Address of the entry point
/// @param _validator Address of the validator
constructor(IEntryPoint _EntryPoint, IValidator _validator)
EntryPointManager(_EntryPoint)
ValidatorManager(_validator)
{
_disableInitializers();
}

/// @notice Initializes the SoulWallet with given parameters
/// @param owners List of owner addresses (passkey public key hash or eoa address)
/// @param defalutCallbackHandler Default callback handler address
/// @param modules List of module data
/// @param plugins List of plugin data
function initialize(
bytes32[] calldata owners,
address defalutCallbackHandler,
Expand All @@ -60,10 +71,16 @@ contract SoulWallet is
}
}

/// @notice Gets the address of the entry point
/// @return IEntryPoint Address of the entry point
function entryPoint() public view override(BaseAccount) returns (IEntryPoint) {
return EntryPointManager._entryPoint();
}

/// @notice Validates the user's signature
/// @param userOp User operation details
/// @param userOpHash Hash of the user operation
/// @return validationData Data related to validation process
function _validateSignature(UserOperation calldata userOp, bytes32 userOpHash)
internal
virtual
Expand Down Expand Up @@ -91,10 +108,15 @@ contract SoulWallet is
validationData = validationData | ((sigValid && guardHookResult) ? 0 : SIG_VALIDATION_FAILED);
}

/// @notice Upgrades the contract to a new implementation
/// @param newImplementation Address of the new implementation
/// @dev Can only be called from an external module for security reasons
function upgradeTo(address newImplementation) external onlyModule {
UpgradeManager._upgradeTo(newImplementation);
}

/// @notice Handles the upgrade from an old implementation
/// @param oldImplementation Address of the old implementation
function upgradeFrom(address oldImplementation) external pure override {
(oldImplementation);
revert Errors.NOT_IMPLEMENTED(); //Initial version no need data migration
Expand Down
67 changes: 48 additions & 19 deletions contracts/SoulWalletFactory.sol
Original file line number Diff line number Diff line change
Expand Up @@ -12,36 +12,48 @@ import "@account-abstraction/contracts/interfaces/IEntryPoint.sol";
import "@openzeppelin/contracts/access/Ownable.sol";

/**
* @author soulwallet team.
* @title A factory contract to create soul wallet.
* @dev it is called by the entrypoint which call the "initCode" factory to create and return the sender wallet address.
* @notice .
* @title SoulWalletFactory
* @author soulwallet team
* @notice A factory contract to create soul wallets
* @dev This contract is called by the entrypoint which uses the "initCode" to create and return the sender's wallet address
*/

contract SoulWalletFactory is Ownable {
uint256 private immutable _WALLETIMPL;
IEntryPoint public immutable entryPoint;
string public constant VERSION = "0.0.1";

event SoulWalletCreation(address proxy);
event SoulWalletCreation(address indexed proxy);

/**
* @dev Initializes the factory with the wallet implementation and entry point addresses
* @param _walletImpl Address of the SoulWallet implementation
* @param _entryPoint Address of the EntryPoint contract
* @param _owner Address of the contract owner
*/
constructor(address _walletImpl, address _entryPoint, address _owner) Ownable(_owner) {
require(_walletImpl != address(0));
require(_walletImpl != address(0), "Invalid wallet implementation address");
_WALLETIMPL = uint256(uint160(_walletImpl));
require(_entryPoint != address(0));
require(_entryPoint != address(0), "Invalid entry point address");
entryPoint = IEntryPoint(_entryPoint);
}

/**
* @notice Returns the wallet implementation address
* @return Address of the wallet implementation
*/
function walletImpl() external view returns (address) {
return address(uint160(_WALLETIMPL));
}

function _calcSalt(bytes memory _initializer, bytes32 _salt) private pure returns (bytes32 salt) {
salt = keccak256(abi.encodePacked(keccak256(_initializer), _salt));
return keccak256(abi.encodePacked(keccak256(_initializer), _salt));
}

/**
* @notice deploy the soul wallet contract using proxy and returns the address of the proxy. should be called by entrypoint with useropeartoin.initcode > 0
* @dev Deploys the SoulWallet using a proxy and returns the proxy's address
* @param _initializer Initialization data
* @param _salt Salt for the create2 deployment
* @return proxy Address of the deployed proxy
*/
function createWallet(bytes memory _initializer, bytes32 _salt) external returns (address proxy) {
bytes memory deploymentData = abi.encodePacked(type(SoulWalletProxy).creationCode, _WALLETIMPL);
Expand All @@ -60,48 +72,65 @@ contract SoulWalletFactory is Ownable {
}

/**
* @notice returns the proxy creationCode external method.
* @dev used by soulwalletlib to calcudate the soul wallet address.
* @return bytes .
* @notice Returns the proxy's creation code
* @dev Used by soulwalletlib to calculate the SoulWallet address
* @return Byte array representing the proxy's creation code
*/
function proxyCode() external pure returns (bytes memory) {
return _proxyCode();
}

/**
* @notice returns the proxy creationCode private method.
* @dev .
* @return bytes .
*/
function _proxyCode() private pure returns (bytes memory) {
return type(SoulWalletProxy).creationCode;
}

/**
* @notice return the counterfactual address of soul wallet as it would be return by createWallet()
* @notice Calculates the counterfactual address of the SoulWallet as it would be returned by `createWallet`
* @param _initializer Initialization data
* @param _salt Salt for the create2 deployment
* @return proxy Counterfactual address of the SoulWallet
*/
function getWalletAddress(bytes memory _initializer, bytes32 _salt) external view returns (address proxy) {
bytes memory deploymentData = abi.encodePacked(type(SoulWalletProxy).creationCode, _WALLETIMPL);
bytes32 salt = _calcSalt(_initializer, _salt);
proxy = Create2.computeAddress(salt, keccak256(deploymentData));
}

/**
* @notice Deposits ETH to the entry point on behalf of the contract
*/
function deposit() public payable {
entryPoint.depositTo{value: msg.value}(address(this));
}

/**
* @notice Allows the owner to withdraw ETH from entrypoint contract
* @param withdrawAddress Address to receive the withdrawn ETH
* @param amount Amount of ETH to withdraw
*/
function withdrawTo(address payable withdrawAddress, uint256 amount) public onlyOwner {
entryPoint.withdrawTo(withdrawAddress, amount);
}

/**
* @notice Allows the owner to add stake to the entry point
* @param unstakeDelaySec Duration (in seconds) after which the stake can be unlocked
*/
function addStake(uint32 unstakeDelaySec) external payable onlyOwner {
entryPoint.addStake{value: msg.value}(unstakeDelaySec);
}

/**
* @notice Allows the owner to unlock their stake from the entry point
*/
function unlockStake() external onlyOwner {
entryPoint.unlockStake();
}

/**
* @notice Allows the owner to withdraw their stake from the entry point
* @param withdrawAddress Address to receive the withdrawn stake
*/
function withdrawStake(address payable withdrawAddress) external onlyOwner {
entryPoint.withdrawStake(withdrawAddress);
}
Expand Down
17 changes: 11 additions & 6 deletions contracts/SoulWalletProxy.sol
Original file line number Diff line number Diff line change
@@ -1,17 +1,21 @@
// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.20;

/**
* @title SoulWalletProxy
* @notice A proxy contract that forwards calls to an implementation contract
* @dev This proxy uses the EIP-1967 standard for storage slots
*/
contract SoulWalletProxy {
/**
* @dev Storage slot with the address of the current implementation.
* This is the keccak-256 hash of "eip1967.proxy.implementation" subtracted by 1
* @notice Storage slot with the address of the current implementation
* @dev This is the keccak-256 hash of "eip1967.proxy.implementation" subtracted by 1
*/
bytes32 private constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;

/**
* @dev Initializes the contract setting the implementation
*
* @param logic Address of the initial implementation.
* @notice Initializes the proxy with the address of the initial implementation contract
* @param logic Address of the initial implementation
*/
constructor(address logic) {
assembly ("memory-safe") {
Expand All @@ -20,7 +24,8 @@ contract SoulWalletProxy {
}

/**
* @dev Fallback function
* @notice Fallback function which forwards all calls to the implementation contract
* @dev Uses delegatecall to ensure the context remains within the proxy
*/
fallback() external payable {
assembly {
Expand Down
9 changes: 9 additions & 0 deletions contracts/authority/Authority.sol
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,16 @@ import "../interfaces/IModuleManager.sol";
import "../libraries/Errors.sol";
import "./ModuleAuth.sol";

/**
* @title Authority
* @notice An abstract contract that provides authorization mechanisms
* @dev Inherits various authorization patterns including EntryPoint, Owner, and Module-based authentication
*/
abstract contract Authority is EntryPointAuth, OwnerAuth, ModuleAuth {
/**
* @notice Ensures the calling contract is either the Authority contract itself or an authorized module
* @dev Uses the inherited `_isAuthorizedModule()` from ModuleAuth for module-based authentication
*/
modifier onlySelfOrModule() {
if (msg.sender != address(this) && !_isAuthorizedModule()) {
revert Errors.CALLER_MUST_BE_SELF_OR_MODULE();
Expand Down
13 changes: 12 additions & 1 deletion contracts/authority/EntryPointAuth.sol
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,17 @@ pragma solidity ^0.8.20;
import "@account-abstraction/contracts/interfaces/IEntryPoint.sol";
import "../libraries/Errors.sol";

/**
* @title EntryPointAuth
* @notice Abstract contract to provide EntryPoint based authentication
* @dev Requires the inheriting contracts to implement the `_entryPoint` method
*/
abstract contract EntryPointAuth {
/**
* @notice Expected to return the associated entry point for the contract
* @dev Must be implemented by inheriting contracts
* @return The EntryPoint associated with the contract
*/
function _entryPoint() internal view virtual returns (IEntryPoint);

/*
Expand All @@ -16,7 +26,8 @@ abstract contract EntryPointAuth {
┌────────────┐ ┌────────┐
│ entryPoint │ ──► │ here │
└────────────┘ └────────┘

* @notice Modifier to ensure the caller is the expected entry point
* @dev If not called from the expected entry point, it will revert
*/
modifier onlyEntryPoint() {
if (msg.sender != address(_entryPoint())) {
Expand Down
14 changes: 14 additions & 0 deletions contracts/authority/ModuleAuth.sol
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,23 @@ pragma solidity ^0.8.20;

import "../libraries/Errors.sol";

/**
* @title ModuleAuth
* @notice Abstract contract to provide Module-based authentication
* @dev Requires the inheriting contracts to implement the `_isAuthorizedModule` method
*/
abstract contract ModuleAuth {
/**
* @notice Expected to return whether the current context is authorized as a module
* @dev Must be implemented by inheriting contracts
* @return True if the context is an authorized module, otherwise false
*/
function _isAuthorizedModule() internal view virtual returns (bool);

/**
* @notice Modifier to ensure the caller is an authorized module
* @dev If not called from an authorized module, it will revert
*/
modifier onlyModule() {
if (!_isAuthorizedModule()) {
revert Errors.CALLER_MUST_BE_MODULE();
Expand Down
11 changes: 11 additions & 0 deletions contracts/authority/OwnerAuth.sol
Original file line number Diff line number Diff line change
@@ -1,6 +1,17 @@
// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.20;

/**
* @title OwnerAuth
* @notice Abstract contract to provide Owner-based authentication
* @dev Requires the inheriting contracts to implement the `_isOwner` method
*/
abstract contract OwnerAuth {
/**
* @notice Expected to return whether the provided owner identifier matches the owner context
* @dev Must be implemented by inheriting contracts
* @param owner The owner identifier to be checked
* @return True if the provided owner identifier matches the current owner context, otherwise false
*/
function _isOwner(bytes32 owner) internal view virtual returns (bool);
}
4 changes: 2 additions & 2 deletions contracts/base/EntryPointManager.sol
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ import "../authority/EntryPointAuth.sol";
abstract contract EntryPointManager is EntryPointAuth {
IEntryPoint private immutable _ENTRY_POINT;

constructor(IEntryPoint anEntryPoint) {
_ENTRY_POINT = anEntryPoint;
constructor(IEntryPoint entryPoint) {
_ENTRY_POINT = entryPoint;
}

function _entryPoint() internal view override returns (IEntryPoint) {
Expand Down
25 changes: 22 additions & 3 deletions contracts/base/ExecutionManager.sol
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,26 @@ import "../authority/Authority.sol";
import "./PluginManager.sol";
import "../interfaces/IExecutionManager.sol";

/**
* @title ExecutionManager
* @notice Manages the execution of transactions and batches of transactions
* @dev Inherits functionality from IExecutionManager, Authority, and PluginManager
*/
abstract contract ExecutionManager is IExecutionManager, Authority, PluginManager {
/**
* execute a transaction
* @notice Execute a transaction
* @param dest The destination address for the transaction
* @param value The amount of ether to be sent with the transaction
* @param func The calldata for the transaction
*/
function execute(address dest, uint256 value, bytes calldata func) external override onlyEntryPoint {
_call(dest, value, func);
}

/**
* execute a sequence of transactions
* @notice Execute a sequence of transactions without any associated ether
* @param dest List of destination addresses for each transaction
* @param func List of calldata for each transaction
*/
function executeBatch(address[] calldata dest, bytes[] calldata func) external override onlyEntryPoint {
for (uint256 i = 0; i < dest.length;) {
Expand All @@ -26,7 +36,10 @@ abstract contract ExecutionManager is IExecutionManager, Authority, PluginManage
}

/**
* execute a sequence of transactions
* @notice Execute a sequence of transactions, each potentially having associated ether
* @param dest List of destination addresses for each transaction
* @param value List of ether amounts for each transaction
* @param func List of calldata for each transaction
*/
function executeBatch(address[] calldata dest, uint256[] calldata value, bytes[] calldata func)
external
Expand All @@ -41,6 +54,12 @@ abstract contract ExecutionManager is IExecutionManager, Authority, PluginManage
}
}

/**
* @dev Internal function to handle the call logic
* @param target Address of the target contract
* @param value Ether to be sent with the transaction
* @param data Calldata for the transaction
*/
function _call(address target, uint256 value, bytes memory data) private executeHook(target, value, data) {
assembly ("memory-safe") {
let result := call(gas(), target, value, add(data, 0x20), mload(data), 0, 0)
Expand Down
Loading