Skip to content

Commit

Permalink
readme update
Browse files Browse the repository at this point in the history
  • Loading branch information
CodeExplorer29 committed Apr 19, 2024
1 parent c5800f6 commit d7fe0f2
Show file tree
Hide file tree
Showing 12 changed files with 179 additions and 80 deletions.
21 changes: 4 additions & 17 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@

- Support [ERC-4337: Account Abstraction](https://eips.ethereum.org/EIPS/eip-4337)
- [Modular design ](https://hackmd.io/3gbndH7tSl2J1EbNePJ3Yg)
- Implement [asset / keystore](https://hackmd.io/-YY8jD7IQ7qfEZaDepXZsA?view) separation architecture
- Upgradability: The smart contract for this wallet can be upgraded in a secure way to add new features or fix vulnerabilities in the future.
- Stablecoin pay gas: Users can pay transaction gas fees with stablecoins such as USDC, USDT, DAI, etc.

Expand Down Expand Up @@ -42,34 +41,22 @@ All contracts are held within the `soul-wallet-contract/contracts` folder.
```
contracts
├── abstract
├── automation
├── dev
│ └── tokens
├── factory
├── hooks
│ └── 2fa
├── interfaces
├── keystore
│ ├── L1
│ │ ├── base
│ │ └── interfaces
│ └── interfaces
├── libraries
├── modules
│ ├── interfaces
│ ├── keystore
│ │ ├── arbitrum
│ ├── socialRecovery
│ │ ├── base
│ │ ├── interfaces
│ │ └── optimism
│ ├── securityControlModule
│ │ └── trustedContractManager
│ │ ├── trustedHookManager
│ │ ├── trustedModuleManager
│ │ └── trustedValidatorManager
│ │ └── interfaces
│ └── upgrade
├── paymaster
│ └── interfaces
├── proxy
└── validator
└── libraries
```
Expand Down Expand Up @@ -114,7 +101,7 @@ contract NewModule is BaseModule {

### Hook

o integrate a new hook, your contract should inherit `IHook` interface. This interface will define the standard structure and functionalities for your hooks.
To integrate a new hook, your contract should inherit `IHook` interface. This interface will define the standard structure and functionalities for your hooks.

```solidity
Expand Down
11 changes: 11 additions & 0 deletions contracts/SoulWallet.sol
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,13 @@ import {SoulWalletModuleManager} from "./abstract/SoulWalletModuleManager.sol";
import {SoulWalletHookManager} from "./abstract/SoulWalletHookManager.sol";
import {SoulWalletUpgradeManager} from "./abstract/SoulWalletUpgradeManager.sol";

/**
* @title SoulWallet
* @dev This contract is the main entry point for the SoulWallet. It implements the IAccount and IERC1271 interfaces,
* and is compatible with the ERC-4337 standard.
* It inherits from multiple base contracts and managers to provide the core functionality of the wallet.
* This includes managing entry points, owners, modules, hooks, and upgrades, as well as handling ERC1271 signatures and providing a fallback function.
*/
contract SoulWallet is
Initializable,
IAccount,
Expand All @@ -37,6 +44,10 @@ contract SoulWallet is
_disableInitializers();
}

/**
* @notice Initializes the SoulWallet contract
* @dev This function can only be called once. It sets the initial owners, default callback handler, modules, and hooks.
*/
function initialize(
bytes32[] calldata owners,
address defalutCallbackHandler,
Expand Down
19 changes: 19 additions & 0 deletions contracts/automation/AaveUsdcSaveAutomation.sol
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@ interface IAaveV3 {
function supply(address asset, uint256 amount, address onBehalfOf, uint16 referralCode) external;
}

/**
* @title AaveUsdcSaveAutomation
* @dev This contract allows a bot to deposit USDC to Aave on behalf of a user.
*/
contract AaveUsdcSaveAutomation is Ownable {
using SafeERC20 for IERC20;

Expand All @@ -18,6 +22,9 @@ contract AaveUsdcSaveAutomation is Ownable {
IAaveV3 immutable aave;
mapping(address => bool) public bots;

/**
* @dev Modifier to make a function callable only by a bot.
*/
modifier onlyBot() {
require(bots[msg.sender], "no permission");
_;
Expand All @@ -29,12 +36,24 @@ contract AaveUsdcSaveAutomation is Ownable {
usdcToken.approve(address(aave), 2 ** 256 - 1);
}

/**
* @notice Deposits USDC to Aave on behalf of a user
* @dev This function can only be called by a bot
* @param _user The address of the user for whom to deposit USDC
* @param amount The amount of USDC to deposit
*/
function depositUsdcToAave(address _user, uint256 amount) public onlyBot {
usdcToken.safeTransferFrom(_user, address(this), amount);
aave.supply(address(usdcToken), amount, _user, 0);
emit UsdcDepositedToAave(_user, amount);
}

/**
* @notice Deposits USDC to Aave on behalf of multiple users
* @dev This function can only be called by a bot
* @param _users An array of addresses of the users for whom to deposit USDC
* @param amounts An array of amounts of USDC to deposit for each user
*/
function depositUsdcToAaveBatch(address[] calldata _users, uint256[] calldata amounts) public onlyBot {
require(_users.length == amounts.length, "invalid input");
for (uint256 i = 0; i < _users.length; i++) {
Expand Down
19 changes: 16 additions & 3 deletions contracts/dev/ReceivePayment.sol
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,31 @@ pragma solidity ^0.8.20;

import "@openzeppelin/contracts/access/Ownable.sol";

/**
* @title ReceivePayment
* @dev This contract isto collect social recovery fees from users.
* Users pay ETH to this contract, and we will performs the social recovery operation for them.
* so that user only need to collect guardian signatures, after that, our backend will perform the recovery operation.
*/
contract ReceivePayment is Ownable {
event PaymentReceived(bytes32 indexed paymentId, address indexed sender, uint256 amount);

constructor(address _owner) Ownable(_owner) {}
// just emit event id with payment id generated by backend
// there is no need to add logic in contract to validate payment id
// it is handled by backend

/**
* @notice Makes a payment to the contract
* @dev This function is payable, meaning it can receive Ether. It emits a PaymentReceived event.
* @param _paymentId The ID of the payment, generated by the backend
*/
function pay(bytes32 _paymentId) external payable {
emit PaymentReceived(_paymentId, msg.sender, msg.value);
}

/**
* @notice Withdraws the contract balance to a specified address
* @dev This function can only be called by the owner of the contract. It emits a Withdraw event.
* @param _to The address to which the funds will be withdrawn
*/
function withdraw(address _to) external onlyOwner {
(bool success,) = payable(_to).call{value: address(this).balance}("");
require(success, "Withdraw failed");
Expand Down
11 changes: 0 additions & 11 deletions contracts/libraries/Errors.sol
Original file line number Diff line number Diff line change
Expand Up @@ -6,35 +6,24 @@ library Errors {
error ADDRESS_NOT_EXISTS();
error DATA_ALREADY_EXISTS();
error DATA_NOT_EXISTS();
error CALLER_MUST_BE_ENTRYPOINT();
error CALLER_MUST_BE_SELF_OR_MODULE();
error CALLER_MUST_BE_MODULE();
error HASH_ALREADY_APPROVED();
error HASH_ALREADY_REJECTED();
error INVALID_ADDRESS();
error INVALID_GUARD_HOOK_DATA();
error INVALID_SELECTOR();
error INVALID_SIGNTYPE();
error MODULE_ADDRESS_EMPTY();
error MODULE_NOT_SUPPORT_INTERFACE();
error MODULE_SELECTOR_UNAUTHORIZED();
error MODULE_SELECTORS_EMPTY();
error MODULE_EXECUTE_FROM_MODULE_RECURSIVE();
error NO_OWNER();
error SELECTOR_ALREADY_EXISTS();
error SELECTOR_NOT_EXISTS();
error UNSUPPORTED_SIGNTYPE();
error INVALID_LOGIC_ADDRESS();
error SAME_LOGIC_ADDRESS();
error UPGRADE_FAILED();
error NOT_IMPLEMENTED();
error INVALID_SIGNATURE();
error ALERADY_INITIALIZED();
error INVALID_KEY();
error NOT_INITIALIZED();
error INVALID_TIME_RANGE();
error UNAUTHORIZED();
error INVALID_DATA();
error GUARDIAN_SIGNATURE_INVALID();
error UNTRUSTED_KEYSTORE_LOGIC();
}
51 changes: 51 additions & 0 deletions contracts/modules/socialRecovery/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
# SocialRecoveryModule

The `SocialRecoveryModule` is a Solidity contract that can be installed in wallets to enable a social recovery mechanism. This module allows a user to designate a list of guardians for their wallet and establish a recovery threshold. If a wallet is lost or compromised, the guardians can initiate a recovery process by signing a special EIP712 signature. However, this recovery process is subject to a user-defined time lock period, and the guardians can only execute the recovery after this period has passed. This mechanism ensures that the user's assets remain secure and recoverable, even in unforeseen circumstances.

## Recovery flow

![social recovery flow](socialReoceryFlow.png)

- Step 1: Users install the Social Recovery Module in their SoulWallet. They need to configure the guardian hash and the execution delay period when installing the module. The guardian hash refers to the keccak256 hash of the GuardianData, ensuring the privacy of guardian identities. Others cannot check your guardians' settings on-chain and they are only revealed when the user initiates the social recovery process.

```solidity
struct GuardianData {
address[] guardians;
uint256 threshold;
uint256 salt;
}
```

- Step 2: When users want to recover their wallet using the guardians, they have to contact the guardians to sign an EIP-712 based signature and use the following scheme:

- EIP712Domain

```json
{
"EIP712Domain": [
{ "type": "uint256", "name": "chainId" },
{ "type": "address", "name": "SocialRecovery" }
]
}
```

- SocialRecovery

```json
{
"SocialRecovery": [
{ "type": "address", "name": "wallet" },
{ "type": "uint256", "name": "nonce" },
{ "type": "bytes32[]", "name": "newOwners" }
]
}
```

Once the signatures are collected and the threshold is met, it can call scheduleRecovery to enter the waiting queue for recovery.

- Step 3: If the timelock period has passed, one can call `executeRecovery` to perform the recovery. The social recovery module will then reset the owners based on the previous setting.

## Considerations

- Users can call `cancelAllRecovery` to invalidate transactions in the pending queue.
- Users can call `setGuardian` to change guardians' settings without a timelock.
24 changes: 23 additions & 1 deletion contracts/modules/socialRecovery/SocialRecoveryModule.sol
Original file line number Diff line number Diff line change
@@ -1,21 +1,34 @@
pragma solidity ^0.8.17;
pragma solidity ^0.8.20;

import "../BaseModule.sol";
import "./base/BaseSocialRecovery.sol";

/**
* @title SocialRecoveryModule
* @dev This contract extends BaseModule and BaseSocialRecovery to provide social recovery functionality for a wallet.
* It allows a wallet owner to set a list of guardians and a recovery delay period. If the wallet is lost or compromised,
* the guardians can recover the wallet after the delay period has passed.
*/
contract SocialRecoveryModule is BaseModule, BaseSocialRecovery {
bytes4 private constant _FUNC_RESET_OWNER = bytes4(keccak256("resetOwner(bytes32)"));
bytes4 private constant _FUNC_RESET_OWNERS = bytes4(keccak256("resetOwners(bytes32[])"));
mapping(address => bool) walletInited;

constructor() EIP712("SocialRecovery", "1") {}

/**
* @dev De-initializes the social recovery settings for the sender's wallet.
*/
function _deInit() internal override {
address _sender = sender();
_clearWalletSocialRecoveryInfo(_sender);
walletInited[_sender] = false;
}

/**
* @dev Initializes the social recovery settings for the sender's wallet.
* @param _data The encoded guardian hash and delay period.
*/
function _init(bytes calldata _data) internal override {
address _sender = sender();
(bytes32 guardianHash, uint256 delayPeroid) = abi.decode(_data, (bytes32, uint256));
Expand All @@ -24,10 +37,19 @@ contract SocialRecoveryModule is BaseModule, BaseSocialRecovery {
walletInited[_sender] = true;
}

/**
* @dev Checks if the social recovery settings for a wallet have been initialized.
* @param wallet The address of the wallet.
* @return A boolean indicating whether the social recovery settings for the wallet have been initialized.
*/
function inited(address wallet) internal view override returns (bool) {
return walletInited[wallet];
}

/**
* @dev Returns the list of functions required by this module.
* @return An array of function selectors.
*/
function requiredFunctions() external pure override returns (bytes4[] memory) {
bytes4[] memory functions = new bytes4[](2);
functions[0] = _FUNC_RESET_OWNER;
Expand Down
Loading

0 comments on commit d7fe0f2

Please sign in to comment.