-
Notifications
You must be signed in to change notification settings - Fork 78
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: As a user, I want to have an example ERC-1271 module (#314)
- Loading branch information
1 parent
8b1edf9
commit 4c68daf
Showing
2 changed files
with
152 additions
and
0 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,76 @@ | ||
// SPDX-License-Identifier: MIT | ||
pragma solidity 0.8.21; | ||
|
||
import { AbstractModule } from "../../interface/AbstractModule.sol"; | ||
import { AttestationPayload } from "../../types/Structs.sol"; | ||
|
||
contract ERC1271Module is AbstractModule { | ||
address public owner; | ||
|
||
error InvalidSignature(); | ||
error WrongSender(); | ||
|
||
constructor(address _owner) { | ||
owner = _owner; | ||
} | ||
|
||
/** | ||
* @notice Recover the signer of hash, assuming it's an EOA account | ||
* @dev Only for EthSign signatures | ||
* @param _hash Hash of message that was signed | ||
* @param _signature Signature encoded as (bytes32 r, bytes32 s, uint8 v) | ||
*/ | ||
function recoverSigner(bytes32 _hash, bytes memory _signature) internal pure returns (address signer) { | ||
require(_signature.length == 65, "Invalid signature length"); | ||
bytes32 r; | ||
bytes32 s; | ||
uint8 v; | ||
assembly { | ||
r := mload(add(_signature, 0x20)) | ||
s := mload(add(_signature, 0x40)) | ||
v := byte(0, mload(add(_signature, 0x60))) | ||
} | ||
if (uint256(s) > 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0) { | ||
revert("SignatureValidator#recoverSigner: invalid signature 's' value"); | ||
} | ||
if (v != 27 && v != 28) { | ||
revert("SignatureValidator#recoverSigner: invalid signature 'v' value"); | ||
} | ||
|
||
signer = ecrecover(_hash, v, r, s); | ||
// Prevent signer from being 0x0 | ||
require(signer != address(0x0), "SignatureValidator#recoverSigner: INVALID_SIGNER"); | ||
return signer; | ||
} | ||
|
||
/** | ||
* @notice Verifies that the signer is the owner of the signing contract. | ||
*/ | ||
function isValidSignature(bytes32 _hash, bytes memory _signature) internal view returns (bytes4) { | ||
// Validate signatures | ||
address signer = recoverSigner(_hash, _signature); | ||
if (signer == owner) { | ||
return 0x1626ba7e; | ||
} else { | ||
return 0xffffffff; | ||
} | ||
} | ||
|
||
function run( | ||
AttestationPayload memory attestationPayload, | ||
bytes memory validationPayload, | ||
address txSender, | ||
uint256 /*value*/ | ||
) public view override { | ||
if (txSender != owner) { | ||
revert WrongSender(); | ||
} | ||
address signee = abi.decode(attestationPayload.subject, (address)); | ||
uint256 nonce = abi.decode(attestationPayload.attestationData, (uint256)); | ||
bytes memory message = abi.encodePacked(signee, nonce); | ||
bytes32 digest = keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n", message)); | ||
if (isValidSignature(digest, validationPayload) != 0x1626ba7e) { | ||
revert InvalidSignature(); | ||
} | ||
} | ||
} |
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,76 @@ | ||
// SPDX-License-Identifier: MIT | ||
pragma solidity 0.8.21; | ||
|
||
import { Test } from "forge-std/Test.sol"; | ||
import { AbstractModule } from "../../src/interface/AbstractModule.sol"; | ||
import { ERC1271Module } from "../../src/examples/modules/ERC1271Module.sol"; | ||
import { AttestationPayload } from "../../src/types/Structs.sol"; | ||
|
||
contract ERC1271ModuleTest is Test { | ||
ERC1271Module private erc1271Module; | ||
uint256 internal signerPrivateKey; | ||
address internal signerAddress; | ||
|
||
event ModuleRegistered(string name, string description, address moduleAddress); | ||
|
||
function setUp() public { | ||
(signerAddress, signerPrivateKey) = makeAddrAndKey("veraxUser"); | ||
erc1271Module = new ERC1271Module(signerAddress); | ||
vm.deal(signerAddress, 1 ether); | ||
} | ||
|
||
function test_ERC1271Module_verifySignature() public { | ||
address user = makeAddr("user"); | ||
uint256 nonce = 1234567; | ||
AttestationPayload memory attestationPayload = AttestationPayload( | ||
bytes32(uint256(1234)), | ||
0, | ||
abi.encode(user), | ||
abi.encode(nonce) | ||
); | ||
|
||
bytes32 hash = keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n", abi.encodePacked(user, nonce))); | ||
(uint8 v, bytes32 r, bytes32 s) = vm.sign(signerPrivateKey, hash); | ||
|
||
bytes memory signature = abi.encodePacked(r, s, v); | ||
|
||
erc1271Module.run(attestationPayload, signature, signerAddress, 0); | ||
} | ||
|
||
function test_ERC1271Module_InvalidSignature() public { | ||
address user = makeAddr("user"); | ||
uint256 nonce = 1234567; | ||
AttestationPayload memory attestationPayload = AttestationPayload( | ||
bytes32(uint256(1234)), | ||
0, | ||
abi.encode(user), | ||
abi.encode(nonce) | ||
); | ||
|
||
bytes32 hash = keccak256(abi.encodePacked("\x19Not a correct SignMessage:\n", abi.encodePacked(user, nonce))); | ||
(uint8 v, bytes32 r, bytes32 s) = vm.sign(signerPrivateKey, hash); | ||
|
||
bytes memory signature = abi.encodePacked(r, s, v); | ||
|
||
vm.expectRevert(ERC1271Module.InvalidSignature.selector); | ||
erc1271Module.run(attestationPayload, signature, signerAddress, 0); | ||
} | ||
|
||
function test_ERC1271Module_WrongSender() public { | ||
address user = makeAddr("NotASigner"); | ||
AttestationPayload memory attestationPayload = AttestationPayload( | ||
bytes32(uint256(1234)), | ||
0, | ||
bytes("subject"), | ||
new bytes(1) | ||
); | ||
|
||
vm.expectRevert(ERC1271Module.WrongSender.selector); | ||
erc1271Module.run(attestationPayload, bytes("0"), user, 0); | ||
} | ||
|
||
function test_EcRecoverModule_supportsInterface() public { | ||
bool isAbstractModuleSupported = erc1271Module.supportsInterface(type(AbstractModule).interfaceId); | ||
assertEq(isAbstractModuleSupported, true); | ||
} | ||
} |