Make MiniMe implement ERC2612

Fixes vacp2p/minime#5
This commit is contained in:
Ricardo Guilherme Schmidt
2023-09-26 01:15:07 -03:00
committed by r4bbit
parent 38d62c5517
commit a522ebb09d
4 changed files with 222 additions and 40 deletions

View File

@@ -1,30 +1,29 @@
| contracts/MiniMeToken.sol:MiniMeToken contract | | | | | |
|------------------------------------------------|-----------------|-------|--------|-------|---------|
| Deployment Cost | Deployment Size | | | | |
| 1651926 | 9241 | | | | |
| 2149485 | 12251 | | | | |
| Function Name | min | avg | median | max | # calls |
| allowance | 0 | 60 | 0 | 786 | 13 |
| approve | 0 | 15018 | 14715 | 31708 | 10 |
| approveAndCall | 0 | 31204 | 0 | 93613 | 3 |
| allowance | 0 | 62 | 0 | 808 | 13 |
| approve | 0 | 15064 | 14767 | 31771 | 10 |
| approveAndCall | 0 | 31232 | 0 | 93698 | 3 |
| balanceOf | 0 | 550 | 0 | 2731 | 63 |
| balanceOfAt | 0 | 905 | 0 | 4027 | 78 |
| changeController | 0 | 593 | 0 | 3558 | 6 |
| claimTokens | 9582 | 41342 | 57222 | 57222 | 3 |
| claimTokens | 9537 | 41267 | 57132 | 57132 | 3 |
| controller | 0 | 0 | 0 | 0 | 9 |
| decimals | 0 | 0 | 0 | 0 | 9 |
| destroyTokens | 2286 | 5184 | 4288 | 8979 | 3 |
| destroyTokens | 2286 | 5185 | 4288 | 8983 | 3 |
| enableTransfers | 0 | 0 | 0 | 0 | 3 |
| generateTokens | 0 | 14007 | 0 | 95829 | 62 |
| generateTokens | 0 | 14015 | 0 | 95885 | 62 |
| name | 0 | 0 | 0 | 0 | 9 |
| parentSnapShotBlock | 0 | 0 | 0 | 0 | 10 |
| parentToken | 0 | 0 | 0 | 0 | 10 |
| receive | 7960 | 7979 | 7979 | 7998 | 2 |
| symbol | 0 | 0 | 0 | 0 | 9 |
| totalSupply | 0 | 286 | 0 | 2480 | 22 |
| totalSupplyAt | 0 | 866 | 0 | 3637 | 17 |
| transfer | 571 | 39327 | 50440 | 93129 | 20 |
| transferFrom | 0 | 16831 | 3495 | 66552 | 7 |
| totalSupplyAt | 0 | 873 | 0 | 3659 | 17 |
| transfer | 526 | 39283 | 50395 | 93092 | 20 |
| transferFrom | 0 | 16834 | 3495 | 66574 | 7 |

View File

@@ -1,45 +1,123 @@
<<<<<<< HEAD
<<<<<<< HEAD
AllowanceTest:testAllowance() (gas: 42712)
AllowanceTest:testAllowanceAlreadySet() (gas: 36809)
AllowanceTest:testAllowanceReset() (gas: 45925)
AllowanceTest:testApproveAndCall() (gas: 98532)
AllowanceTest:testApproveTransferDisabled() (gas: 7968)
||||||| parent of fd986cf (add missing return on approve)
AllowanceTest:testAllowance() (gas: 42712)
AllowanceTest:testAllowanceAlreadySet() (gas: 36809)
AllowanceTest:testAllowanceReset() (gas: 45925)
AllowanceTest:testApproveAndCall() (gas: 98532)
AllowanceTest:testApproveTransferDisabled() (gas: 7968)
=======
AllowanceTest:testAllowance() (gas: 42775)
AllowanceTest:testAllowanceAlreadySet() (gas: 36905)
AllowanceTest:testAllowanceReset() (gas: 46071)
AllowanceTest:testApproveAndCall() (gas: 98617)
AllowanceTest:testApproveTransferDisabled() (gas: 7994)
>>>>>>> fd986cf (add missing return on approve)
AllowanceTest:testDeployment() (gas: 26711)
AllowanceTest:testNoAllowance() (gas: 9462)
AllowanceTest:testRejectedApproval() (gas: 13643)
ClaimTokensTest:testClaimERC20() (gas: 63808)
ClaimTokensTest:testClaimETH() (gas: 13682)
ClaimTokensTest:testClaimSelf() (gas: 61290)
AllowanceTest:testRejectedApproval() (gas: 13683)
ClaimTokensTest:testClaimERC20() (gas: 63718)
ClaimTokensTest:testClaimETH() (gas: 13637)
ClaimTokensTest:testClaimSelf() (gas: 61200)
ClaimTokensTest:testDeployment() (gas: 26595)
CreateCloneTokenTest:testCloneFutureSnapshot() (gas: 101370)
CreateCloneTokenTest:testCreateCloneToken() (gas: 1692517)
CreateCloneTokenTest:testCloneFutureSnapshot() (gas: 101288)
CreateCloneTokenTest:testCreateCloneToken() (gas: 2190766)
CreateCloneTokenTest:testDeployment() (gas: 26550)
CreateCloneTokenTest:testGenerateTokens() (gas: 102115)
CreateCloneTokenTest:testGenerateTokens() (gas: 102171)
DestroyTokensTest:testDeployment() (gas: 26595)
DestroyTokensTest:testDestroyTokens() (gas: 13479)
DestroyTokensTest:testDestroyTokens() (gas: 13483)
DestroyTokensTest:testDestroyTokensNotEnoughBalance() (gas: 9644)
DestroyTokensTest:testDestroyTokensNotEnoughSupply() (gas: 7975)
GenerateTokensTest:testDeployment() (gas: 26550)
GenerateTokensTest:testGenerateTokens() (gas: 109561)
GenerateTokensTest:testGenerateTokens() (gas: 109587)
GenerateTokensTest:testGenerateTokensSupplyOverflow() (gas: 3126)
GenerateTokensTest:test_RevertWhen_SenderIsNotController() (gas: 14994)
GenerateTokensTest:test_RevertWhen_SenderIsNotController() (gas: 15016)
MiniMeTokenTest:testDeployment() (gas: 26535)
ReceiveTest:testAcceptingEther() (gas: 18628)
ReceiveTest:testDeployment() (gas: 26595)
ReceiveTest:testRejectingEther() (gas: 18691)
ReentrancyTest:testAttack() (gas: 229327)
ReentrancyTest:testAttack() (gas: 229445)
TestPermit:testDeployment() (gas: 26573)
TestPermit:testInvalidAllowance() (gas: 84344)
TestPermit:testInvalidBalance() (gas: 67425)
TestPermit:testPermit() (gas: 84547)
TestPermit:testRevertExpiredPermit() (gas: 27878)
TestPermit:testRevertInvalidNonce() (gas: 57309)
TestPermit:testRevertInvalidSigner() (gas: 56070)
TestPermit:testRevertSignatureReplay() (gas: 96596)
TestPermit:testTransferFromLimitedPermit() (gas: 216258)
TestPermit:testTransferFromMaxPermit() (gas: 236197)
TestSnapshotReads:testDeployment() (gas: 26550)
<<<<<<< HEAD
TestSnapshotReads:testSnapshotReads() (gas: 755896)
||||||| parent of fd986cf (add missing return on approve)
TestSnapshotReads:testSnapshotReads() (gas: 755896)
TestPermit:testDeployment() (gas: 45858)
TestPermit:testInvalidAllowance() (gas: 84397)
TestPermit:testInvalidBalance() (gas: 67478)
TestPermit:testPermit() (gas: 84644)
TestPermit:testRevertExpiredPermit() (gas: 27953)
TestPermit:testRevertInvalidNonce() (gas: 57362)
TestPermit:testRevertInvalidSigner() (gas: 56145)
TestPermit:testRevertSignatureReplay() (gas: 96746)
TestPermit:testTransferFromLimitedPermit() (gas: 216308)
TestPermit:testTransferFromMaxPermit() (gas: 236247)
=======
TestSnapshotReads:testSnapshotReads() (gas: 756322)
>>>>>>> fd986cf (add missing return on approve)
TransferTest:testDeployment() (gas: 26617)
TransferTest:testDoubleTransfer() (gas: 92515)
TransferTest:testDoubleTransfer2() (gas: 70668)
TransferTest:testInvalidDestinationTransfer() (gas: 6447)
TransferTest:testInvalidDestinationTransfer2() (gas: 6444)
TransferTest:testMultipleTransferToSame() (gas: 114745)
TransferTest:testMultipleTransferToSame2() (gas: 92942)
TransferTest:testRejectedTransfer() (gas: 59966)
TransferTest:testTransfer() (gas: 81763)
TransferTest:testTransfer2() (gas: 59939)
TransferTest:testTransferControllerZero() (gas: 59886)
TransferTest:testTransferDisabled() (gas: 8013)
TransferTest:testDoubleTransfer() (gas: 92425)
TransferTest:testDoubleTransfer2() (gas: 70578)
TransferTest:testInvalidDestinationTransfer() (gas: 6402)
TransferTest:testInvalidDestinationTransfer2() (gas: 6399)
TransferTest:testMultipleTransferToSame() (gas: 114655)
TransferTest:testMultipleTransferToSame2() (gas: 92852)
TransferTest:testRejectedTransfer() (gas: 59921)
TransferTest:testTransfer() (gas: 81718)
TransferTest:testTransfer2() (gas: 59894)
TransferTest:testTransferControllerZero() (gas: 59841)
TransferTest:testTransferDisabled() (gas: 7968)
TransferTest:testTransferFromDisabled() (gas: 6526)
TransferTest:testTransferNoBalance() (gas: 16972)
<<<<<<< HEAD
TransferTest:testTransferNoBalance() (gas: 16972)
||||||| parent of d658aa3 (Make MiniMe implement ERC2612)
AllowanceTest:testAllowance() (gas: 244303)
AllowanceTest:testDeployment() (gas: 45814)
CreateCloneTokenTest:testCreateCloneToken() (gas: 2182389)
CreateCloneTokenTest:testDeployment() (gas: 45769)
CreateCloneTokenTest:testGenerateTokens() (gas: 2061699)
DestroyTokensTest:testDeployment() (gas: 45598)
DestroyTokensTest:testDestroyTokens() (gas: 124942)
GenerateTokensTest:testDeployment() (gas: 45553)
GenerateTokensTest:testGenerateTokens() (gas: 114621)
GenerateTokensTest:test_RevertWhen_SenderIsNotController() (gas: 14930)
MiniMeTokenTest:testDeployment() (gas: 45598)
ReentrancyTest:testAttack() (gas: 229394)
TransferTest:testDeployment() (gas: 45814)
TransferTest:testTransfer() (gas: 201281)
=======
AllowanceTest:testAllowance() (gas: 244406)
AllowanceTest:testDeployment() (gas: 45880)
CreateCloneTokenTest:testCreateCloneToken() (gas: 2681910)
CreateCloneTokenTest:testDeployment() (gas: 45835)
CreateCloneTokenTest:testGenerateTokens() (gas: 2561311)
DestroyTokensTest:testDeployment() (gas: 45664)
DestroyTokensTest:testDestroyTokens() (gas: 124942)
GenerateTokensTest:testDeployment() (gas: 45619)
GenerateTokensTest:testGenerateTokens() (gas: 114621)
GenerateTokensTest:test_RevertWhen_SenderIsNotController() (gas: 14930)
MiniMeTokenTest:testDeployment() (gas: 45664)
ReentrancyTest:testAttack() (gas: 229482)
TransferTest:testDeployment() (gas: 45880)
TransferTest:testTransfer() (gas: 201303)
>>>>>>> d658aa3 (Make MiniMe implement ERC2612)
||||||| parent of fd986cf (add missing return on approve)
TransferTest:testTransferNoBalance() (gas: 16972)
=======
TransferTest:testTransferNoBalance() (gas: 16927)
>>>>>>> fd986cf (add missing return on approve)

View File

@@ -12,6 +12,8 @@ error Overflow();
error AllowanceAlreadySet();
error OperationFailed();
error ControllerNotSet();
error ERC2612ExpiredSignature(uint256 deadline);
error ERC2612InvalidSigner(address signer, address owner);
/*
Copyright 2016, Jordi Baylina
@@ -33,6 +35,10 @@ import { Controlled } from "./Controlled.sol";
import { TokenController } from "./TokenController.sol";
import { ApproveAndCallFallBack } from "./ApproveAndCallFallBack.sol";
import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import { IERC20Permit } from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Permit.sol";
import { EIP712 } from "@openzeppelin/contracts/utils/cryptography/EIP712.sol";
import { ECDSA } from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol";
import { Nonces } from "./Nonces.sol";
/// @title MiniMeBase Contract
/// @author Jordi Baylina
@@ -40,11 +46,13 @@ import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
/// token using the token distribution at a given block, this will allow DAO's
/// and DApps to upgrade their features in a decentralized manner without
/// affecting the original token
abstract contract MiniMeBase is Controlled, IERC20 {
abstract contract MiniMeBase is Controlled, IERC20, IERC20Permit, EIP712, Nonces {
string public name; //The Token's name: e.g. DigixDAO Tokens
uint8 public immutable decimals; //Number of decimals of the smallest unit
string public symbol; //An identifier: e.g. REP
string public constant TOKEN_VERSION = "MMT_0.2"; //An arbitrary versioning scheme
bytes32 private constant PERMIT_TYPEHASH =
keccak256("Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)");
/// @dev `Checkpoint` is the structure that attaches a block number to a
/// given value, the block number attached is the one that last changed the
@@ -98,7 +106,9 @@ abstract contract MiniMeBase is Controlled, IERC20 {
uint8 _decimalUnits,
string memory _tokenSymbol,
bool _transfersEnabled
) {
)
EIP712(_tokenName, TOKEN_VERSION)
{
name = _tokenName; // Set the name
decimals = _decimalUnits; // Set the decimals
symbol = _tokenSymbol; // Set the symbol
@@ -200,23 +210,58 @@ abstract contract MiniMeBase is Controlled, IERC20 {
/// @param _amount The amount of tokens to be approved for transfer
/// @return success True if the approval was successful
function approve(address _spender, uint256 _amount) public returns (bool success) {
return doApprove(msg.sender, _spender, _amount);
}
/**
* @inheritdoc IERC20Permit
*/
function permit(
address owner,
address spender,
uint256 value,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
)
public
virtual
{
if (block.timestamp > deadline) {
revert ERC2612ExpiredSignature(deadline);
}
bytes32 structHash = keccak256(abi.encode(PERMIT_TYPEHASH, owner, spender, value, _useNonce(owner), deadline));
bytes32 hash = _hashTypedDataV4(structHash);
address signer = ECDSA.recover(hash, v, r, s);
if (signer != owner) {
revert ERC2612InvalidSigner(signer, owner);
}
doApprove(owner, spender, value);
}
function doApprove(address _owner, address _spender, uint256 _amount) internal returns (bool) {
if (!transfersEnabled) revert TransfersDisabled();
// To change the approve amount you first have to reduce the addresses`
// allowance to zero by calling `approve(_spender,0)` if it is not
// already 0 to mitigate the race condition described here:
// https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
if ((_amount != 0) && (allowed[msg.sender][_spender] != 0)) revert AllowanceAlreadySet();
if ((_amount != 0) && (allowed[_owner][_spender] != 0)) revert AllowanceAlreadySet();
// Alerts the token controller of the approve function call
if (isContract(controller)) {
if (!TokenController(controller).onApprove(msg.sender, _spender, _amount)) {
if (!TokenController(controller).onApprove(_owner, _spender, _amount)) {
revert ControllerRejected();
}
}
allowed[msg.sender][_spender] = _amount;
emit Approval(msg.sender, _spender, _amount);
allowed[_owner][_spender] = _amount;
emit Approval(_owner, _spender, _amount);
return true;
}
@@ -300,6 +345,21 @@ abstract contract MiniMeBase is Controlled, IERC20 {
}
}
///
/// @inheritdoc IERC20Permit
///
function nonces(address owner) public view virtual override(IERC20Permit, Nonces) returns (uint256) {
return super.nonces(owner);
}
///
/// @inheritdoc IERC20Permit
///
// solhint-disable-next-line func-name-mixedcase
function DOMAIN_SEPARATOR() external view virtual returns (bytes32) {
return _domainSeparatorV4();
}
////////////////
// Generate and destroy tokens
////////////////

45
contracts/Nonces.sol Normal file
View File

@@ -0,0 +1,45 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
/**
* @dev Provides tracking nonces for addresses. Nonces will only increment.
*/
abstract contract Nonces {
/**
* @dev The nonce used for an `account` is not the expected current nonce.
*/
error InvalidAccountNonce(address account, uint256 currentNonce);
mapping(address account => uint256) private _nonces;
/**
* @dev Returns the next unused nonce for an address.
*/
function nonces(address owner) public view virtual returns (uint256) {
return _nonces[owner];
}
/**
* @dev Consumes a nonce.
*
* Returns the current value and increments nonce.
*/
function _useNonce(address owner) internal virtual returns (uint256) {
// 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);
}
}
}