Skip to content

Commit

Permalink
feat: refactor utility files (#15)
Browse files Browse the repository at this point in the history
* feat: remove contracts and tests to simplify examples
* feat: rename payable contract
* feat: improve examples and test
* feat: improve erc1363 tests
* feat: improve guardian contact
* feat: add dist file
* feat: improve mock test
* docs: improve comments
* feat: update naming in comments and update readme
* feat: add docgen
  • Loading branch information
vittominacori authored Oct 20, 2023
1 parent 895e6a2 commit 742e306
Show file tree
Hide file tree
Showing 43 changed files with 4,866 additions and 2,003 deletions.
2 changes: 1 addition & 1 deletion .env.example
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# vuepress deploy
GIT_DEPLOY_DIR=docs/.vuepress/dist
GIT_DEPLOY_DIR=pages/.vuepress/dist
GIT_DEPLOY_BRANCH=gh-pages
GIT_DEPLOY_REPO=origin
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -90,4 +90,4 @@ jobs:
with:
check_hidden: true
check_filenames: true
skip: package-lock.json
skip: .git,package-lock.json
4 changes: 2 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -54,5 +54,5 @@ contracts-exposed
.idea

# vuepress
docs/.vuepress/dist
docs/.vuepress/.env.json
pages/.vuepress/dist
pages/.vuepress/.env.json
89 changes: 41 additions & 48 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,32 +5,25 @@
[![Coverage Status](https://codecov.io/gh/vittominacori/erc1363-payable-token/graph/badge.svg)](https://codecov.io/gh/vittominacori/erc1363-payable-token)
[![MIT licensed](https://img.shields.io/github/license/vittominacori/erc1363-payable-token.svg)](https://github.com/vittominacori/erc1363-payable-token/blob/master/LICENSE)

ERC-1363 allows to implement an ERC-20 smart token.
---

It means that we can add a callback to be executed after transferring or approving tokens.
[ERC-1363](https://eips.ethereum.org/EIPS/eip-1363) is an extension interface for ERC-20 tokens that supports executing code on a recipient contract after `transfer` or `transferFrom`, or code on a spender contract after `approve`, in a single transaction.

This is an implementation of the [EIP-1363](https://eips.ethereum.org/EIPS/eip-1363) that defines an interface for ERC-20 tokens that supports executing code on a recipient contract after `transfer` or `transferFrom`, or code on a spender contract after `approve`, in a single transaction.
There is no way to execute code on a receiver/spender contract after an ERC-20 `transfer`, `transferFrom` or `approve` so, to perform an action, it is required to send another transaction.
This introduces complexity in UI development and friction on adoption as users must wait for the first transaction to be executed and then submit the second one. They must also pay GAS twice.

## Abstract
There is no way to execute any code on a receiver or spender contract after an ERC-20 `transfer`, `transferFrom` or `approve` so, to make an action, it is required to send another transaction.
ERC-1363 makes tokens capable of performing actions more easily and working without the use of any off-chain listener.
It allows to make a callback on receiver/spender contracts, after transfers or approvals, in a single transaction.

This introduces complexity on UI development and friction on adoption because users must wait for the first transaction to be executed, and then send the second one. They must also pay GAS twice.
The following are functions and callbacks introduced by ERC-1363:

This proposal aims to make tokens capable of performing actions more easily and working with contracts without the use of any other external listener.
It allows to make a callback on a receiver or spender contract, after a transfer or an approval, in a single transaction.
* `transferAndCall` and `transferFromAndCall` will call an `onTransferReceived` on a `ERC1363Receiver` contract.
* `approveAndCall` will call an `onApprovalReceived` on a `ERC1363Spender` contract.

There are many proposed uses of Ethereum smart contracts that can accept EIP-20 callbacks.
ERC-1363 tokens can be used for specific utilities in all cases that require a callback to be executed after a transfer or an approval received.
ERC-1363 is also useful for avoiding token loss or token locking in contracts by verifying the recipient contract's ability to handle tokens.

Examples could be

* to create a token payable crowdsale
* selling services for tokens
* paying invoices
* making subscriptions

For these reasons it was originally named **"Payable Token"**.

Anyway you can use it for specific utilities or for any other purposes who require the execution of a callback after a transfer or approval received.
**NOTE: This repo contains the reference implementation of the official [ERC-1363](https://eips.ethereum.org/EIPS/eip-1363).**

## Install

Expand All @@ -46,7 +39,6 @@ pragma solidity ^0.8.20;
import {ERC1363} from "erc-payable-token/contracts/token/ERC1363/ERC1363.sol";
contract MyToken is ERC1363 {
constructor(string memory name, string memory symbol) ERC20(name, symbol) {
// your stuff
}
Expand All @@ -63,7 +55,7 @@ This repo contains:

[IERC1363.sol](https://github.com/vittominacori/erc1363-payable-token/blob/master/contracts/token/ERC1363/IERC1363.sol)

Interface of the ERC1363 standard as defined in the [EIP-1363](https://eips.ethereum.org/EIPS/eip-1363).
Interface of the ERC-1363 standard as defined in the [ERC-1363](https://eips.ethereum.org/EIPS/eip-1363).

```solidity
interface IERC1363 is IERC20, IERC165 {
Expand All @@ -76,23 +68,11 @@ interface IERC1363 is IERC20, IERC165 {
}
```

### IERC1363Errors

[IERC1363Errors.sol](https://github.com/vittominacori/erc1363-payable-token/blob/master/contracts/token/ERC1363/IERC1363Errors.sol)

Interface of the ERC1363 custom errors following the [ERC-6093](https://eips.ethereum.org/EIPS/eip-6093) rationale.

### ERC1363

[ERC1363.sol](https://github.com/vittominacori/erc1363-payable-token/blob/master/contracts/token/ERC1363/ERC1363.sol)

Implementation of the ERC1363 interface.

### IERC1363Receiver

[IERC1363Receiver.sol](https://github.com/vittominacori/erc1363-payable-token/blob/master/contracts/token/ERC1363/IERC1363Receiver.sol)

Interface for any contract that wants to support `transferAndCall` or `transferFromAndCall` from ERC1363 token contracts.
Interface for any contract that wants to support `transferAndCall` or `transferFromAndCall` from ERC-1363 token contracts.

```solidity
interface IERC1363Receiver {
Expand All @@ -104,27 +84,39 @@ interface IERC1363Receiver {

[IERC1363Spender.sol](https://github.com/vittominacori/erc1363-payable-token/blob/master/contracts/token/ERC1363/IERC1363Spender.sol)

Interface for any contract that wants to support `approveAndCall` from ERC1363 token contracts.
Interface for any contract that wants to support `approveAndCall` from ERC-1363 token contracts.

```solidity
interface IERC1363Spender {
function onApprovalReceived(address sender, uint256 amount, bytes calldata data) external returns (bytes4);
}
```

### ERC1363Holder
### IERC1363Errors

[IERC1363Errors.sol](https://github.com/vittominacori/erc1363-payable-token/blob/master/contracts/token/ERC1363/IERC1363Errors.sol)

Interface of the ERC-1363 custom errors following the [ERC-6093](https://eips.ethereum.org/EIPS/eip-6093) rationale.

### ERC1363

[ERC1363.sol](https://github.com/vittominacori/erc1363-payable-token/blob/master/contracts/token/ERC1363/ERC1363.sol)

Implementation of the ERC-1363 interface.

[ERC1363Holder.sol](https://github.com/vittominacori/erc1363-payable-token/blob/master/contracts/token/ERC1363/utils/ERC1363Holder.sol)
The reference implementation of ERC-1363 that extends ERC-20 and adds support for executing code after transfers and approvals on recipient contracts.

Implementation of `IERC1363Receiver` and `IERC1363Spender` that will allow a contract to receive ERC1363 token transfers or approval.
> **NOTE**: `transferAndCall`, `transferFromAndCall` and `approveAndCall` revert if the recipient/spender is an EOA address. To transfer tokens to an EOA or approve it to spend tokens, use the ERC-20 `transfer`, `transferFrom` or `approve` methods.
IMPORTANT: When inheriting this contract, you must include a way to use the received tokens or spend the allowance, otherwise they will be stuck.
## Examples

### ERC1363Payable
**IMPORTANT**: the example contracts are for testing purpose only. When inheriting or copying from these contracts, you must include a way to use the received tokens, otherwise they will be stuck into the contract.

[ERC1363Payable.sol](https://github.com/vittominacori/erc1363-payable-token/blob/master/contracts/payment/ERC1363Payable.sol)
### ERC1363Guardian

Implementation proposal of a contract that wants to accept ERC1363 payments. It intercepts what is the ERC1363 token desired for payments and throws if another is sent.
[ERC1363Guardian.sol](https://github.com/vittominacori/erc1363-payable-token/blob/master/contracts/examples/ERC1363Guardian.sol)

As example: a contract that allows to accept ERC-1363 callback after transfers or approvals.

It emits a `TokensReceived` event to notify the transfer received by the contract.

Expand All @@ -134,21 +126,22 @@ It emits a `TokensApproved` event to notify the approval received by the contrac

It also implements a `_approvalReceived` function that can be overridden to make your stuff within your contract after a `onApprovalReceived`.

### ERC1363PayableCrowdsale
### ERC1363MethodCallReceiver

[ERC1363PayableCrowdsale.sol](https://github.com/vittominacori/erc1363-payable-token/blob/master/contracts/examples/ERC1363PayableCrowdsale.sol)
[ERC1363MethodCallReceiver.sol](https://github.com/vittominacori/erc1363-payable-token/blob/master/contracts/examples/ERC1363MethodCallReceiver.sol)

As example: an Implementation of a classic token Crowdsale, but paid with ERC1363 tokens instead of ETH.
As example: a contract that allows to test passing methods via abi encoded function call.

### ERC1363MethodCallReceiver
It executed the method passed via `data`. Methods emit a `MethodCall` event.

[ERC1363MethodCallReceiver.sol](https://github.com/vittominacori/erc1363-payable-token/blob/master/contracts/examples/ERC1363MethodCallReceiver.sol)
## Documentation

As example: a contract allowing to test passing methods via abi encoded function call.
* [Documentation](https://github.com/vittominacori/erc1363-payable-token/tree/master/docs/index.md)

## Code Analysis

* [Control Flow](https://raw.githubusercontent.com/vittominacori/erc1363-payable-token/master/analysis/control-flow/ERC1363.png)
* [Description Table](https://github.com/vittominacori/erc1363-payable-token/blob/master/analysis/description-table/ERC1363.md)
* [Inheritance Tree](https://raw.githubusercontent.com/vittominacori/erc1363-payable-token/master/analysis/inheritance-tree/ERC1363.png)
* [UML](https://raw.githubusercontent.com/vittominacori/erc1363-payable-token/master/analysis/uml/ERC1363.svg)

Expand Down
97 changes: 97 additions & 0 deletions contracts/examples/ERC1363Guardian.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.20;

import {IERC1363Receiver} from "../token/ERC1363/IERC1363Receiver.sol";
import {IERC1363Spender} from "../token/ERC1363/IERC1363Spender.sol";

/**
* @title ERC1363Guardian
* @dev Implementation example of a contract that allows to accept ERC-1363 callback after transfers or approvals.
*
* IMPORTANT: This contract is for testing purpose only. When inheriting or copying from this contract,
* you must include a way to use the received tokens, otherwise they will be stuck into the contract.
*/
contract ERC1363Guardian is IERC1363Receiver, IERC1363Spender {
/**
* @dev Emitted when a `value` amount of tokens `token` are moved from `from` to
* this contract by `operator` using `transferAndCall` or `transferFromAndCall`.
*/
event TokensReceived(
address indexed token,
address indexed operator,
address indexed from,
uint256 value,
bytes data
);

/**
* @dev Emitted when the allowance for token `token` of this contract for an `owner` is set by
* a call to `approveAndCall`. `value` is the new allowance.
*/
event TokensApproved(address indexed token, address indexed owner, uint256 value, bytes data);

/*
* @inheritdoc IERC1363Receiver
*/
function onTransferReceived(
address operator,
address from,
uint256 value,
bytes calldata data
) external override returns (bytes4) {
// The ERC-1363 contract is always the caller.
address token = msg.sender;

emit TokensReceived(token, operator, from, value, data);

_transferReceived(token, operator, from, value, data);

return this.onTransferReceived.selector;
}

/*
* @inheritdoc IERC1363Spender
*/
function onApprovalReceived(address owner, uint256 value, bytes calldata data) external override returns (bytes4) {
// The ERC-1363 contract is always the caller.
address token = msg.sender;

emit TokensApproved(token, owner, value, data);

_approvalReceived(token, owner, value, data);

return this.onApprovalReceived.selector;
}

/**
* @dev Called after validating a `onTransferReceived`. Override this method to make your stuff within your contract.
* @param token The address of the token that was received.
* @param operator The address which called `transferAndCall` or `transferFromAndCall` function.
* @param from The address which are tokens transferred from.
* @param value The amount of tokens transferred.
* @param data Additional data with no specified format.
*/
function _transferReceived(
address token,
address operator,
address from,
uint256 value,
bytes calldata data
) internal virtual {
// optional override
}

/**
* @dev Called after validating a `onApprovalReceived`. Override this method to make your stuff within your contract.
* @param token The address of the token that was approved.
* @param owner The address which called `approveAndCall` function and previously owned the tokens.
* @param value The amount of tokens to be spent.
* @param data Additional data with no specified format.
*/
function _approvalReceived(address token, address owner, uint256 value, bytes calldata data) internal virtual {
// optional override
// I.e. you could transfer the approved tokens into the `ERC1363Guardian` contract by doing:
// IERC20(token).transferFrom(owner, address(this), value);
}
}
38 changes: 15 additions & 23 deletions contracts/examples/ERC1363MethodCallReceiver.sol
Original file line number Diff line number Diff line change
Expand Up @@ -2,53 +2,45 @@

pragma solidity ^0.8.20;

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

import {IERC1363Receiver} from "../token/ERC1363/IERC1363Receiver.sol";

/**
* @title ERC1363MethodCallReceiver
* @dev ERC1363MethodCallReceiver is an example contract allowing test passing methods
* via abi encoded function call.
* @dev Implementation example of a contract that allows to test passing methods via abi encoded function call.
*
* IMPORTANT: This contract is for testing purpose only. When inheriting or copying from this contract,
* you must include a way to use the received tokens, otherwise they will be stuck into the contract.
*/
contract ERC1363MethodCallReceiver is ERC165, IERC1363Receiver {
contract ERC1363MethodCallReceiver is IERC1363Receiver {
/**
* @dev Event for logging method call.
* @param method The function that has been called.
* @param param The function param.
*/
event MethodCall(string method, string param);

constructor() {}

/**
* @inheritdoc IERC165
*/
function supportsInterface(bytes4 interfaceId) public view virtual override(ERC165) returns (bool) {
return interfaceId == type(IERC1363Receiver).interfaceId || super.supportsInterface(interfaceId);
}

/*
* NOTE: remember that the ERC1363 contract is always the caller.
* @inheritdoc IERC1363Receiver
* @dev Whenever ERC-1363 tokens are transferred to this contract via `transferAndCall` or `transferFromAndCall` this function is called.
* In this example the abi encoded method passed in `data` is executed on this contract.
*/
function onTransferReceived(
address /* operator */,
address /* from */,
uint256 /* value */,
bytes calldata data
) public override returns (bytes4) {
function onTransferReceived(address, address, uint256, bytes calldata data) external override returns (bytes4) {
(bool success, ) = address(this).call(data);

require(success, "Low level call failed");

return IERC1363Receiver.onTransferReceived.selector;
return this.onTransferReceived.selector;
}

/*
* @dev A simple method without parameters. Just for testing purpose.
*/
function methodWithoutParam() public {
emit MethodCall("methodWithoutParam", "");
}

/*
* @dev A simple method accepting parameters. Just for testing purpose.
*/
function methodWithParam(string calldata param) public {
emit MethodCall("methodWithParam", param);
}
Expand Down
Loading

0 comments on commit 742e306

Please sign in to comment.