Skip to content

Commit

Permalink
feat: Add increase/decrease allowance functionality (#20)
Browse files Browse the repository at this point in the history
* feat: setup functions and interfaces

* feat: add increase/decrease allowance functions

* fix: update all natspec

* fix: rm ds-test submodule
  • Loading branch information
Lucas Manuel authored Mar 9, 2022
1 parent cc1dc96 commit 15ecb91
Show file tree
Hide file tree
Showing 9 changed files with 127 additions and 75 deletions.
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

0 comments on commit 15ecb91

Please sign in to comment.