Skip to content

Commit

Permalink
Merge pull request #111 from morpho-labs/refactor/ecdsa
Browse files Browse the repository at this point in the history
refactor(sig-utils): simplify contract
  • Loading branch information
MerlinEgalite authored Sep 25, 2023
2 parents c423211 + 26b7fd6 commit d3ead8c
Show file tree
Hide file tree
Showing 2 changed files with 20 additions and 40 deletions.
31 changes: 9 additions & 22 deletions test/forge/PermitTest.sol
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,6 @@ import "./helpers/SigUtils.sol";
import "./helpers/BaseTest.sol";

contract PermitTest is BaseTest {
SigUtils internal sigUtils;

uint256 internal constant OWNER_PK = 0xA11CE;
uint256 internal constant SPENDER_PK = 0xB0B;

Expand All @@ -16,8 +14,6 @@ contract PermitTest is BaseTest {
function setUp() public override {
super.setUp();

sigUtils = new SigUtils(vault.DOMAIN_SEPARATOR());

owner = vm.addr(OWNER_PK);
spender = vm.addr(SPENDER_PK);

Expand All @@ -27,8 +23,7 @@ contract PermitTest is BaseTest {
function testPermit() public {
Permit memory permit = Permit({owner: owner, spender: spender, value: 1e18, nonce: 0, deadline: 1 days});

bytes32 digest = sigUtils.getTypedDataHash(permit);

bytes32 digest = SigUtils.toTypedDataHash(vault.DOMAIN_SEPARATOR(), permit);
(uint8 v, bytes32 r, bytes32 s) = vm.sign(OWNER_PK, digest);

vault.permit(permit.owner, permit.spender, permit.value, permit.deadline, v, r, s);
Expand All @@ -41,8 +36,7 @@ contract PermitTest is BaseTest {
Permit memory permit =
Permit({owner: owner, spender: spender, value: 1e18, nonce: vault.nonces(owner), deadline: 1 days});

bytes32 digest = sigUtils.getTypedDataHash(permit);

bytes32 digest = SigUtils.toTypedDataHash(vault.DOMAIN_SEPARATOR(), permit);
(uint8 v, bytes32 r, bytes32 s) = vm.sign(OWNER_PK, digest);

vm.warp(1 days + 1 seconds); // fast forward one second past the deadline
Expand All @@ -55,8 +49,7 @@ contract PermitTest is BaseTest {
Permit memory permit =
Permit({owner: owner, spender: spender, value: 1e18, nonce: vault.nonces(owner), deadline: 1 days});

bytes32 digest = sigUtils.getTypedDataHash(permit);

bytes32 digest = SigUtils.toTypedDataHash(vault.DOMAIN_SEPARATOR(), permit);
(uint8 v, bytes32 r, bytes32 s) = vm.sign(SPENDER_PK, digest); // spender signs owner's approval

vm.expectRevert("ERC20Permit: invalid signature");
Expand All @@ -72,8 +65,7 @@ contract PermitTest is BaseTest {
deadline: 1 days
});

bytes32 digest = sigUtils.getTypedDataHash(permit);

bytes32 digest = SigUtils.toTypedDataHash(vault.DOMAIN_SEPARATOR(), permit);
(uint8 v, bytes32 r, bytes32 s) = vm.sign(OWNER_PK, digest);

vm.expectRevert("ERC20Permit: invalid signature");
Expand All @@ -83,8 +75,7 @@ contract PermitTest is BaseTest {
function testRevertSignatureReplay() public {
Permit memory permit = Permit({owner: owner, spender: spender, value: 1e18, nonce: 0, deadline: 1 days});

bytes32 digest = sigUtils.getTypedDataHash(permit);

bytes32 digest = SigUtils.toTypedDataHash(vault.DOMAIN_SEPARATOR(), permit);
(uint8 v, bytes32 r, bytes32 s) = vm.sign(OWNER_PK, digest);

vault.permit(permit.owner, permit.spender, permit.value, permit.deadline, v, r, s);
Expand All @@ -96,8 +87,7 @@ contract PermitTest is BaseTest {
function testTransferFromLimitedPermit() public {
Permit memory permit = Permit({owner: owner, spender: spender, value: 1e18, nonce: 0, deadline: 1 days});

bytes32 digest = sigUtils.getTypedDataHash(permit);

bytes32 digest = SigUtils.toTypedDataHash(vault.DOMAIN_SEPARATOR(), permit);
(uint8 v, bytes32 r, bytes32 s) = vm.sign(OWNER_PK, digest);

vault.permit(permit.owner, permit.spender, permit.value, permit.deadline, v, r, s);
Expand All @@ -114,8 +104,7 @@ contract PermitTest is BaseTest {
Permit memory permit =
Permit({owner: owner, spender: spender, value: type(uint256).max, nonce: 0, deadline: 1 days});

bytes32 digest = sigUtils.getTypedDataHash(permit);

bytes32 digest = SigUtils.toTypedDataHash(vault.DOMAIN_SEPARATOR(), permit);
(uint8 v, bytes32 r, bytes32 s) = vm.sign(OWNER_PK, digest);

vault.permit(permit.owner, permit.spender, permit.value, permit.deadline, v, r, s);
Expand All @@ -137,8 +126,7 @@ contract PermitTest is BaseTest {
deadline: 1 days
});

bytes32 digest = sigUtils.getTypedDataHash(permit);

bytes32 digest = SigUtils.toTypedDataHash(vault.DOMAIN_SEPARATOR(), permit);
(uint8 v, bytes32 r, bytes32 s) = vm.sign(OWNER_PK, digest);

vault.permit(permit.owner, permit.spender, permit.value, permit.deadline, v, r, s);
Expand All @@ -156,8 +144,7 @@ contract PermitTest is BaseTest {
deadline: 1 days
});

bytes32 digest = sigUtils.getTypedDataHash(permit);

bytes32 digest = SigUtils.toTypedDataHash(vault.DOMAIN_SEPARATOR(), permit);
(uint8 v, bytes32 r, bytes32 s) = vm.sign(OWNER_PK, digest);

vault.permit(permit.owner, permit.spender, permit.value, permit.deadline, v, r, s);
Expand Down
29 changes: 11 additions & 18 deletions test/forge/helpers/SigUtils.sol
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.0;

import {ECDSA} from "@openzeppelin/utils/cryptography/ECDSA.sol";

struct Permit {
address owner;
address spender;
Expand All @@ -9,25 +11,16 @@ struct Permit {
uint256 deadline;
}

// keccak256("Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)");
bytes32 constant PERMIT_TYPEHASH = 0x6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c9;

contract SigUtils {
bytes32 internal DOMAIN_SEPARATOR;

constructor(bytes32 _DOMAIN_SEPARATOR) {
DOMAIN_SEPARATOR = _DOMAIN_SEPARATOR;
}
bytes32 constant PERMIT_TYPEHASH =
keccak256("Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)");

// computes the hash of a permit
function getStructHash(Permit memory _permit) internal pure returns (bytes32) {
return keccak256(
abi.encode(PERMIT_TYPEHASH, _permit.owner, _permit.spender, _permit.value, _permit.nonce, _permit.deadline)
library SigUtils {
function toTypedDataHash(bytes32 domainSeparator, Permit memory permit) internal pure returns (bytes32) {
return ECDSA.toTypedDataHash(
domainSeparator,
keccak256(
abi.encode(PERMIT_TYPEHASH, permit.owner, permit.spender, permit.value, permit.nonce, permit.deadline)
)
);
}

// computes the hash of the fully encoded EIP-712 message for the domain, which can be used to recover the signer
function getTypedDataHash(Permit memory _permit) public view returns (bytes32) {
return keccak256(abi.encodePacked("\x19\x01", DOMAIN_SEPARATOR, getStructHash(_permit)));
}
}

0 comments on commit d3ead8c

Please sign in to comment.