-
Notifications
You must be signed in to change notification settings - Fork 129
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #236 from TokenySolutions/bt-235-permit
BT-235: ERC2612 - Add ERC-20 Permit function compatibility
- Loading branch information
Showing
7 changed files
with
412 additions
and
23 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
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,164 @@ | ||
// SPDX-License-Identifier: GPL-3.0 | ||
// | ||
// :+#####%%%%%%%%%%%%%%+ | ||
// .-*@@@%+.:+%@@@@@%%#***%@@%= | ||
// :=*%@@@#=. :#@@% *@@@%= | ||
// .-+*%@%*-.:+%@@@@@@+. -*+: .=#. :%@@@%- | ||
// :=*@@@@%%@@@@@@@@@%@@@- .=#@@@%@%= =@@@@#. | ||
// -=+#%@@%#*=:. :%@@@@%. -*@@#*@@@@@@@#=:- *@@@@+ | ||
// =@@%=:. :=: *@@@@@%#- =%*%@@@@#+-. =+ :%@@@%- | ||
// -@@%. .+@@@ =+=-. @@#- +@@@%- =@@@@%: | ||
// :@@@. .+@@#%: : .=*=-::.-%@@@+*@@= +@@@@#. | ||
// %@@: +@%%* =%@@@@@@@@@@@#. .*@%- +@@@@*. | ||
// #@@= .+@@@@%:=*@@@@@- :%@%: .*@@@@+ | ||
// *@@* +@@@#-@@%-:%@@* +@@#. :%@@@@- | ||
// -@@% .:-=++*##%%%@@@@@@@@@@@@*. :@+.@@@%: .#@@+ =@@@@#: | ||
// .@@@*-+*#%%%@@@@@@@@@@@@@@@@%%#**@@%@@@. *@=*@@# :#@%= .#@@@@#- | ||
// -%@@@@@@@@@@@@@@@*+==-:-@@@= *@# .#@*-=*@@@@%= -%@@@* =@@@@@%- | ||
// -+%@@@#. %@%%= -@@:+@: -@@* *@@*-:: -%@@%=. .*@@@@@# | ||
// *@@@* +@* *@@##@@- #@*@@+ -@@= . :+@@@#: .-+@@@%+- | ||
// +@@@%*@@:..=@@@@* .@@@* .#@#. .=+- .=%@@@*. :+#@@@@*=: | ||
// =@@@@%@@@@@@@@@@@@@@@@@@@@@@%- :+#*. :*@@@%=. .=#@@@@%+: | ||
// .%@@= ..... .=#@@+. .#@@@*: -*%@@@@%+. | ||
// +@@#+===---:::... .=%@@*- +@@@+. -*@@@@@%+. | ||
// -@@@@@@@@@@@@@@@@@@@@@@%@@@@= -@@@+ -#@@@@@#=. | ||
// ..:::---===+++***###%%%@@@#- .#@@+ -*@@@@@#=. | ||
// @@@@@@+. +@@*. .+@@@@@%=. | ||
// -@@@@@= =@@%: -#@@@@%+. | ||
// +@@@@@. =@@@= .+@@@@@*: | ||
// #@@@@#:%@@#. :*@@@@#- | ||
// @@@@@%@@@= :#@@@@+. | ||
// :@@@@@@@#.:#@@@%- | ||
// +@@@@@@-.*@@@*: | ||
// #@@@@#.=@@@+. | ||
// @@@@+-%@%= | ||
// :@@@#%@%= | ||
// +@@@@%- | ||
// :#%%= | ||
// | ||
|
||
/** | ||
* NOTICE | ||
* | ||
* The T-REX software is licensed under a proprietary license or the GPL v.3. | ||
* If you choose to receive it under the GPL v.3 license, the following applies: | ||
* T-REX is a suite of smart contracts implementing the ERC-3643 standard and | ||
* developed by Tokeny to manage and transfer financial assets on EVM blockchains | ||
* | ||
* Copyright (C) 2023, Tokeny sàrl. | ||
* | ||
* This program is free software: you can redistribute it and/or modify | ||
* it under the terms of the GNU General Public License as published by | ||
* the Free Software Foundation, either version 3 of the License, or | ||
* (at your option) any later version. | ||
* | ||
* This program is distributed in the hope that it will be useful, | ||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
* GNU General Public License for more details. | ||
* | ||
* You should have received a copy of the GNU General Public License | ||
* along with this program. If not, see <https://www.gnu.org/licenses/>. | ||
*/ | ||
|
||
pragma solidity 0.8.27; | ||
|
||
import { IERC20Permit } from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Permit.sol"; | ||
import { ECDSA } from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol"; | ||
import { IERC5267 } from "@openzeppelin/contracts/interfaces/IERC5267.sol"; | ||
import { NoncesUpgradeable } from "../utils/NoncesUpgradeable.sol"; | ||
|
||
error ERC2612ExpiredSignature(uint256 deadline); | ||
error ERC2612InvalidSigner(address signer, address owner); | ||
|
||
|
||
abstract contract TokenPermit is IERC20Permit, IERC5267, NoncesUpgradeable { | ||
|
||
bytes32 private constant _PERMIT_TYPEHASH = | ||
keccak256("Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)"); | ||
|
||
bytes32 private constant _TYPE_HASH = | ||
keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"); | ||
|
||
/// @inheritdoc IERC20Permit | ||
function permit( | ||
address owner, | ||
address spender, | ||
uint256 value, | ||
uint256 deadline, | ||
uint8 v, | ||
bytes32 r, | ||
bytes32 s | ||
) external { | ||
require(block.timestamp <= deadline, ERC2612ExpiredSignature(deadline)); | ||
|
||
bytes32 structHash = keccak256(abi.encode(_PERMIT_TYPEHASH, owner, spender, value, _useNonce(owner), deadline)); | ||
|
||
bytes32 hash = ECDSA.toTypedDataHash(_domainSeparatorV4(), structHash); | ||
|
||
address signer = ECDSA.recover(hash, v, r, s); | ||
require(signer == owner, ERC2612InvalidSigner(signer, owner)); | ||
|
||
_approve(owner, spender, value); | ||
} | ||
|
||
/// @inheritdoc IERC20Permit | ||
// solhint-disable-next-line func-name-mixedcase | ||
function DOMAIN_SEPARATOR() external view returns (bytes32) { | ||
return _domainSeparatorV4(); | ||
} | ||
|
||
/// @inheritdoc IERC5267 | ||
function eip712Domain() | ||
external | ||
view | ||
virtual | ||
returns ( | ||
bytes1 _fields, | ||
string memory _name, | ||
string memory _version, | ||
uint256 _chainId, | ||
address _verifyingContract, | ||
bytes32 _salt, | ||
uint256[] memory _extensions | ||
) | ||
{ | ||
return ( | ||
hex"0f", // 01111 | ||
name(), | ||
version(), | ||
block.chainid, | ||
address(this), | ||
bytes32(0), | ||
new uint256[](0) | ||
); | ||
} | ||
|
||
/// @inheritdoc IERC20Permit | ||
function nonces(address owner) public view override(IERC20Permit, NoncesUpgradeable) returns (uint256) { | ||
return super.nonces(owner); | ||
} | ||
|
||
/// @dev Implemented in Token.sol | ||
function name() public virtual view returns (string memory); | ||
|
||
/// @dev Implemented in Token.sol | ||
function version() public virtual view returns (string memory); | ||
|
||
/// @dev Implemented in Token.sol | ||
function _approve(address _owner, address _spender, uint256 _value) internal virtual; | ||
|
||
// @dev Returns the domain separator for the current chain. | ||
function _domainSeparatorV4() internal view returns (bytes32) { | ||
return keccak256( | ||
abi.encode( | ||
_TYPE_HASH, | ||
keccak256(bytes(name())), | ||
keccak256(bytes(version())), | ||
block.chainid, | ||
address(this) | ||
) | ||
); | ||
} | ||
|
||
} |
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,58 @@ | ||
// SPDX-License-Identifier: MIT | ||
// OpenZeppelin Contracts (last updated v5.0.0) (utils/Nonces.sol) | ||
pragma solidity 0.8.27; | ||
|
||
error InvalidAccountNonce(address account, uint256 currentNonce); | ||
|
||
/** | ||
* @dev Provides tracking nonces for addresses. Nonces will only increment. | ||
*/ | ||
abstract contract NoncesUpgradeable { | ||
|
||
/// @custom:storage-location erc7201:openzeppelin.storage.Nonces | ||
struct NoncesStorage { | ||
mapping(address account => uint256) _nonces; | ||
} | ||
|
||
// keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.Nonces")) - 1)) & ~bytes32(uint256(0xff)) | ||
bytes32 private constant _NONCES_STORAGE_LOCATION = 0x5ab42ced628888259c08ac98db1eb0cf702fc1501344311d8b100cd1bfe4bb00; | ||
|
||
/** | ||
* @dev Returns the next unused nonce for an address. | ||
*/ | ||
function nonces(address owner) public view virtual returns (uint256) { | ||
NoncesStorage storage $ = _getNoncesStorage(); | ||
return $._nonces[owner]; | ||
} | ||
|
||
/** | ||
* @dev Consumes a nonce. | ||
* | ||
* Returns the current value and increments nonce. | ||
*/ | ||
function _useNonce(address owner) internal virtual returns (uint256) { | ||
NoncesStorage storage $ = _getNoncesStorage(); | ||
// For each account, the nonce has an initial value of 0, can only be incremented by one, and cannot be | ||
// decremented or reset. This guarantees that the nonce never overflows. | ||
unchecked { | ||
// It is important to do x++ and not ++x here. | ||
return $._nonces[owner]++; | ||
} | ||
} | ||
|
||
/** | ||
* @dev Same as {_useNonce} but checking that `nonce` is the next valid for `owner`. | ||
*/ | ||
function _useCheckedNonce(address owner, uint256 nonce) internal virtual { | ||
uint256 current = _useNonce(owner); | ||
if (nonce != current) { | ||
revert InvalidAccountNonce(owner, current); | ||
} | ||
} | ||
|
||
function _getNoncesStorage() private pure returns (NoncesStorage storage $) { | ||
assembly { | ||
$.slot := _NONCES_STORAGE_LOCATION | ||
} | ||
} | ||
} |
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
Oops, something went wrong.