Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion buidler.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ usePlugin("buidler-gas-reporter");
export default {
defaultNetwork: "buidlerevm",
solc: {
version: "0.6.10",
version: "0.8.0",
optimizer: {
enabled: true,
runs: 200
Expand Down
89 changes: 74 additions & 15 deletions contracts/ERC20Permit.sol
Original file line number Diff line number Diff line change
@@ -1,23 +1,20 @@
// SPDX-License-Identifier: GPL-3.0-or-later
// Adapted from https://github.com/OpenZeppelin/openzeppelin-contracts/blob/53516bc555a454862470e7860a9b5254db4d00f5/contracts/token/ERC20/ERC20Permit.sol
pragma solidity ^0.6.0;
pragma solidity ^0.8.0;

import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import "./IERC2612.sol";
import "acc-erc20/contracts/ERC20.sol";

/**
* @author Georgios Konstantopoulos
* @dev Extension of {ERC20} that allows token holders to use their tokens
* without sending any transactions by setting {IERC20-allowance} with a
* signature using the {permit} method, and then spend them via
* {IERC20-transferFrom}.
* @author Alberto Cuesta Cañada
* @dev Extension of {ERC20} that allows token holders set allowances or approve a single `transferFrom` using off-chain signatures.
*
* The {permit} signature mechanism conforms to the {IERC2612} interface.
* The mechanisms don't conform to the {IERC2612} interface.
*/
abstract contract ERC20Permit is ERC20, IERC2612 {
mapping (address => uint256) public override nonces;
abstract contract ERC20Permit is ERC20 {
mapping (address => uint256) public nonces;

bytes32 public immutable PERMIT_TYPEHASH = keccak256("Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)");
bytes32 public immutable TRANSFERFROM_TYPEHASH = keccak256("TransferFrom(address sender,address recipient,uint256 value,uint256 nonce,uint256 deadline)");
bytes32 public immutable DOMAIN_SEPARATOR;

constructor(string memory name_, string memory symbol_) internal ERC20(name_, symbol_) {
Expand All @@ -38,12 +35,18 @@ abstract contract ERC20Permit is ERC20, IERC2612 {
}

/**
* @dev See {IERC2612-permit}.
* @dev Similar to {IERC2612-permit}, but with a packed signature.
*
* In cases where the free option is not a concern, deadline can simply be
* set to uint(-1), so it should be seen as an optional parameter
* set to uint(-1), so it should be seen as an optional parameter.
*/
function permit(address owner, address spender, uint256 amount, uint256 deadline, uint8 v, bytes32 r, bytes32 s) public virtual override {
function permit(
address owner,
address spender,
uint256 amount,
uint256 deadline,
bytes memory signature
) public virtual {
require(deadline >= block.timestamp, "ERC20Permit: expired deadline");

bytes32 hashStruct = keccak256(
Expand All @@ -65,12 +68,68 @@ abstract contract ERC20Permit is ERC20, IERC2612 {
)
);

(bytes32 r, bytes32 s, uint8 v) = unpack(signature);
address signer = ecrecover(hash, v, r, s);
require(
signer != address(0) && signer == owner,
"ERC20Permit: invalid signature"
"ERC20Permit::permit: invalid signature"
);

_approve(owner, spender, amount);
}

/**
* @dev Similar to {IERC20-transferFrom}, but with a packed signature. It doesn't check or change allowances.
*
* In cases where the free option is not a concern, deadline can simply be
* set to uint(-1), so it should be seen as an optional parameter.
*/
function transferFromWithSignature(
address sender,
address recipient,
uint256 amount,
uint256 deadline,
bytes memory signature
) public virtual returns(bool) {
require(deadline >= block.timestamp, "ERC20Permit: expired deadline");

bytes32 hashStruct = keccak256(
abi.encode(
TRANSFERFROM_TYPEHASH,
sender,
recipient,
amount,
nonces[sender]++,
deadline
)
);

bytes32 hash = keccak256(
abi.encodePacked(
'\x19\x01',
DOMAIN_SEPARATOR,
hashStruct
)
);

(bytes32 r, bytes32 s, uint8 v) = unpack(signature);
address signer = ecrecover(hash, v, r, s);
require(
signer != address(0) && signer == sender,
"ERC20Permit::transferFrom: invalid signature"
);

_transfer(sender, recipient, amount);
return true;
}

/// @dev Unpack r, s and v from a `bytes` signature.
/// @param signature A packed signature.
function unpack(bytes memory signature) internal pure returns (bytes32 r, bytes32 s, uint8 v) {
assembly {
r := mload(add(signature, 0x20))
s := mload(add(signature, 0x40))
v := byte(0, mload(add(signature, 0x60)))
}
}
}
2 changes: 1 addition & 1 deletion contracts/IERC2612.sol
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-3.0-or-later
// Code adapted from https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2237/
pragma solidity ^0.6.0;
pragma solidity ^0.8.0;

/**
* @dev Interface of the ERC2612 standard as defined in the EIP.
Expand Down
25 changes: 0 additions & 25 deletions contracts/Migrations.sol

This file was deleted.

2 changes: 1 addition & 1 deletion contracts/TestERC20.sol
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.6.10;
pragma solidity ^0.8.0;

import "./ERC20Permit.sol";

Expand Down
6 changes: 3 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "erc20permit",
"version": "0.0.4",
"version": "0.0.5",
"description": "Solidity contracts implementing ERC2612",
"author": "Alberto Cuesta Cañada",
"engines": {
Expand Down Expand Up @@ -30,16 +30,16 @@
"lint:sol": "solhint -f table contracts/**/*.sol"
},
"dependencies": {
"@openzeppelin/contracts": "3.1.0"
"acc-erc20": "^0.5.1"
},
"devDependencies": {
"@nomiclabs/buidler": "^1.3.8",
"@nomiclabs/buidler-truffle5": "^1.3.4",
"@nomiclabs/buidler-web3": "^1.3.4",
"@openzeppelin/contracts": "3.1.0",
"@openzeppelin/test-helpers": "^0.5.6",
"@truffle/hdwallet-provider": "^1.0.40",
"@types/mocha": "^8.0.0",
"acc-erc20": "^0.5.1",
"buidler-gas-reporter": "0.1.4-beta.4",
"chai": "4.2.0",
"ethereumjs-util": "^7.0.3",
Expand Down
10 changes: 5 additions & 5 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -639,11 +639,6 @@
fs-extra "^8.1.0"
try-require "^1.2.1"

"@openzeppelin/contracts@3.1.0":
version "3.1.0"
resolved "https://registry.yarnpkg.com/@openzeppelin/contracts/-/contracts-3.1.0.tgz#bcea457ef89069fbe5a617f50b25b6a8272895d5"
integrity sha512-dVXDnUKxrAKLzPdCRkz+N8qsVkK1XxJ6kk3zuI6zaQmcKxN7CkizoDP7lXxcs/Mi2I0mxceTRjJBqlzFffLJrQ==

"@openzeppelin/test-helpers@^0.5.6":
version "0.5.6"
resolved "https://registry.yarnpkg.com/@openzeppelin/test-helpers/-/test-helpers-0.5.6.tgz#cafa3fdb741be9e3ff525916257d6cdce45ed86a"
Expand Down Expand Up @@ -1115,6 +1110,11 @@ abstract-leveldown@~2.7.1:
dependencies:
xtend "~4.0.0"

acc-erc20@^0.5.0:
version "0.5.0"
resolved "https://registry.yarnpkg.com/acc-erc20/-/acc-erc20-0.5.0.tgz#01aef88ba75b5401998925179eae586da34d8b8e"
integrity sha512-+LpOR2SvvNtoTVBxw0VuRIEkiS7nibe1mBmhNb/x8k0YhRK/Y7XYe+sKjnf33sjIlbyu8Xj5V8rj1p3sz/zHEA==

accepts@~1.3.7:
version "1.3.7"
resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.7.tgz#531bc726517a3b2b41f850021c6cc15eaab507cd"
Expand Down