Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Add increase/decrease allowance functionality #20

Merged
merged 5 commits into from
Mar 9, 2022
Merged
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
6 changes: 3 additions & 3 deletions .gitmodules
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
[submodule "modules/ds-test"]
path = modules/ds-test
url = https://github.com/dapphub/ds-test
[submodule "modules/contract-test-utils"]
path = modules/contract-test-utils
url = https://github.com/maple-labs/contract-test-utils
10 changes: 10 additions & 0 deletions contracts/ERC20.sol
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,16 @@ contract ERC20 is IERC20 {
return true;
}

function decreaseAllowance(address spender_, uint256 subtractedAmount_) external override returns (bool success_) {
_approve(msg.sender, spender_, allowance[msg.sender][spender_] - subtractedAmount_);
return true;
}

function increaseAllowance(address spender_, uint256 addedAmount_) external override returns (bool success_) {
_approve(msg.sender, spender_, allowance[msg.sender][spender_] + addedAmount_);
return true;
}

function transfer(address recipient_, uint256 amount_) external override returns (bool success_) {
_transfer(msg.sender, recipient_, amount_);
return true;
Expand Down
10 changes: 10 additions & 0 deletions contracts/ERC20Permit.sol
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,16 @@ contract ERC20Permit is IERC20Permit {
return true;
}

function decreaseAllowance(address spender_, uint256 subtractedAmount_) external override returns (bool success_) {
_approve(msg.sender, spender_, allowance[msg.sender][spender_] - subtractedAmount_);
return true;
}

function increaseAllowance(address spender_, uint256 addedAmount_) external override returns (bool success_) {
_approve(msg.sender, spender_, allowance[msg.sender][spender_] + addedAmount_);
return true;
}

function permit(address owner, address spender, uint256 amount, uint256 deadline, uint8 v, bytes32 r, bytes32 s) external override {
require(deadline >= block.timestamp, "ERC20Permit:EXPIRED");
bytes32 digest = keccak256(
Expand Down
86 changes: 52 additions & 34 deletions contracts/interfaces/IERC20.sol
Original file line number Diff line number Diff line change
Expand Up @@ -5,80 +5,98 @@ pragma solidity ^0.8.7;
interface IERC20 {

/**
* @dev Emits an event indicating that tokens have moved from one account to another.
* @param owner_ Account that tokens have moved from.
* @param recipient_ Account that tokens have moved to.
* @param amount_ Amount of tokens that have been transferred.
* @dev Emits an event indicating that tokens have moved from one account to another.
* @param owner_ Account that tokens have moved from.
* @param recipient_ Account that tokens have moved to.
* @param amount_ Amount of tokens that have been transferred.
*/
event Transfer(address indexed owner_, address indexed recipient_, uint256 amount_);

/**
* @dev Emits an event indicating that one account has set the allowance of another account over their tokens.
* @param owner_ Account that tokens are approved from.
* @param spender_ Account that tokens are approved for.
* @param amount_ Amount of tokens that have been approved.
* @dev Emits an event indicating that one account has set the allowance of another account over their tokens.
* @param owner_ Account that tokens are approved from.
* @param spender_ Account that tokens are approved for.
* @param amount_ Amount of tokens that have been approved.
*/
event Approval(address indexed owner_, address indexed spender_, uint256 amount_);

/**
* @dev Returns the name of the token.
* @dev Returns the name of the token.
*/
function name() external view returns (string memory name_);

/**
* @dev Returns the symbol of the token.
* @dev Returns the symbol of the token.
*/
function symbol() external view returns (string memory symbol_);

/**
* @dev Returns the decimal precision used by the token.
* @dev Returns the decimal precision used by the token.
*/
function decimals() external view returns (uint8 decimals_);

/**
* @dev Returns the total amount of tokens in existence.
* @dev Returns the total amount of tokens in existence.
*/
function totalSupply() external view returns (uint256 totalSupply_);

/**
* @dev Returns the amount of tokens owned by a given account.
* @param account_ Account that owns the tokens.
* @dev Returns the amount of tokens owned by a given account.
* @param account_ Account that owns the tokens.
*/
function balanceOf(address account_) external view returns (uint256 balance_);

/**
* @dev Function that returns the allowance that one account has given another over their tokens.
* @param owner_ Account that tokens are approved from.
* @param spender_ Account that tokens are approved for.
* @dev Function that returns the allowance that one account has given another over their tokens.
* @param owner_ Account that tokens are approved from.
* @param spender_ Account that tokens are approved for.
*/
function allowance(address owner_, address spender_) external view returns (uint256 allowance_);

/**
* @dev Function that allows one account to set the allowance of another account over their tokens.
* Emits an {Approval} event.
* @param spender_ Account that tokens are approved for.
* @param amount_ Amount of tokens that have been approved.
* @return success_ Boolean indicating whether the operation succeeded.
* @dev Function that allows one account to set the allowance of another account over their tokens.
* Emits an {Approval} event.
* @param spender_ Account that tokens are approved for.
* @param amount_ Amount of tokens that have been approved.
* @return success_ Boolean indicating whether the operation succeeded.
*/
function approve(address spender_, uint256 amount_) external returns (bool success_);

/**
* @dev Moves an amount of tokens from `msg.sender` to a specified account.
* Emits a {Transfer} event.
* @param recipient_ Account that receives tokens.
* @param amount_ Amount of tokens that are transferred.
* @return success_ Boolean indicating whether the operation succeeded.
* @dev Function that allows one account to decrease the allowance of another account over their tokens.
* Emits an {Approval} event.
* @param spender_ Account that tokens are approved for.
* @param subtractedAmount_ Amount to decrease approval by.
* @return success_ Boolean indicating whether the operation succeeded.
*/
function decreaseAllowance(address spender_, uint256 subtractedAmount_) external returns (bool success_);

/**
* @dev Function that allows one account to increase the allowance of another account over their tokens.
* Emits an {Approval} event.
* @param spender_ Account that tokens are approved for.
* @param addedAmount_ Amount to increase approval by.
* @return success_ Boolean indicating whether the operation succeeded.
*/
function increaseAllowance(address spender_, uint256 addedAmount_) external returns (bool success_);

/**
* @dev Moves an amount of tokens from `msg.sender` to a specified account.
* Emits a {Transfer} event.
* @param recipient_ Account that receives tokens.
* @param amount_ Amount of tokens that are transferred.
* @return success_ Boolean indicating whether the operation succeeded.
*/
function transfer(address recipient_, uint256 amount_) external returns (bool success_);

/**
* @dev Moves a pre-approved amount of tokens from a sender to a specified account.
* Emits a {Transfer} event.
* Emits an {Approval} event.
* @param owner_ Account that tokens are moving from.
* @param recipient_ Account that receives tokens.
* @param amount_ Amount of tokens that are transferred.
* @return success_ Boolean indicating whether the operation succeeded.
* @dev Moves a pre-approved amount of tokens from a sender to a specified account.
* Emits a {Transfer} event.
* Emits an {Approval} event.
* @param owner_ Account that tokens are moving from.
* @param recipient_ Account that receives tokens.
* @param amount_ Amount of tokens that are transferred.
* @return success_ Boolean indicating whether the operation succeeded.
*/
function transferFrom(address owner_, address recipient_, uint256 amount_) external returns (bool success_);

Expand Down
30 changes: 15 additions & 15 deletions contracts/interfaces/IERC20Permit.sol
Original file line number Diff line number Diff line change
Expand Up @@ -7,33 +7,33 @@ import { IERC20 } from "./IERC20.sol";
interface IERC20Permit is IERC20 {

/**
@dev Approve by signature.
@param owner_ Owner address that signed the permit.
@param spender_ Spender of the permit.
@param amount_ Permit approval spend limit.
@param deadline_ Deadline after which the permit is invalid.
@param v_ ECDSA signature v component.
@param r_ ECDSA signature r component.
@param s_ ECDSA signature s component.
* @dev Approve by signature.
* @param owner_ Owner address that signed the permit.
* @param spender_ Spender of the permit.
* @param amount_ Permit approval spend limit.
* @param deadline_ Deadline after which the permit is invalid.
* @param v_ ECDSA signature v component.
* @param r_ ECDSA signature r component.
* @param s_ ECDSA signature s component.
*/
function permit(address owner_, address spender_, uint amount_, uint deadline_, uint8 v_, bytes32 r_, bytes32 s_) external;

/**
* @dev Returns the permit type hash.
* @return hash_ The typehash for the commit.
* @dev Returns the permit type hash.
* @return hash_ The typehash for the commit.
*/
function PERMIT_TYPEHASH() external pure returns (bytes32 hash_);

/**
* @dev Returns the nonce for the given owner.
* @param owner The addreses of the owner account.
* @return nonce_ The current nonce.
* @dev Returns the nonce for the given owner.
* @param owner The addreses of the owner account.
* @return nonce_ The current nonce.
*/
function nonces(address owner) external view returns (uint256 nonce_);

/**
* @dev Returns the signature domain separator.
* @return domain_ The domain for the contract.
* @dev Returns the signature domain separator.
* @return domain_ The domain for the contract.
*/
function DOMAIN_SEPARATOR() external view returns (bytes32 domain_);

Expand Down
41 changes: 31 additions & 10 deletions contracts/test/ERC20.t.sol
Original file line number Diff line number Diff line change
@@ -1,17 +1,12 @@
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity ^0.8.7;

import { DSTest } from "../../modules/ds-test/src/test.sol";
import { InvariantTest, TestUtils } from "../../modules/contract-test-utils/contracts/test.sol";

import { ERC20User } from "./accounts/ERC20User.sol";
import { MockERC20 } from "./mocks/MockERC20.sol";
import { ERC20User } from "./accounts/ERC20User.sol";
import { MockERC20 } from "./mocks/MockERC20.sol";

import { InvariantTest } from "./utils/InvariantTest.sol";
import { Vm } from "./utils/Vm.sol";

contract ERC20Test is DSTest {

Vm vm = Vm(0x7109709ECfa91a80626fF3989D68f67F5b1DD12D);
contract ERC20Test is TestUtils {

bytes constant ARITHMETIC_ERROR = abi.encodeWithSignature("Panic(uint256)", 0x11);

Expand Down Expand Up @@ -60,6 +55,32 @@ contract ERC20Test is DSTest {
assertEq(token.allowance(self, account), amount);
}

function test_increaseAllowance(address account, uint256 initialAmount, uint256 addedAmount) public {
initialAmount = constrictToRange(initialAmount, 0, type(uint256).max / 2);
addedAmount = constrictToRange(addedAmount, 0, type(uint256).max / 2);

token.approve(account, initialAmount);

assertEq(token.allowance(self, account), initialAmount);

assertTrue(token.increaseAllowance(account, addedAmount));

assertEq(token.allowance(self, account), initialAmount + addedAmount);
}

function test_decreaseAllowance(address account, uint256 initialAmount, uint256 subtractedAmount) public {
initialAmount = constrictToRange(initialAmount, 0, type(uint256).max);
subtractedAmount = constrictToRange(subtractedAmount, 0, initialAmount);

token.approve(account, initialAmount);

assertEq(token.allowance(self, account), initialAmount);

assertTrue(token.decreaseAllowance(account, subtractedAmount));

assertEq(token.allowance(self, account), initialAmount - subtractedAmount);
}

function test_transfer(address account, uint256 amount) public {
token.mint(self, amount);

Expand Down Expand Up @@ -152,7 +173,7 @@ contract ERC20Test is DSTest {

}

contract ERC20Invariants is DSTest, InvariantTest {
contract ERC20Invariants is TestUtils, InvariantTest {

BalanceSum balanceSum;

Expand Down
17 changes: 5 additions & 12 deletions contracts/test/ERC20Permit.t.sol
Original file line number Diff line number Diff line change
@@ -1,17 +1,12 @@
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity ^0.8.7;

import { DSTest } from "../../modules/ds-test/src/test.sol";
import { InvariantTest, TestUtils } from "../../modules/contract-test-utils/contracts/test.sol";

import { ERC20Permit } from "../ERC20Permit.sol";

import { ERC20PermitUser } from "./accounts/ERC20User.sol";

import { MockERC20Permit } from "./mocks/MockERC20.sol";

import { Vm } from "./utils/Vm.sol";
import { InvariantTest } from "./utils/InvariantTest.sol";

import { ERC20PermitUser } from "./accounts/ERC20User.sol";
import { MockERC20Permit } from "./mocks/MockERC20.sol";
import { ERC20Test, MockERC20 } from "./ERC20.t.sol";

contract ERC20PermitBaseTest is ERC20Test {
Expand All @@ -22,9 +17,7 @@ contract ERC20PermitBaseTest is ERC20Test {

}

contract ERC20PermitTest is DSTest {

Vm vm = Vm(0x7109709ECfa91a80626fF3989D68f67F5b1DD12D);
contract ERC20PermitTest is TestUtils {

bytes constant ARITHMETIC_ERROR = abi.encodeWithSignature("Panic(uint256)", 0x11);

Expand Down Expand Up @@ -155,7 +148,7 @@ contract ERC20PermitTest is DSTest {

}

contract ERC20Invariants is DSTest, InvariantTest {
contract ERC20Invariants is TestUtils, InvariantTest {

BalanceSum balanceSum;

Expand Down
1 change: 1 addition & 0 deletions modules/contract-test-utils
Submodule contract-test-utils added at 3bda52
1 change: 0 additions & 1 deletion modules/ds-test
Submodule ds-test deleted from 0a5da5