Skip to content

Commit

Permalink
feat: As a user, I want to have an example ERC-1271 module (#314)
Browse files Browse the repository at this point in the history
  • Loading branch information
Builddddder authored Oct 23, 2023
1 parent 8b1edf9 commit 4c68daf
Show file tree
Hide file tree
Showing 2 changed files with 152 additions and 0 deletions.
76 changes: 76 additions & 0 deletions contracts/src/examples/modules/ERC1271Module.sol
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();
}
}
}
76 changes: 76 additions & 0 deletions contracts/test/example/ERC1271Module.t.sol
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);
}
}

0 comments on commit 4c68daf

Please sign in to comment.