mirror of
https://github.com/luxfi/onchain-id.git
synced 2026-10-30 15:23:49 +00:00
250 lines
8.6 KiB
Solidity
250 lines
8.6 KiB
Solidity
// SPDX-License-Identifier: GPL-3.0
|
|
pragma solidity ^0.8.17;
|
|
|
|
import "@openzeppelin/contracts/access/Ownable.sol";
|
|
import "@openzeppelin/contracts/utils/cryptography/ECDSA.sol";
|
|
import "../factory/IdFactory.sol";
|
|
|
|
using ECDSA for bytes32;
|
|
|
|
/// A required parameter was set to the Zero address.
|
|
error ZeroAddress();
|
|
/// The maximum number of signers was reached at deployment.
|
|
error TooManySigners();
|
|
/// The signed attempted to add was already approved.
|
|
error SignerAlreadyApproved(address signer);
|
|
/// The signed attempted to remove was not approved.
|
|
error SignerAlreadyNotApproved(address signer);
|
|
/// A requested ONCHAINID deployment was requested without a valid signature while the Gateway requires one.
|
|
error UnsignedDeployment();
|
|
/// A requested ONCHAINID deployment was requested and signer by a non approved signer.
|
|
error UnapprovedSigner(address signer);
|
|
/// A requested ONCHAINID deployment was requested with a signature revoked.
|
|
error RevokedSignature(bytes signature);
|
|
/// A requested ONCHAINID deployment was requested with a signature that expired.
|
|
error ExpiredSignature(bytes signature);
|
|
/// Attempted to revoke a signature that was already revoked.
|
|
error SignatureAlreadyRevoked(bytes signature);
|
|
/// Attempted to approve a signature that was not revoked.
|
|
error SignatureNotRevoked(bytes signature);
|
|
|
|
contract Gateway is Ownable {
|
|
IdFactory public idFactory;
|
|
mapping(address => bool) public approvedSigners;
|
|
mapping(bytes => bool) public revokedSignatures;
|
|
|
|
event SignerApproved(address indexed signer);
|
|
event SignerRevoked(address indexed signer);
|
|
event SignatureRevoked(bytes indexed signature);
|
|
event SignatureApproved(bytes indexed signature);
|
|
|
|
/**
|
|
* @dev Constructor for the ONCHAINID Factory Gateway.
|
|
* @param idFactoryAddress the address of the factory to operate (the Gateway must be owner of the Factory).
|
|
*/
|
|
constructor(address idFactoryAddress, address[] memory signersToApprove) Ownable() {
|
|
if (idFactoryAddress == address(0)) {
|
|
revert ZeroAddress();
|
|
}
|
|
if (signersToApprove.length > 10) {
|
|
revert TooManySigners();
|
|
}
|
|
|
|
for (uint i = 0; i < signersToApprove.length; i++) {
|
|
approvedSigners[signersToApprove[i]] = true;
|
|
}
|
|
|
|
idFactory = IdFactory(idFactoryAddress);
|
|
}
|
|
|
|
/**
|
|
* @dev Approve a signer to sign ONCHAINID deployments. If the Gateway is setup to require signature, only
|
|
* deployments requested with a valid signature from an approved signer will be accepted.
|
|
* If the gateway does not require a signature,
|
|
* @param signer the signer address to approve.
|
|
*/
|
|
function approveSigner(address signer) external onlyOwner {
|
|
if (signer == address(0)) {
|
|
revert ZeroAddress();
|
|
}
|
|
|
|
if (approvedSigners[signer]) {
|
|
revert SignerAlreadyApproved(signer);
|
|
}
|
|
|
|
approvedSigners[signer] = true;
|
|
|
|
emit SignerApproved(signer);
|
|
}
|
|
|
|
/**
|
|
* @dev Revoke a signer to sign ONCHAINID deployments.
|
|
* @param signer the signer address to revoke.
|
|
*/
|
|
function revokeSigner(address signer) external onlyOwner {
|
|
if (signer == address(0)) {
|
|
revert ZeroAddress();
|
|
}
|
|
|
|
if (!approvedSigners[signer]) {
|
|
revert SignerAlreadyNotApproved(signer);
|
|
}
|
|
|
|
delete approvedSigners[signer];
|
|
|
|
emit SignerRevoked(signer);
|
|
}
|
|
|
|
/**
|
|
* @dev Deploy an ONCHAINID using a factory. The operation must be signed by
|
|
* an approved public key. This method allow to deploy an ONCHAINID using a custom salt.
|
|
* @param identityOwner the address to set as a management key.
|
|
* @param salt to use for the deployment.
|
|
* @param signatureExpiry the block timestamp where the signature will expire.
|
|
* @param signature the approval containing the salt and the identityOwner address.
|
|
*/
|
|
function deployIdentityWithSalt(
|
|
address identityOwner,
|
|
string memory salt,
|
|
uint256 signatureExpiry,
|
|
bytes calldata signature
|
|
) external returns (address) {
|
|
if (identityOwner == address(0)) {
|
|
revert ZeroAddress();
|
|
}
|
|
|
|
if (signatureExpiry != 0 && signatureExpiry < block.timestamp) {
|
|
revert ExpiredSignature(signature);
|
|
}
|
|
|
|
address signer = ECDSA.recover(
|
|
keccak256(
|
|
abi.encode(
|
|
"Authorize ONCHAINID deployment",
|
|
identityOwner,
|
|
salt,
|
|
signatureExpiry
|
|
)
|
|
).toEthSignedMessageHash(),
|
|
signature
|
|
);
|
|
|
|
if (!approvedSigners[signer]) {
|
|
revert UnapprovedSigner(signer);
|
|
}
|
|
|
|
if (revokedSignatures[signature]) {
|
|
revert RevokedSignature(signature);
|
|
}
|
|
|
|
return idFactory.createIdentity(identityOwner, salt);
|
|
}
|
|
|
|
/**
|
|
* @dev Deploy an ONCHAINID using a factory. The operation must be signed by
|
|
* an approved public key. This method allow to deploy an ONCHAINID using a custom salt and a custom list of
|
|
* management keys. Note that the identity Owner address won't be added as a management keys, if this is desired,
|
|
* the key hash must be listed in the managementKeys array.
|
|
* @param identityOwner the address to set as a management key.
|
|
* @param salt to use for the deployment.
|
|
* @param managementKeys the list of management keys to add to the ONCHAINID.
|
|
* @param signatureExpiry the block timestamp where the signature will expire.
|
|
* @param signature the approval containing the salt and the identityOwner address.
|
|
*/
|
|
function deployIdentityWithSaltAndManagementKeys(
|
|
address identityOwner,
|
|
string memory salt,
|
|
bytes32[] calldata managementKeys,
|
|
uint256 signatureExpiry,
|
|
bytes calldata signature
|
|
) external returns (address) {
|
|
if (identityOwner == address(0)) {
|
|
revert ZeroAddress();
|
|
}
|
|
|
|
if (signatureExpiry != 0 && signatureExpiry < block.timestamp) {
|
|
revert ExpiredSignature(signature);
|
|
}
|
|
|
|
address signer = ECDSA.recover(
|
|
keccak256(
|
|
abi.encode(
|
|
"Authorize ONCHAINID deployment",
|
|
identityOwner,
|
|
salt,
|
|
managementKeys,
|
|
signatureExpiry
|
|
)
|
|
).toEthSignedMessageHash(),
|
|
signature
|
|
);
|
|
|
|
if (!approvedSigners[signer]) {
|
|
revert UnapprovedSigner(signer);
|
|
}
|
|
|
|
if (revokedSignatures[signature]) {
|
|
revert RevokedSignature(signature);
|
|
}
|
|
|
|
return idFactory.createIdentityWithManagementKeys(identityOwner, salt, managementKeys);
|
|
}
|
|
|
|
/**
|
|
* @dev Deploy an ONCHAINID using a factory using the identityOwner address as salt.
|
|
* @param identityOwner the address to set as a management key.
|
|
*/
|
|
function deployIdentityForWallet(address identityOwner) external returns (address) {
|
|
if (identityOwner == address(0)) {
|
|
revert ZeroAddress();
|
|
}
|
|
|
|
return idFactory.createIdentity(identityOwner, Strings.toHexString(identityOwner));
|
|
}
|
|
|
|
/**
|
|
* @dev Revoke a signature, if the signature is used to deploy an ONCHAINID, the deployment would be rejected.
|
|
* @param signature the signature to revoke.
|
|
*/
|
|
function revokeSignature(bytes calldata signature) external onlyOwner {
|
|
if (revokedSignatures[signature]) {
|
|
revert SignatureAlreadyRevoked(signature);
|
|
}
|
|
|
|
revokedSignatures[signature] = true;
|
|
|
|
emit SignatureRevoked(signature);
|
|
}
|
|
|
|
/**
|
|
* @dev Remove a signature from the revoke list.
|
|
* @param signature the signature to approve.
|
|
*/
|
|
function approveSignature(bytes calldata signature) external onlyOwner {
|
|
if (!revokedSignatures[signature]) {
|
|
revert SignatureNotRevoked(signature);
|
|
}
|
|
|
|
delete revokedSignatures[signature];
|
|
|
|
emit SignatureApproved(signature);
|
|
}
|
|
|
|
/**
|
|
* @dev Transfer the ownership of the factory to a new owner.
|
|
* @param newOwner the new owner of the factory.
|
|
*/
|
|
function transferFactoryOwnership(address newOwner) external onlyOwner {
|
|
idFactory.transferOwnership(newOwner);
|
|
}
|
|
|
|
/**
|
|
* @dev Call a function on the factory. Only the owner of the Gateway can call this method.
|
|
* @param data the data to call on the factory.
|
|
*/
|
|
function callFactory(bytes memory data) external onlyOwner {
|
|
(bool success,) = address(idFactory).call(data);
|
|
require(success, "Gateway: call to factory failed");
|
|
}
|
|
}
|