Skip to content

Commit

Permalink
feat: check erc20 return behavior (#22)
Browse files Browse the repository at this point in the history
* feat: check erc20 transfers and approvals return value

* feat: rename class and removed reserved words

* chore: bump version
  • Loading branch information
vittominacori committed Jan 25, 2024
1 parent 1b96776 commit 5f8e412
Show file tree
Hide file tree
Showing 12 changed files with 268 additions and 27 deletions.
Binary file modified analysis/control-flow/ERC1363.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
16 changes: 8 additions & 8 deletions contracts/mocks/ERC1363ReceiverMock.sol
Original file line number Diff line number Diff line change
Expand Up @@ -15,19 +15,19 @@ contract ERC1363ReceiverMock is IERC1363Receiver {
}

bytes4 private _retval;
RevertType private _error;
RevertType private _err;

event Received(address operator, address from, uint256 value, bytes data);
error CustomError(bytes4);

constructor() {
_retval = IERC1363Receiver.onTransferReceived.selector;
_error = RevertType.None;
_err = RevertType.None;
}

function setUp(bytes4 retval, RevertType error) public {
function setUp(bytes4 retval, RevertType err) public {
_retval = retval;
_error = error;
_err = err;
}

function onTransferReceived(
Expand All @@ -36,13 +36,13 @@ contract ERC1363ReceiverMock is IERC1363Receiver {
uint256 value,
bytes calldata data
) external override returns (bytes4) {
if (_error == RevertType.RevertWithoutMessage) {
if (_err == RevertType.RevertWithoutMessage) {
revert();
} else if (_error == RevertType.RevertWithMessage) {
} else if (_err == RevertType.RevertWithMessage) {
revert("ERC1363ReceiverMock: reverting");
} else if (_error == RevertType.RevertWithCustomError) {
} else if (_err == RevertType.RevertWithCustomError) {
revert CustomError(_retval);
} else if (_error == RevertType.Panic) {
} else if (_err == RevertType.Panic) {
uint256 a = uint256(0) / uint256(0);
a;
}
Expand Down
20 changes: 20 additions & 0 deletions contracts/mocks/ERC1363ReturnFalseOnERC20Mock.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.20;

import {IERC20, ERC20, ERC1363} from "../token/ERC1363/ERC1363.sol";

// mock class testing an ERC-20 token that returns false
abstract contract ERC1363ReturnFalseOnERC20Mock is ERC1363 {
function transfer(address, uint256) public pure override(IERC20, ERC20) returns (bool) {
return false;
}

function transferFrom(address, address, uint256) public pure override(IERC20, ERC20) returns (bool) {
return false;
}

function approve(address, uint256) public pure override(IERC20, ERC20) returns (bool) {
return false;
}
}
16 changes: 8 additions & 8 deletions contracts/mocks/ERC1363SpenderMock.sol
Original file line number Diff line number Diff line change
Expand Up @@ -15,29 +15,29 @@ contract ERC1363SpenderMock is IERC1363Spender {
}

bytes4 private _retval;
RevertType private _error;
RevertType private _err;

event Approved(address owner, uint256 value, bytes data);
error CustomError(bytes4);

constructor() {
_retval = IERC1363Spender.onApprovalReceived.selector;
_error = RevertType.None;
_err = RevertType.None;
}

function setUp(bytes4 retval, RevertType error) public {
function setUp(bytes4 retval, RevertType err) public {
_retval = retval;
_error = error;
_err = err;
}

function onApprovalReceived(address owner, uint256 value, bytes calldata data) external override returns (bytes4) {
if (_error == RevertType.RevertWithoutMessage) {
if (_err == RevertType.RevertWithoutMessage) {
revert();
} else if (_error == RevertType.RevertWithMessage) {
} else if (_err == RevertType.RevertWithMessage) {
revert("ERC1363SpenderMock: reverting");
} else if (_error == RevertType.RevertWithCustomError) {
} else if (_err == RevertType.RevertWithCustomError) {
revert CustomError(_retval);
} else if (_error == RevertType.Panic) {
} else if (_err == RevertType.Panic) {
uint256 a = uint256(0) / uint256(0);
a;
}
Expand Down
14 changes: 10 additions & 4 deletions contracts/token/ERC1363/ERC1363.sol
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

pragma solidity ^0.8.20;

import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import {IERC20, ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import {IERC165, ERC165} from "@openzeppelin/contracts/utils/introspection/ERC165.sol";

import {IERC1363} from "./IERC1363.sol";
Expand Down Expand Up @@ -35,7 +35,9 @@ abstract contract ERC1363 is ERC20, ERC165, IERC1363, IERC1363Errors {
* @inheritdoc IERC1363
*/
function transferAndCall(address to, uint256 value, bytes memory data) public virtual returns (bool) {
transfer(to, value);
if (!transfer(to, value)) {
revert ERC1363TransferFailed(to, value);
}
_checkOnTransferReceived(_msgSender(), to, value, data);
return true;
}
Expand All @@ -56,7 +58,9 @@ abstract contract ERC1363 is ERC20, ERC165, IERC1363, IERC1363Errors {
uint256 value,
bytes memory data
) public virtual returns (bool) {
transferFrom(from, to, value);
if (!transferFrom(from, to, value)) {
revert ERC1363TransferFromFailed(from, to, value);
}
_checkOnTransferReceived(from, to, value, data);
return true;
}
Expand All @@ -72,7 +76,9 @@ abstract contract ERC1363 is ERC20, ERC165, IERC1363, IERC1363Errors {
* @inheritdoc IERC1363
*/
function approveAndCall(address spender, uint256 value, bytes memory data) public virtual returns (bool) {
approve(spender, value);
if (!approve(spender, value)) {
revert ERC1363ApproveFailed(spender, value);
}
_checkOnApprovalReceived(spender, value, data);
return true;
}
Expand Down
22 changes: 22 additions & 0 deletions contracts/token/ERC1363/IERC1363Errors.sol
Original file line number Diff line number Diff line change
Expand Up @@ -30,4 +30,26 @@ interface IERC1363Errors {
* @param spender The address which will spend the funds.
*/
error ERC1363InvalidSpender(address spender);

/**
* @dev Indicates a failure with the ERC-20 `transfer` during a `transferAndCall` operation. Used in transfers.
* @param receiver The address to which tokens are being transferred.
* @param value The amount of tokens to be transferred.
*/
error ERC1363TransferFailed(address receiver, uint256 value);

/**
* @dev Indicates a failure with the ERC-20 `transferFrom` during a `transferFromAndCall` operation. Used in transfers.
* @param sender The address from which to send tokens.
* @param receiver The address to which tokens are being transferred.
* @param value The amount of tokens to be transferred.
*/
error ERC1363TransferFromFailed(address sender, address receiver, uint256 value);

/**
* @dev Indicates a failure with the ERC-20 `approve` during a `approveAndCall` operation. Used in approvals.
* @param spender The address which will spend the funds.
* @param value The amount of tokens to be spent.
*/
error ERC1363ApproveFailed(address spender, uint256 value);
}
34 changes: 31 additions & 3 deletions dist/ERC1363.dist.sol
Original file line number Diff line number Diff line change
Expand Up @@ -802,6 +802,28 @@ interface IERC1363Errors {
* @param spender The address which will spend the funds.
*/
error ERC1363InvalidSpender(address spender);

/**
* @dev Indicates a failure with the ERC-20 `transfer` during a `transferAndCall` operation. Used in transfers.
* @param receiver The address to which tokens are being transferred.
* @param value The amount of tokens to be transferred.
*/
error ERC1363TransferFailed(address receiver, uint256 value);

/**
* @dev Indicates a failure with the ERC-20 `transferFrom` during a `transferFromAndCall` operation. Used in transfers.
* @param sender The address from which to send tokens.
* @param receiver The address to which tokens are being transferred.
* @param value The amount of tokens to be transferred.
*/
error ERC1363TransferFromFailed(address sender, address receiver, uint256 value);

/**
* @dev Indicates a failure with the ERC-20 `approve` during a `approveAndCall` operation. Used in approvals.
* @param spender The address which will spend the funds.
* @param value The amount of tokens to be spent.
*/
error ERC1363ApproveFailed(address spender, uint256 value);
}


Expand Down Expand Up @@ -900,7 +922,9 @@ abstract contract ERC1363 is ERC20, ERC165, IERC1363, IERC1363Errors {
* @inheritdoc IERC1363
*/
function transferAndCall(address to, uint256 value, bytes memory data) public virtual returns (bool) {
transfer(to, value);
if (!transfer(to, value)) {
revert ERC1363TransferFailed(to, value);
}
_checkOnTransferReceived(_msgSender(), to, value, data);
return true;
}
Expand All @@ -921,7 +945,9 @@ abstract contract ERC1363 is ERC20, ERC165, IERC1363, IERC1363Errors {
uint256 value,
bytes memory data
) public virtual returns (bool) {
transferFrom(from, to, value);
if (!transferFrom(from, to, value)) {
revert ERC1363TransferFromFailed(from, to, value);
}
_checkOnTransferReceived(from, to, value, data);
return true;
}
Expand All @@ -937,7 +963,9 @@ abstract contract ERC1363 is ERC20, ERC165, IERC1363, IERC1363Errors {
* @inheritdoc IERC1363
*/
function approveAndCall(address spender, uint256 value, bytes memory data) public virtual returns (bool) {
approve(spender, value);
if (!approve(spender, value)) {
revert ERC1363ApproveFailed(spender, value);
}
_checkOnApprovalReceived(spender, value, data);
return true;
}
Expand Down
46 changes: 46 additions & 0 deletions docs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -347,6 +347,52 @@ _Indicates a failure with the token `spender`. Used in approvals._
| ---- | ---- | ----------- |
| spender | address | The address which will spend the funds. |

### ERC1363TransferFailed

```solidity
error ERC1363TransferFailed(address receiver, uint256 value)
```

_Indicates a failure with the ERC-20 `transfer` during a `transferAndCall` operation. Used in transfers._

#### Parameters

| Name | Type | Description |
| ---- | ---- | ----------- |
| receiver | address | The address to which tokens are being transferred. |
| value | uint256 | The amount of tokens to be transferred. |

### ERC1363TransferFromFailed

```solidity
error ERC1363TransferFromFailed(address sender, address receiver, uint256 value)
```

_Indicates a failure with the ERC-20 `transferFrom` during a `transferFromAndCall` operation. Used in transfers._

#### Parameters

| Name | Type | Description |
| ---- | ---- | ----------- |
| sender | address | The address from which to send tokens. |
| receiver | address | The address to which tokens are being transferred. |
| value | uint256 | The amount of tokens to be transferred. |

### ERC1363ApproveFailed

```solidity
error ERC1363ApproveFailed(address spender, uint256 value)
```

_Indicates a failure with the ERC-20 `approve` during a `approveAndCall` operation. Used in approvals._

#### Parameters

| Name | Type | Description |
| ---- | ---- | ----------- |
| spender | address | The address which will spend the funds. |
| value | uint256 | The amount of tokens to be spent. |

## IERC1363Receiver

_Interface for any contract that wants to support `transferAndCall` or `transferFromAndCall` from ERC-1363 token contracts._
Expand Down
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "erc-payable-token",
"version": "5.1.7",
"version": "5.2.0",
"description": "ERC-1363 Payable Token Implementation",
"files": [
"contracts",
Expand Down
1 change: 0 additions & 1 deletion test/helpers/enums.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,5 @@ function Enum(...options) {
}

module.exports = {
Enum,
RevertType: Enum('None', 'RevertWithoutMessage', 'RevertWithMessage', 'RevertWithCustomError', 'Panic'),
};
Loading

0 comments on commit 5f8e412

Please sign in to comment.