-
Notifications
You must be signed in to change notification settings - Fork 74
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: Introduce Modules V2 with more flexibility
- Loading branch information
Showing
13 changed files
with
1,087 additions
and
4 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
// SPDX-License-Identifier: MIT | ||
pragma solidity 0.8.21; | ||
|
||
import { AttestationPayload } from "../types/Structs.sol"; | ||
import { IERC165 } from "openzeppelin-contracts/contracts/utils/introspection/IERC165.sol"; | ||
|
||
/** | ||
* @title Abstract Module V2 | ||
* @author Consensys | ||
* @notice Defines the minimal Module V2 interface | ||
*/ | ||
abstract contract AbstractModuleV2 is IERC165 { | ||
/// @notice Error thrown when someone else than the portal's owner is trying to revoke | ||
error OnlyPortalOwner(); | ||
|
||
/** | ||
* @notice Executes the module's custom logic | ||
* @param attestationPayload The incoming attestation data | ||
* @param validationPayload Additional data required for verification | ||
* @param initialCaller The address of the initial caller (transaction sender) | ||
* @param value The value (ETH) optionally passed in the attesting transaction | ||
* @param attester The address defined by the Portal as the attester for this payload | ||
* @param portal The issuing Portal's address | ||
*/ | ||
function run( | ||
AttestationPayload memory attestationPayload, | ||
bytes memory validationPayload, | ||
address initialCaller, | ||
uint256 value, | ||
address attester, | ||
address portal | ||
) public virtual; | ||
|
||
/** | ||
* @notice Checks if the contract implements the Module interface. | ||
* @param interfaceID The ID of the interface to check. | ||
* @return A boolean indicating interface support. | ||
*/ | ||
function supportsInterface(bytes4 interfaceID) public pure virtual override returns (bool) { | ||
return interfaceID == type(AbstractModuleV2).interfaceId || interfaceID == type(IERC165).interfaceId; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,83 @@ | ||
// SPDX-License-Identifier: MIT | ||
pragma solidity 0.8.21; | ||
|
||
import { AbstractModuleV2 } from "../abstracts/AbstractModuleV2.sol"; | ||
import { AttestationPayload } from "../types/Structs.sol"; | ||
import { PortalRegistry } from "../PortalRegistry.sol"; | ||
import { ECDSA } from "openzeppelin-contracts/contracts/utils/cryptography/ECDSA.sol"; | ||
|
||
/** | ||
* @title ECDSA Module V2 | ||
* @author Consensys | ||
* @notice This Module can be used by Portal creators to | ||
* require a signature of the attestation payload | ||
* from an authorized signer before issuing the attestation. | ||
* @dev DISCLAIMER: This module doesn't check that a signature can be used only once! | ||
*/ | ||
contract ECDSAModuleV2 is AbstractModuleV2 { | ||
using ECDSA for bytes32; | ||
|
||
PortalRegistry public portalRegistry; | ||
|
||
mapping(address portal => mapping(address signer => bool authorizedSigners)) public authorizedSigners; | ||
|
||
/// @notice Error thrown when an array length mismatch occurs | ||
error ArrayLengthMismatch(); | ||
/// @notice Error thrown when a signer is not authorized by the module | ||
error SignerNotAuthorized(); | ||
|
||
/// @notice Event emitted when the authorized signers are set | ||
event SignersAuthorized(address indexed portal, address[] signers, bool[] authorizationStatus); | ||
|
||
modifier onlyPortalOwner(address portal) { | ||
if (msg.sender != portalRegistry.getPortalByAddress(portal).ownerAddress) revert OnlyPortalOwner(); | ||
_; | ||
} | ||
|
||
/** | ||
* @notice Contract constructor sets the portal registry | ||
*/ | ||
constructor(address _portalRegistry) { | ||
portalRegistry = PortalRegistry(_portalRegistry); | ||
} | ||
|
||
/** | ||
* @notice Set the authorized status of signers | ||
* @param signers The signers to be set | ||
* @param authorizationStatus The authorization status of signers | ||
* @dev The length of `signers` and `authorizationStatus` must be the same | ||
*/ | ||
function setAuthorizedSigners( | ||
address portal, | ||
address[] memory signers, | ||
bool[] memory authorizationStatus | ||
) public onlyPortalOwner(portal) { | ||
if (signers.length != authorizationStatus.length) revert ArrayLengthMismatch(); | ||
|
||
for (uint256 i = 0; i < signers.length; i++) { | ||
authorizedSigners[portal][signers[i]] = authorizationStatus[i]; | ||
} | ||
|
||
emit SignersAuthorized(portal, signers, authorizationStatus); | ||
} | ||
|
||
/** | ||
* @inheritdoc AbstractModuleV2 | ||
* @param attestationPayload The Payload of the attestation | ||
* @param validationPayload The validation payload required for the module, in this case the signature of the payload | ||
* @param portal The Portal issuing the attestation | ||
* @notice If the signer of the transaction payload is not an expected address, an error is thrown | ||
*/ | ||
function run( | ||
AttestationPayload memory attestationPayload, | ||
bytes memory validationPayload, | ||
address /*initialCaller*/, | ||
uint256 /*value*/, | ||
address /*attester*/, | ||
address portal | ||
) public view override { | ||
bytes32 messageHash = keccak256(abi.encode(attestationPayload)); | ||
address messageSigner = messageHash.toEthSignedMessageHash().recover(validationPayload); | ||
if (!authorizedSigners[portal][messageSigner]) revert SignerNotAuthorized(); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,101 @@ | ||
// SPDX-License-Identifier: MIT | ||
pragma solidity 0.8.21; | ||
|
||
import { AbstractModuleV2 } from "../abstracts/AbstractModuleV2.sol"; | ||
import { PortalRegistry } from "../PortalRegistry.sol"; | ||
import { AttestationPayload } from "../types/Structs.sol"; | ||
import { ECDSA } from "openzeppelin-contracts/contracts/utils/cryptography/ECDSA.sol"; | ||
import { SignatureChecker } from "openzeppelin-contracts/contracts/utils/cryptography/SignatureChecker.sol"; | ||
import { IERC1271 } from "openzeppelin-contracts/contracts/interfaces/IERC1271.sol"; | ||
import { Address } from "openzeppelin-contracts/contracts/utils/Address.sol"; | ||
|
||
/** | ||
* @title ERC-1271 Module V2 | ||
* @author Consensys | ||
* @notice This Module can be used by Portal creators to | ||
* require an ERC_1271 signature of the attestation payload | ||
* from an authorized signer before issuing the attestation. | ||
* @dev DISCLAIMER: This module doesn't check that a signature can be used only once! | ||
*/ | ||
contract ERC1271ModuleV2 is AbstractModuleV2 { | ||
using ECDSA for bytes32; | ||
using Address for address; | ||
|
||
PortalRegistry public portalRegistry; | ||
|
||
mapping(address portal => mapping(address signer => bool authorizedSigners)) public authorizedSigners; | ||
|
||
/// @notice Error thrown when an array length mismatch occurs | ||
error ArrayLengthMismatch(); | ||
/// @notice Error thrown when a signer is not authorized by the module | ||
error SignerNotAuthorized(); | ||
/// @notice Error thrown when a signature is invalid | ||
error InvalidSignature(); | ||
/// @notice Error thrown when a signature validation fails | ||
error FailedValidation(); | ||
|
||
/// @notice Event emitted when the authorized signers are set | ||
event SignersAuthorized(address indexed portal, address[] signers, bool[] authorizationStatus); | ||
|
||
modifier onlyPortalOwner(address portal) { | ||
if (msg.sender != portalRegistry.getPortalByAddress(portal).ownerAddress) revert OnlyPortalOwner(); | ||
_; | ||
} | ||
|
||
/** | ||
* @notice Contract constructor sets the portal registry | ||
*/ | ||
constructor(address _portalRegistry) { | ||
portalRegistry = PortalRegistry(_portalRegistry); | ||
} | ||
|
||
/** | ||
* @notice Set the accepted status of schemaIds | ||
* @param signers The signers to be set | ||
* @param authorizationStatus The authorization status of signers | ||
* @dev The length of `signers` and `authorizationStatus` must be the same | ||
*/ | ||
function setAuthorizedSigners( | ||
address portal, | ||
address[] memory signers, | ||
bool[] memory authorizationStatus | ||
) public onlyPortalOwner(portal) { | ||
if (signers.length != authorizationStatus.length) revert ArrayLengthMismatch(); | ||
|
||
for (uint256 i = 0; i < signers.length; i++) { | ||
authorizedSigners[portal][signers[i]] = authorizationStatus[i]; | ||
} | ||
|
||
emit SignersAuthorized(portal, signers, authorizationStatus); | ||
} | ||
|
||
/** | ||
* @inheritdoc AbstractModuleV2 | ||
* @param attestationPayload The Payload of the attestation | ||
* @param validationPayload The validation payload required for the module, in this case the signature of the payload | ||
* @param portal The Portal issuing the attestation | ||
* @notice If the signer of the transaction payload is not an expected address, an error is thrown | ||
*/ | ||
function run( | ||
AttestationPayload memory attestationPayload, | ||
bytes memory validationPayload, | ||
address /*initialCaller*/, | ||
uint256 /*value*/, | ||
address /*attester*/, | ||
address portal | ||
) public view override { | ||
bytes32 messageHash = keccak256(abi.encode(attestationPayload)); | ||
address messageSigner = messageHash.toEthSignedMessageHash().recover(validationPayload); | ||
if (!authorizedSigners[portal][messageSigner]) revert SignerNotAuthorized(); | ||
|
||
if (messageSigner.isContract()) { | ||
try IERC1271(messageSigner).isValidSignature(messageHash.toEthSignedMessageHash(), validationPayload) returns ( | ||
bytes4 magicValue | ||
) { | ||
if (magicValue != IERC1271.isValidSignature.selector) revert InvalidSignature(); | ||
} catch { | ||
revert FailedValidation(); | ||
} | ||
} | ||
} | ||
} |
Oops, something went wrong.