Skip to content

Commit b8442f2

Browse files
authored
multisignature signer (#96)
1 parent 9a37972 commit b8442f2

File tree

2 files changed

+123
-4
lines changed

2 files changed

+123
-4
lines changed
Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
// SPDX-License-Identifier: MIT
2+
3+
pragma solidity ^0.8.0;
4+
5+
import {ECDSA} from "solady/utils/ECDSA.sol";
6+
import {MerkleProofLib} from "solady/utils/MerkleProofLib.sol";
7+
import {ISigner} from "../interfaces/IERC7579Modules.sol";
8+
import {SignerBase} from "../sdk/moduleBase/SignerBase.sol";
9+
import {PackedUserOperation} from "../interfaces/PackedUserOperation.sol";
10+
import {
11+
SIG_VALIDATION_SUCCESS_UINT,
12+
SIG_VALIDATION_FAILED_UINT,
13+
MODULE_TYPE_VALIDATOR,
14+
MODULE_TYPE_HOOK,
15+
ERC1271_MAGICVALUE,
16+
ERC1271_INVALID
17+
} from "../types/Constants.sol";
18+
19+
struct ECDSAValidatorStorage {
20+
address owner;
21+
}
22+
23+
contract MultiSignatureECDSASigner is SignerBase {
24+
mapping(address => uint256) public usedIds;
25+
mapping(bytes32 id => mapping(address wallet => address)) public signer;
26+
event SignerRegistered(address indexed kernel, bytes32 indexed id, address indexed owner);
27+
error NoSignerRegistered();
28+
29+
function isInitialized(address wallet) external view override returns (bool) {
30+
return usedIds[wallet] > 0;
31+
}
32+
33+
function _signerOninstall(bytes32 id, bytes calldata _data) internal override {
34+
if(signer[id][msg.sender] == address(0)) {
35+
usedIds[msg.sender]++;
36+
}
37+
signer[id][msg.sender] = address(bytes20(_data[0:20]));
38+
emit SignerRegistered(msg.sender, id, address(bytes20(_data[0:20])));
39+
}
40+
41+
function _signerOnUninstall(bytes32 id, bytes calldata) internal override {
42+
if(signer[id][msg.sender] == address(0)) {
43+
revert NoSignerRegistered();
44+
}
45+
delete signer[id][msg.sender];
46+
usedIds[msg.sender]--;
47+
}
48+
49+
function checkUserOpSignature(bytes32 id, PackedUserOperation calldata userOp, bytes32 userOpHash)
50+
external
51+
payable
52+
override
53+
returns (uint256)
54+
{
55+
bytes calldata sig = userOp.signature;
56+
address owner = signer[id][msg.sender];
57+
if (sig.length == 65) {
58+
// simple ecdsa verification
59+
if (owner == ECDSA.recover(userOpHash, sig)) {
60+
return SIG_VALIDATION_SUCCESS_UINT;
61+
}
62+
bytes32 ethHash = ECDSA.toEthSignedMessageHash(userOpHash);
63+
address recovered = ECDSA.recover(ethHash, sig);
64+
if (owner != recovered) {
65+
return SIG_VALIDATION_FAILED_UINT;
66+
}
67+
return SIG_VALIDATION_SUCCESS_UINT;
68+
}
69+
bytes memory ecdsaSig = sig[0:65];
70+
bytes32 merkleRoot = bytes32(sig[65:97]);
71+
bytes32[] memory proof = abi.decode(sig[97:], (bytes32[]));
72+
require(MerkleProofLib.verify(proof, merkleRoot, userOpHash), "hash is not in proof");
73+
// simple ecdsa verification
74+
if (owner == ECDSA.recover(merkleRoot, ecdsaSig)) {
75+
return SIG_VALIDATION_SUCCESS_UINT;
76+
}
77+
bytes32 ethRoot = ECDSA.toEthSignedMessageHash(merkleRoot);
78+
address merkleRecovered = ECDSA.recover(ethRoot, ecdsaSig);
79+
if (owner != merkleRecovered) {
80+
return SIG_VALIDATION_FAILED_UINT;
81+
}
82+
return SIG_VALIDATION_SUCCESS_UINT;
83+
}
84+
85+
function checkSignature(bytes32 id, address sender, bytes32 hash, bytes calldata sig)
86+
external
87+
view
88+
override
89+
returns (bytes4)
90+
{
91+
address owner = signer[id][msg.sender];
92+
if (sig.length == 65) {
93+
// simple ecdsa verification
94+
if (owner == ECDSA.recover(hash, sig)) {
95+
return ERC1271_MAGICVALUE;
96+
}
97+
bytes32 ethHash = ECDSA.toEthSignedMessageHash(hash);
98+
address recovered = ECDSA.recover(ethHash, sig);
99+
if (owner != recovered) {
100+
return ERC1271_INVALID;
101+
}
102+
return ERC1271_MAGICVALUE;
103+
}
104+
bytes memory ecdsaSig = sig[0:65];
105+
bytes32 merkleRoot = bytes32(sig[65:97]);
106+
bytes32[] memory proof = abi.decode(sig[97:], (bytes32[]));
107+
require(MerkleProofLib.verify(proof, merkleRoot, hash), "hash is not in proof");
108+
// simple ecdsa verification
109+
if (owner == ECDSA.recover(merkleRoot, ecdsaSig)) {
110+
return ERC1271_MAGICVALUE;
111+
}
112+
bytes32 ethRoot = ECDSA.toEthSignedMessageHash(merkleRoot);
113+
address merkleRecovered = ECDSA.recover(ethRoot, ecdsaSig);
114+
if (owner != merkleRecovered) {
115+
return ERC1271_INVALID;
116+
}
117+
return ERC1271_MAGICVALUE;
118+
}
119+
}

src/validator/MultiSignatureECDSAValidator.sol

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -77,8 +77,8 @@ contract MultiSignatureECDSAValidator is IValidator, IHook {
7777
return SIG_VALIDATION_SUCCESS_UINT;
7878
}
7979
bytes32 ethRoot = ECDSA.toEthSignedMessageHash(merkleRoot);
80-
address recovered = ECDSA.recover(ethRoot, ecdsaSig);
81-
if (owner != recovered) {
80+
address merkleRecovered = ECDSA.recover(ethRoot, ecdsaSig);
81+
if (owner != merkleRecovered) {
8282
return SIG_VALIDATION_FAILED_UINT;
8383
}
8484
return SIG_VALIDATION_SUCCESS_UINT;
@@ -112,8 +112,8 @@ contract MultiSignatureECDSAValidator is IValidator, IHook {
112112
return ERC1271_MAGICVALUE;
113113
}
114114
bytes32 ethRoot = ECDSA.toEthSignedMessageHash(merkleRoot);
115-
address recovered = ECDSA.recover(ethRoot, ecdsaSig);
116-
if (owner != recovered) {
115+
address merkleRecovered = ECDSA.recover(ethRoot, ecdsaSig);
116+
if (owner != merkleRecovered) {
117117
return ERC1271_INVALID;
118118
}
119119
return ERC1271_MAGICVALUE;

0 commit comments

Comments
 (0)