Skip to content
Closed
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
6 changes: 6 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,9 @@
[submodule "lib/solady"]
path = lib/solady
url = https://github.com/vectorized/solady
[submodule "lib/limit-order-protocol"]
path = lib/limit-order-protocol
url = https://github.com/1inch/limit-order-protocol
[submodule "lib/solidity-utils"]
path = lib/solidity-utils
url = https://github.com/1inch/solidity-utils
66 changes: 59 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,16 +1,68 @@
# Kernel
# Patch Wallets

Kernel is a minimal smart contract account designed to be extended.
Patch Wallets is a wallet protocol that leverages EIP-4337 compliant smart contracts to provide a secure and convenient way for users to transact on Ethereum and EVM-compatible networks using their email, phone number, or social media accounts in a non-custodial manner. This repository contains the Kernel smart contracts source code for Patch Wallets.

- [Docs](https://docs.zerodev.app/extend-wallets/overview)
- [Code](https://github.com/zerodevapp/kernel)
## Resources

## Build
- [Docs](https://docs.patchwallet.com/projects/patch-wallets)
- [Code](https://github.com/PaymagicXYZ/kernel)

Make sure [Foundry](https://github.com/foundry-rs/foundry) is installed. Then:
## Introduction

```
Patch Wallets allow users to transact on the Ethereum network using their email, phone number, or social media accounts, eliminating the need for private keys or custodians. The protocol uses the EIP-4337 compatible Kernel contracts with the same deterministic address deployed to all EVM chains. Each wallet is attached to the email, phone number, or social media account of the user and transaction signing can be done through any signing service, although we recommend Lit Protocol.

## Getting Started

### Prerequisites

Ensure [Foundry](https://github.com/foundry-rs/foundry) is installed.

### Build

To build the project, run the following commands:

```bash
forge install
forge build
forge test
```

## Deploy

### Test Contracts

First runs simulation, giving gas costs.

```bash
forge script scripts/DeployKernelMultiTest.s.sol --sig "run(bytes32 salt)" "0x1" --fork-url <RPC_URL>
```

Actually broadcasts deploy and setup txs to the network

```bash
forge script scripts/DeployKernelMultiTest.s.sol --sig "run(bytes32 salt)" "0x1" --fork-url <RPC_URL> --broadcast
```

This deploys contracts to following deterministic addresses:

- MultiECDSAValidatorNew: 0x3153652fAC4454b9Ae847d38F607aE78731902C9
- MultiECDSAFactoryPatch: 0xA39A072E0330f940C10496546125B5e6a3B4B811

### Production Contracts

First runs simulation, giving gas costs.

```bash
forge script scripts/DeployKernelMultiProd.s.sol --sig "run(bytes32 salt)" "0x2" --fork-url <RPC_URL>
```

Actually broadcasts deploy and setup txs to the network

```bash
forge script scripts/DeployKernelMultiProd.s.sol --sig "run(bytes32 salt)" "0x2" --fork-url <RPC_URL> --broadcast
```

This deploys contracts to following deterministic addresses:

- MultiECDSAValidatorNew: 0x9392C6a8A0b5d49cc697B8242d477509bAE16700
- MultiECDSAFactoryPatch: 0x33DDF684dcc6937FfE59D8405aA80c41fB518C5c

Large diffs are not rendered by default.

Large diffs are not rendered by default.

321 changes: 321 additions & 0 deletions broadcast/DeployMultiECDSAFactoryPatchProd.s.sol/100/run-latest.json

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

321 changes: 321 additions & 0 deletions broadcast/DeployMultiECDSAFactoryPatchProd.s.sol/10200/run-latest.json

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

469 changes: 469 additions & 0 deletions broadcast/DeployMultiECDSAFactoryPatchProd.s.sol/137/run-latest.json

Large diffs are not rendered by default.

Large diffs are not rendered by default.

321 changes: 321 additions & 0 deletions broadcast/DeployMultiECDSAFactoryPatchProd.s.sol/42161/run-latest.json

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

321 changes: 321 additions & 0 deletions broadcast/DeployMultiECDSAFactoryPatchProd.s.sol/59140/run-latest.json

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

321 changes: 321 additions & 0 deletions broadcast/DeployMultiECDSAFactoryPatchProd.s.sol/59144/run-latest.json

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

469 changes: 469 additions & 0 deletions broadcast/DeployMultiECDSAFactoryPatchProd.s.sol/80001/run-latest.json

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

267 changes: 267 additions & 0 deletions broadcast/DeployMultiECDSAFactoryPatchTest.s.sol/100/run-latest.json

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

267 changes: 267 additions & 0 deletions broadcast/DeployMultiECDSAFactoryPatchTest.s.sol/10200/run-latest.json

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

287 changes: 287 additions & 0 deletions broadcast/DeployMultiECDSAFactoryPatchTest.s.sol/137/run-latest.json

Large diffs are not rendered by default.

Large diffs are not rendered by default.

267 changes: 267 additions & 0 deletions broadcast/DeployMultiECDSAFactoryPatchTest.s.sol/42161/run-latest.json

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

267 changes: 267 additions & 0 deletions broadcast/DeployMultiECDSAFactoryPatchTest.s.sol/59140/run-latest.json

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

267 changes: 267 additions & 0 deletions broadcast/DeployMultiECDSAFactoryPatchTest.s.sol/59144/run-latest.json

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

399 changes: 399 additions & 0 deletions broadcast/DeployMultiECDSAFactoryPatchTest.s.sol/80001/run-latest.json

Large diffs are not rendered by default.

2 changes: 2 additions & 0 deletions remappings.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,5 @@ forge-std/=lib/forge-std/src/
openzeppelin-contracts/=lib/openzeppelin-contracts/
@openzeppelin/=lib/openzeppelin-contracts
solady/=lib/solady/src/
limit-order-protocol/=lib/limit-order-protocol/contracts/
@1inch/solidity-utils/=lib/solidity-utils/
58 changes: 58 additions & 0 deletions scripts/DeployMultiECDSAFactoryPatchProd.s.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
pragma solidity ^0.8.0;

import "src/factory/MultiECDSAFactoryPatch.sol";
import "src/validator/MultiECDSAValidatorNew.sol";
import "src/Kernel.sol";
import "forge-std/Script.sol";
import "forge-std/console.sol";

contract DeployMultiECDSAFactoryPatchProd is Script {
function run(bytes32 salt) public {
uint256 key = vm.envUint("DEPLOYER_PRIVATE_KEY");
vm.startBroadcast(key);

Kernel kernelImplementation = new Kernel{salt: salt}(
IEntryPoint(0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789)
);
console.log("Kernel deployed at: %s", address(kernelImplementation));

MultiECDSAValidatorNew multiECDSAValidatorNew = new MultiECDSAValidatorNew{
salt: salt
}();
console.log(
"MultiECDSAValidatorNew deployed at: %s",
address(multiECDSAValidatorNew)
);

MultiECDSAFactoryPatch multiECDSAFactoryPatch = new MultiECDSAFactoryPatch{
salt: salt
}(
0xf0d5D3FcBFc0009121A630EC8AB67e012117f40c,
IEntryPoint(0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789),
address(kernelImplementation),
multiECDSAValidatorNew
);
console.log(
"MultiECDSAFactoryPatch deployed at: %s",
address(multiECDSAFactoryPatch)
);

multiECDSAFactoryPatch.setImplementation(
address(kernelImplementation),
true
);

address[] memory owners = new address[](1);
owners[0] = address(0xdD664b8A02d3B13C0bdfB1878CbE66aA53B2de06);

multiECDSAFactoryPatch.setOwners(owners);

multiECDSAFactoryPatch.addStake{value: 1}(1);

multiECDSAFactoryPatch.transferOwnership(
0x74427681c620DE258Aa53a382d6a4C865738A06C
);

vm.stopBroadcast();
}
}
54 changes: 54 additions & 0 deletions scripts/DeployMultiECDSAFactoryPatchTest.s.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
pragma solidity ^0.8.0;

import "src/factory/MultiECDSAFactoryPatch.sol";
import "src/validator/MultiECDSAValidatorNew.sol";
import "src/Kernel.sol";
import "forge-std/Script.sol";
import "forge-std/console.sol";

contract DeployMultiECDSAFactoryPatchTest is Script {
function run(bytes32 salt) public {
uint256 key = vm.envUint("DEPLOYER_PRIVATE_KEY");
vm.startBroadcast(key);

Kernel kernelImplementation = new Kernel{salt: salt}(
IEntryPoint(0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789)
);
console.log("Kernel deployed at: %s", address(kernelImplementation));

MultiECDSAValidatorNew multiECDSAValidatorNew = new MultiECDSAValidatorNew{
salt: salt
}();
console.log(
"MultiECDSAValidatorNew deployed at: %s",
address(multiECDSAValidatorNew)
);

MultiECDSAFactoryPatch multiECDSAFactoryPatch = new MultiECDSAFactoryPatch{
salt: salt
}(
0xf0d5D3FcBFc0009121A630EC8AB67e012117f40c,
IEntryPoint(0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789),
address(kernelImplementation),
multiECDSAValidatorNew
);
console.log(
"MultiECDSAFactoryPatch deployed at: %s",
address(multiECDSAFactoryPatch)
);

multiECDSAFactoryPatch.setImplementation(
address(kernelImplementation),
true
);

address[] memory owners = new address[](1);
owners[0] = address(0xaD6442a1b5A9D5a25eDE2f8dC3A99C7038b95CD5);

multiECDSAFactoryPatch.setOwners(owners);

multiECDSAFactoryPatch.addStake{value: 1}(1);

vm.stopBroadcast();
}
}
25 changes: 25 additions & 0 deletions src/factory/MultiECDSAFactory.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
pragma solidity ^0.8.0;

import "./KernelFactory.sol";
import "src/interfaces/IAddressBook.sol";

contract MultiECDSAFactory is KernelFactory, IAddressBook {
address[] owners;
address public implementation;

constructor(
address _owner,
IEntryPoint _entryPoint,
address _implementation
) KernelFactory(_owner, _entryPoint) {
implementation = _implementation;
}

function getOwners() external view override returns (address[] memory) {
return owners;
}

function setOwners(address[] memory _owners) external onlyOwner {
owners = _owners;
}
}
56 changes: 56 additions & 0 deletions src/factory/MultiECDSAFactoryPatch.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
pragma solidity ^0.8.0;

import "src/Kernel.sol";
import "src/factory/KernelFactory.sol";
import "src/interfaces/IAddressBook.sol";
import "src/abstract/KernelStorage.sol";
import "src/validator/MultiECDSAValidatorNew.sol";

contract MultiECDSAFactoryPatch is KernelFactory, IAddressBook {
address[] owners;

address public kernel;
MultiECDSAValidatorNew public immutable multiECDSAValidatorNew;

constructor(
address _owner,
IEntryPoint _entryPoint,
address _kernel,
MultiECDSAValidatorNew _multiECDSAValidatorNew
) KernelFactory(_owner, _entryPoint) {
kernel = _kernel;
multiECDSAValidatorNew = _multiECDSAValidatorNew;
}

function getOwners() external view override returns (address[] memory) {
return owners;
}

function setOwners(address[] memory _owners) external onlyOwner {
owners = _owners;
}

function setKernel(address _kernel) external onlyOwner {
kernel = _kernel;
}

function createAccount(
uint256 _index
) external payable returns (address proxy) {
bytes memory data = abi.encodeWithSelector(
KernelStorage.initialize.selector,
multiECDSAValidatorNew,
abi.encodePacked(address(this))
);
proxy = this.createAccount(kernel, data, _index);
}

function getAccountAddress(uint256 _index) public view returns (address) {
bytes memory _data = abi.encodeWithSelector(
KernelStorage.initialize.selector,
multiECDSAValidatorNew,
abi.encodePacked(address(this))
);
return this.getAccountAddress(_data, _index);
}
}
76 changes: 76 additions & 0 deletions src/validator/MultiECDSAValidatorNew.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

import "solady/utils/ECDSA.sol";
import "src/utils/KernelHelper.sol";
import "src/interfaces/IAddressBook.sol";
import "src/interfaces/IValidator.sol";
import "src/common/Types.sol";

contract MultiECDSAValidatorNew is IKernelValidator {
event OwnerAdded(address indexed kernel, address indexed owner);
event OwnerRemoved(address indexed kernel, address indexed owner);

mapping(address owner => mapping(address kernel => bool) hello)
public isOwner;

function disable(bytes calldata _data) external payable override {
address[] memory owners = abi.decode(_data, (address[]));
for (uint256 i = 0; i < owners.length; i++) {
isOwner[owners[i]][msg.sender] = false;
emit OwnerRemoved(msg.sender, owners[i]);
}
}

function enable(bytes calldata _data) external payable override {
address addressBook = address(bytes20(_data));
address[] memory owners = IAddressBook(addressBook).getOwners();
for (uint256 i = 0; i < owners.length; i++) {
isOwner[owners[i]][msg.sender] = true;
emit OwnerAdded(msg.sender, owners[i]);
}
}

function validateUserOp(
UserOperation calldata _userOp,
bytes32 _userOpHash,
uint256
) external payable override returns (ValidationData validationData) {
address signer = ECDSA.recover(_userOpHash, _userOp.signature);
if (isOwner[signer][msg.sender]) {
return ValidationData.wrap(0);
}

bytes32 hash = ECDSA.toEthSignedMessageHash(_userOpHash);
signer = ECDSA.recover(hash, _userOp.signature);
if (!isOwner[signer][msg.sender]) {
return SIG_VALIDATION_FAILED;
}
return ValidationData.wrap(0);
}

function validateSignature(
bytes32 hash,
bytes calldata signature
) public view override returns (ValidationData) {
bytes32 wrappedHash = keccak256(abi.encodePacked(hash, msg.sender));
address signer = ECDSA.recover(wrappedHash, signature);
if (isOwner[signer][msg.sender]) {
return ValidationData.wrap(0);
}
bytes32 ethHash = ECDSA.toEthSignedMessageHash(wrappedHash);
signer = ECDSA.recover(ethHash, signature);
if (!isOwner[signer][msg.sender]) {
return SIG_VALIDATION_FAILED;
}
return ValidationData.wrap(0);
}

function validCaller(
address _caller,
bytes calldata
) external view override returns (bool) {
return isOwner[_caller][msg.sender];
}
}
Loading