Skip to content

Commit 3f3b07a

Browse files
authored
✨ Add Verifier and Gateways contracts
2 parents f995018 + d532f6d commit 3f3b07a

14 files changed

+2032
-4
lines changed

Diff for: .solcover.js

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
module.exports = {
2+
skipFiles: [
3+
"_testContracts",
4+
],
5+
};

Diff for: .solhint.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
"imports-on-top": "error",
1919
"ordering": "error",
2020
"visibility-modifier-order": "error",
21-
"code-complexity": ["error", 7],
21+
"code-complexity": ["error", 11],
2222
"function-max-lines": ["error", 50],
2323
"max-line-length": ["error", 130],
2424
"max-states-count": ["error", 15],

Diff for: CHANGELOG.md

+9
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,15 @@ All notable changes to this project will be documented in this file.
44
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
55
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
66

7+
## [2.1.0]
8+
9+
### Added
10+
- Implemented a new contract `Gateway` to interact with the `IdFactory`. The `Gateway` contract allows individual
11+
accounts (being EOA or contracts) to deploy identities for their own address as a salt. To deploy using
12+
a custom salt, a signature from an approved signer is required.
13+
- Implemented a new base contract `Verifier` to be extended by contract requiring identity verification based on claims
14+
and trusted issuers.
15+
716
## [2.0.1]
817

918
### Added

Diff for: contracts/_testContracts/VerifierUser.sol

+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
/* solhint-disable */
2+
3+
// SPDX-License-Identifier: GPL-3.0
4+
pragma solidity 0.8.17;
5+
6+
import "../verifiers/Verifier.sol";
7+
8+
contract VerifierUser is Verifier {
9+
constructor() Verifier() {}
10+
11+
function doSomething() onlyVerifiedSender public {}
12+
}

Diff for: contracts/gateway/Gateway.sol

+249
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,249 @@
1+
// SPDX-License-Identifier: GPL-3.0
2+
pragma solidity 0.8.17;
3+
4+
import "@openzeppelin/contracts/access/Ownable.sol";
5+
import "@openzeppelin/contracts/utils/cryptography/ECDSA.sol";
6+
import "../factory/IdFactory.sol";
7+
8+
using ECDSA for bytes32;
9+
10+
/// A required parameter was set to the Zero address.
11+
error ZeroAddress();
12+
/// The maximum number of signers was reached at deployment.
13+
error TooManySigners();
14+
/// The signed attempted to add was already approved.
15+
error SignerAlreadyApproved(address signer);
16+
/// The signed attempted to remove was not approved.
17+
error SignerAlreadyNotApproved(address signer);
18+
/// A requested ONCHAINID deployment was requested without a valid signature while the Gateway requires one.
19+
error UnsignedDeployment();
20+
/// A requested ONCHAINID deployment was requested and signer by a non approved signer.
21+
error UnapprovedSigner(address signer);
22+
/// A requested ONCHAINID deployment was requested with a signature revoked.
23+
error RevokedSignature(bytes signature);
24+
/// A requested ONCHAINID deployment was requested with a signature that expired.
25+
error ExpiredSignature(bytes signature);
26+
/// Attempted to revoke a signature that was already revoked.
27+
error SignatureAlreadyRevoked(bytes signature);
28+
/// Attempted to approve a signature that was not revoked.
29+
error SignatureNotRevoked(bytes signature);
30+
31+
contract Gateway is Ownable {
32+
IdFactory public idFactory;
33+
mapping(address => bool) public approvedSigners;
34+
mapping(bytes => bool) public revokedSignatures;
35+
36+
event SignerApproved(address indexed signer);
37+
event SignerRevoked(address indexed signer);
38+
event SignatureRevoked(bytes indexed signature);
39+
event SignatureApproved(bytes indexed signature);
40+
41+
/**
42+
* @dev Constructor for the ONCHAINID Factory Gateway.
43+
* @param idFactoryAddress the address of the factory to operate (the Gateway must be owner of the Factory).
44+
*/
45+
constructor(address idFactoryAddress, address[] memory signersToApprove) Ownable() {
46+
if (idFactoryAddress == address(0)) {
47+
revert ZeroAddress();
48+
}
49+
if (signersToApprove.length > 10) {
50+
revert TooManySigners();
51+
}
52+
53+
for (uint i = 0; i < signersToApprove.length; i++) {
54+
approvedSigners[signersToApprove[i]] = true;
55+
}
56+
57+
idFactory = IdFactory(idFactoryAddress);
58+
}
59+
60+
/**
61+
* @dev Approve a signer to sign ONCHAINID deployments. If the Gateway is setup to require signature, only
62+
* deployments requested with a valid signature from an approved signer will be accepted.
63+
* If the gateway does not require a signature,
64+
* @param signer the signer address to approve.
65+
*/
66+
function approveSigner(address signer) external onlyOwner {
67+
if (signer == address(0)) {
68+
revert ZeroAddress();
69+
}
70+
71+
if (approvedSigners[signer]) {
72+
revert SignerAlreadyApproved(signer);
73+
}
74+
75+
approvedSigners[signer] = true;
76+
77+
emit SignerApproved(signer);
78+
}
79+
80+
/**
81+
* @dev Revoke a signer to sign ONCHAINID deployments.
82+
* @param signer the signer address to revoke.
83+
*/
84+
function revokeSigner(address signer) external onlyOwner {
85+
if (signer == address(0)) {
86+
revert ZeroAddress();
87+
}
88+
89+
if (!approvedSigners[signer]) {
90+
revert SignerAlreadyNotApproved(signer);
91+
}
92+
93+
delete approvedSigners[signer];
94+
95+
emit SignerRevoked(signer);
96+
}
97+
98+
/**
99+
* @dev Deploy an ONCHAINID using a factory. The operation must be signed by
100+
* an approved public key. This method allow to deploy an ONCHAINID using a custom salt.
101+
* @param identityOwner the address to set as a management key.
102+
* @param salt to use for the deployment.
103+
* @param signatureExpiry the block timestamp where the signature will expire.
104+
* @param signature the approval containing the salt and the identityOwner address.
105+
*/
106+
function deployIdentityWithSalt(
107+
address identityOwner,
108+
string memory salt,
109+
uint256 signatureExpiry,
110+
bytes calldata signature
111+
) external returns (address) {
112+
if (identityOwner == address(0)) {
113+
revert ZeroAddress();
114+
}
115+
116+
if (signatureExpiry != 0 && signatureExpiry < block.timestamp) {
117+
revert ExpiredSignature(signature);
118+
}
119+
120+
address signer = ECDSA.recover(
121+
keccak256(
122+
abi.encode(
123+
"Authorize ONCHAINID deployment",
124+
identityOwner,
125+
salt,
126+
signatureExpiry
127+
)
128+
).toEthSignedMessageHash(),
129+
signature
130+
);
131+
132+
if (!approvedSigners[signer]) {
133+
revert UnapprovedSigner(signer);
134+
}
135+
136+
if (revokedSignatures[signature]) {
137+
revert RevokedSignature(signature);
138+
}
139+
140+
return idFactory.createIdentity(identityOwner, salt);
141+
}
142+
143+
/**
144+
* @dev Deploy an ONCHAINID using a factory. The operation must be signed by
145+
* an approved public key. This method allow to deploy an ONCHAINID using a custom salt and a custom list of
146+
* management keys. Note that the identity Owner address won't be added as a management keys, if this is desired,
147+
* the key hash must be listed in the managementKeys array.
148+
* @param identityOwner the address to set as a management key.
149+
* @param salt to use for the deployment.
150+
* @param managementKeys the list of management keys to add to the ONCHAINID.
151+
* @param signatureExpiry the block timestamp where the signature will expire.
152+
* @param signature the approval containing the salt and the identityOwner address.
153+
*/
154+
function deployIdentityWithSaltAndManagementKeys(
155+
address identityOwner,
156+
string memory salt,
157+
bytes32[] calldata managementKeys,
158+
uint256 signatureExpiry,
159+
bytes calldata signature
160+
) external returns (address) {
161+
if (identityOwner == address(0)) {
162+
revert ZeroAddress();
163+
}
164+
165+
if (signatureExpiry != 0 && signatureExpiry < block.timestamp) {
166+
revert ExpiredSignature(signature);
167+
}
168+
169+
address signer = ECDSA.recover(
170+
keccak256(
171+
abi.encode(
172+
"Authorize ONCHAINID deployment",
173+
identityOwner,
174+
salt,
175+
managementKeys,
176+
signatureExpiry
177+
)
178+
).toEthSignedMessageHash(),
179+
signature
180+
);
181+
182+
if (!approvedSigners[signer]) {
183+
revert UnapprovedSigner(signer);
184+
}
185+
186+
if (revokedSignatures[signature]) {
187+
revert RevokedSignature(signature);
188+
}
189+
190+
return idFactory.createIdentityWithManagementKeys(identityOwner, salt, managementKeys);
191+
}
192+
193+
/**
194+
* @dev Deploy an ONCHAINID using a factory using the identityOwner address as salt.
195+
* @param identityOwner the address to set as a management key.
196+
*/
197+
function deployIdentityForWallet(address identityOwner) external returns (address) {
198+
if (identityOwner == address(0)) {
199+
revert ZeroAddress();
200+
}
201+
202+
return idFactory.createIdentity(identityOwner, Strings.toHexString(identityOwner));
203+
}
204+
205+
/**
206+
* @dev Revoke a signature, if the signature is used to deploy an ONCHAINID, the deployment would be rejected.
207+
* @param signature the signature to revoke.
208+
*/
209+
function revokeSignature(bytes calldata signature) external onlyOwner {
210+
if (revokedSignatures[signature]) {
211+
revert SignatureAlreadyRevoked(signature);
212+
}
213+
214+
revokedSignatures[signature] = true;
215+
216+
emit SignatureRevoked(signature);
217+
}
218+
219+
/**
220+
* @dev Remove a signature from the revoke list.
221+
* @param signature the signature to approve.
222+
*/
223+
function approveSignature(bytes calldata signature) external onlyOwner {
224+
if (!revokedSignatures[signature]) {
225+
revert SignatureNotRevoked(signature);
226+
}
227+
228+
delete revokedSignatures[signature];
229+
230+
emit SignatureApproved(signature);
231+
}
232+
233+
/**
234+
* @dev Transfer the ownership of the factory to a new owner.
235+
* @param newOwner the new owner of the factory.
236+
*/
237+
function transferFactoryOwnership(address newOwner) external onlyOwner {
238+
idFactory.transferOwnership(newOwner);
239+
}
240+
241+
/**
242+
* @dev Call a function on the factory. Only the owner of the Gateway can call this method.
243+
* @param data the data to call on the factory.
244+
*/
245+
function callFactory(bytes memory data) external onlyOwner {
246+
(bool success,) = address(idFactory).call(data);
247+
require(success, "Gateway: call to factory failed");
248+
}
249+
}

0 commit comments

Comments
 (0)