-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
6 changed files
with
291 additions
and
6 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
70 changes: 70 additions & 0 deletions
70
solidity/contracts/modules/accessControl/PermitAccessModule.sol
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,70 @@ | ||
// SPDX-License-Identifier: MIT | ||
pragma solidity ^0.8.19; | ||
|
||
import {IModule, Module} from '@defi-wonderland/prophet-core/solidity/contracts/Module.sol'; | ||
import {IOracle} from '@defi-wonderland/prophet-core/solidity/interfaces/IOracle.sol'; | ||
|
||
import { | ||
IAccessControlModule, IPermitAccessModule | ||
} from '../../../interfaces/modules/accessControl/IPermitAccessModule.sol'; | ||
|
||
import {Nonces} from '@openzeppelin/contracts/utils/Nonces.sol'; | ||
import {ECDSA} from '@openzeppelin/contracts/utils/cryptography/ECDSA.sol'; | ||
import {EIP712} from '@openzeppelin/contracts/utils/cryptography/EIP712.sol'; | ||
|
||
contract PermitAccessModule is Module, IPermitAccessModule, EIP712, Nonces { | ||
constructor(IOracle _oracle) Module(_oracle) EIP712('PermitAccessModule', '1') {} | ||
|
||
/// @inheritdoc IPermitAccessModule | ||
function decodeAccessControlData(bytes memory _data) | ||
public | ||
pure | ||
returns (IAccessControlModule.AccessControlParameters memory _accessControlData) | ||
{ | ||
_accessControlData = abi.decode(_data, (IAccessControlModule.AccessControlParameters)); | ||
} | ||
|
||
/// @inheritdoc IPermitAccessModule | ||
function decodePermitParametersData(bytes memory _data) public pure returns (PermitParameters memory _permitData) { | ||
_permitData = abi.decode(_data, (PermitParameters)); | ||
} | ||
|
||
/// @inheritdoc IModule | ||
function moduleName() external pure returns (string memory _moduleName) { | ||
_moduleName = 'PermitAccessModule'; | ||
} | ||
|
||
/// @inheritdoc IPermitAccessModule | ||
function getDomainSeparator() external view returns (bytes32 _domainSeparator) { | ||
_domainSeparator = _domainSeparatorV4(); | ||
} | ||
|
||
/// @inheritdoc IAccessControlModule | ||
function hasAccess(bytes memory _data) external returns (bool _hasAccess) { | ||
IAccessControlModule.AccessControlParameters memory _accessControlData = decodeAccessControlData(_data); | ||
PermitParameters memory _permitData = decodePermitParametersData(_accessControlData.accessControl.data); | ||
|
||
if (block.timestamp > _permitData.deadline) { | ||
revert PermitAccessModule_InvalidDeadline(); | ||
} | ||
|
||
bytes32 _structHash = keccak256( | ||
abi.encode( | ||
_accessControlData.typehash, | ||
_accessControlData.accessControl.user, | ||
_accessControlData.sender, | ||
_useNonce(_accessControlData.accessControl.user), | ||
_permitData.deadline | ||
) | ||
); | ||
|
||
bytes32 _hash = _hashTypedDataV4(_structHash); | ||
|
||
address _signer = ECDSA.recover(_hash, _permitData.v, _permitData.r, _permitData.s); | ||
if (_signer != _accessControlData.accessControl.user) { | ||
revert PermitAccessModule_InvalidSignature(); | ||
} | ||
|
||
_hasAccess = true; | ||
} | ||
} |
71 changes: 71 additions & 0 deletions
71
solidity/interfaces/modules/accessControl/IPermitAccessModule.sol
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,71 @@ | ||
// SPDX-License-Identifier: MIT | ||
pragma solidity ^0.8.19; | ||
|
||
import {IAccessControlModule} from | ||
'@defi-wonderland/prophet-core/solidity/interfaces/modules/accessControl/IAccessControlModule.sol'; | ||
|
||
/** | ||
* @title IPermitAccessModule | ||
*/ | ||
interface IPermitAccessModule is IAccessControlModule { | ||
/*/////////////////////////////////////////////////////////////// | ||
ERRORS | ||
//////////////////////////////////////////////////////////////*/ | ||
|
||
/** | ||
* @notice Thrown when the deadline is invalid | ||
*/ | ||
error PermitAccessModule_InvalidDeadline(); | ||
|
||
/** | ||
* @notice Thrown when the signature is invalid | ||
*/ | ||
error PermitAccessModule_InvalidSignature(); | ||
|
||
/*/////////////////////////////////////////////////////////////// | ||
STRUCTS | ||
//////////////////////////////////////////////////////////////*/ | ||
/** | ||
* @notice Decode permit parameters data | ||
* @param deadline The deadline timestamp | ||
* @param v The signature v | ||
* @param r The signature r | ||
* @param s The signature s | ||
*/ | ||
struct PermitParameters { | ||
uint256 deadline; | ||
uint8 v; | ||
bytes32 r; | ||
bytes32 s; | ||
} | ||
|
||
/*/////////////////////////////////////////////////////////////// | ||
VIEWS | ||
//////////////////////////////////////////////////////////////*/ | ||
|
||
/** | ||
* @notice Get the domain separator | ||
* @return _domainSeparator The domain separator | ||
*/ | ||
function getDomainSeparator() external view returns (bytes32 _domainSeparator); | ||
|
||
/** | ||
* @notice Decode permit parameters data | ||
* @param _permitData The permit data to decode | ||
*/ | ||
function decodePermitParametersData(bytes memory _data) external pure returns (PermitParameters memory _permitData); | ||
|
||
/** | ||
* @notice Decode access control data | ||
* @param _data The data to decode | ||
* @return _accessControlData The access control data | ||
*/ | ||
function decodeAccessControlData(bytes memory _data) | ||
external | ||
pure | ||
returns (IAccessControlModule.AccessControlParameters memory _accessControlData); | ||
|
||
/*/////////////////////////////////////////////////////////////// | ||
LOGIC | ||
//////////////////////////////////////////////////////////////*/ | ||
} |
144 changes: 144 additions & 0 deletions
144
solidity/test/unit/modules/accessControl/PermitAccessModule.t.sol
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,144 @@ | ||
// SPDX-License-Identifier: MIT | ||
pragma solidity ^0.8.19; | ||
|
||
import 'forge-std/Test.sol'; | ||
|
||
import {Helpers} from '../../../utils/Helpers.sol'; | ||
|
||
import {IModule} from '@defi-wonderland/prophet-core/solidity/interfaces/IModule.sol'; | ||
import {IOracle} from '@defi-wonderland/prophet-core/solidity/interfaces/IOracle.sol'; | ||
import {IERC20} from '@openzeppelin/contracts/token/ERC20/IERC20.sol'; | ||
|
||
import { | ||
IAccessControlModule, | ||
IPermitAccessModule, | ||
PermitAccessModule | ||
} from '../../../../contracts/modules/accessControl/PermitAccessModule.sol'; | ||
|
||
import {IAccessController} from '@defi-wonderland/prophet-core/solidity/interfaces/IAccessController.sol'; | ||
|
||
/** | ||
* @title Access Control Module Unit tests | ||
*/ | ||
contract BaseTest is Test, Helpers { | ||
// Mock Oracle | ||
IOracle public oracle; | ||
|
||
// Access Control Module | ||
PermitAccessModule public permitAccessModule; | ||
|
||
/** | ||
* @notice Deploy the target and mock oracle | ||
*/ | ||
function setUp() public { | ||
oracle = IOracle(makeAddr('Oracle')); | ||
vm.etch(address(oracle), hex'069420'); | ||
|
||
permitAccessModule = new PermitAccessModule(oracle); | ||
} | ||
} | ||
|
||
contract AccessControlModule_Unit_Constructor is BaseTest { | ||
/** | ||
* @notice Test the constructor | ||
*/ | ||
function test_constructor() public view { | ||
// Assert the oracle address | ||
assertEq(address(permitAccessModule.ORACLE()), address(oracle)); | ||
} | ||
} | ||
|
||
contract AccessControlModule_Unit_DecodeAccessControlData is BaseTest { | ||
/** | ||
* @notice Test the decodeAccessControlData function | ||
*/ | ||
function test_decodeAccessControlData() public view { | ||
// Encode the access control data | ||
IAccessControlModule.AccessControlParameters memory _accessControlData = IAccessControlModule | ||
.AccessControlParameters({ | ||
sender: address(0x1), | ||
typehash: hex'1234', | ||
accessControl: IAccessController.AccessControl({user: address(0x2), data: hex'5678'}), | ||
params: hex'9abc' | ||
}); | ||
|
||
// Decode the access control data | ||
IAccessControlModule.AccessControlParameters memory _decodedAccessControlData = | ||
permitAccessModule.decodeAccessControlData(abi.encode(_accessControlData)); | ||
|
||
// Assert the decoded access control data | ||
assertEq(_decodedAccessControlData.sender, _accessControlData.sender); | ||
assertEq(_decodedAccessControlData.typehash, _accessControlData.typehash); | ||
assertEq(_decodedAccessControlData.accessControl.user, _accessControlData.accessControl.user); | ||
assertEq(_decodedAccessControlData.accessControl.data, _accessControlData.accessControl.data); | ||
assertEq(_decodedAccessControlData.params, _accessControlData.params); | ||
} | ||
} | ||
|
||
contract AccessControlModule_Unit_DecodePermitParametersData is BaseTest { | ||
/** | ||
* @notice Test the decodePermitParametersData function | ||
*/ | ||
function test_decodePermitParametersData() public view { | ||
// Encode the permit parameters data | ||
IPermitAccessModule.PermitParameters memory _permitData = | ||
IPermitAccessModule.PermitParameters({deadline: 1, v: 2, r: hex'1234', s: hex'5678'}); | ||
|
||
// Decode the permit parameters data | ||
IPermitAccessModule.PermitParameters memory _decodedPermitData = | ||
permitAccessModule.decodePermitParametersData(abi.encode(_permitData)); | ||
|
||
// Assert the decoded permit parameters data | ||
assertEq(_decodedPermitData.deadline, _permitData.deadline); | ||
assertEq(_decodedPermitData.v, _permitData.v); | ||
assertEq(_decodedPermitData.r, _permitData.r); | ||
assertEq(_decodedPermitData.s, _permitData.s); | ||
} | ||
} | ||
|
||
contract AccessControlModule_Unit_ModuleName is BaseTest { | ||
/** | ||
* @notice Test the moduleName function | ||
*/ | ||
function test_ModuleName() public view { | ||
// Assert the module name | ||
assertEq(permitAccessModule.moduleName(), 'PermitAccessModule'); | ||
} | ||
} | ||
|
||
contract AccessControlModule_Unit_HasAccess is BaseTest { | ||
/** | ||
* @notice Test the hasAccess function | ||
*/ | ||
function test_hasAccess_true() public { | ||
bytes32 _permitHash = keccak256( | ||
abi.encodePacked( | ||
'\x19\x01', | ||
permitAccessModule.getDomainSeparator(), | ||
keccak256(abi.encode(keccak256('1234'), address(0x2), address(0x1), permitAccessModule.nonces(address(0x2)), 1)) | ||
) | ||
); | ||
|
||
(uint8 _v, bytes32 _r, bytes32 _s) = vm.sign(_permitHash); | ||
|
||
// Encode the permit parameters data | ||
IPermitAccessModule.PermitParameters memory _permitData = | ||
IPermitAccessModule.PermitParameters({deadline: 1, v: _v, r: _r, s: _s}); | ||
|
||
bytes memory _permitDataEncoded = abi.encode(_permitData); | ||
|
||
// Encode the access control data | ||
IAccessControlModule.AccessControlParameters memory _accessControlData = IAccessControlModule | ||
.AccessControlParameters({ | ||
sender: address(0x1), | ||
typehash: keccak256('1234'), | ||
accessControl: IAccessController.AccessControl({user: address(0x2), data: _permitDataEncoded}), | ||
params: hex'9abc' | ||
}); | ||
|
||
bytes memory _accessControlDataEncoded = abi.encode(_accessControlData); | ||
|
||
// Assert the has access | ||
assertEq(permitAccessModule.hasAccess(_accessControlDataEncoded), true); | ||
} | ||
} |
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