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

Feature/erc777 #1159 #1684

Merged
merged 68 commits into from
Apr 16, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
68 commits
Select commit Hold shift + click to select a range
fe15d14
IERC777 from specs, constants returned, up to defaultOperators. (#1159)
utgarda Oct 7, 2018
1d26da3
IERC777 oprarator approvals (#1159)
utgarda Oct 7, 2018
ebb4fad
ERC777 oprarator approvals fixes and tests
utgarda Oct 9, 2018
73ac6af
IERC777 send and receive with ERC820 (#1159)
Oct 24, 2018
9a6ed29
ERC777 Add burn functions and fix send functions (#1159)
Oct 27, 2018
2fc7b4d
ERC777 Make expectEvent compatible with web3.js 1.0 (#1159)
Nov 3, 2018
5920482
ERC777 Add ERC820 deploy script (#1159)
Nov 3, 2018
fc63684
ERC777 Complete implementation of ERC777 (#1159)
Nov 3, 2018
be6fcd1
ERC777 Update ERC820 Registry contract to final version (#1159)
Jan 31, 2019
2db4656
ERC777 Move contracts to 'drafts' folder (#1159)
Jan 31, 2019
d9e53d9
ERC777: Update to ERC1820 registry and linter error fix (#1159)
Mar 15, 2019
e54490a
ERC777: implement recent changes of EIP777 (#1159)
Mar 15, 2019
5695639
ERC777 Fix formatting (#1159)
Mar 17, 2019
9965393
Merge branch 'master' into feature/ERC777-#1159
Mar 17, 2019
2582a2a
ERC777 Update to solc 0.5.2 (#1159)
Mar 17, 2019
7f7d296
ERC777 Fix travis CI errors (#1159)
Mar 21, 2019
990073f
ERC777 Fix linter errors again... (#1159)
Mar 21, 2019
17930e5
ERC777 Fix unit test (#1159)
Mar 21, 2019
65257d5
ERC777 Fix unit test again (#1159)
Mar 21, 2019
d98ef86
Remove extra newlines.
nventuro Mar 26, 2019
831b367
Rename ERC777Base to ERC777.
nventuro Mar 26, 2019
f1df62d
Remove 'Token' from contract names.
nventuro Mar 26, 2019
00df2b6
Replace ops for operators.
nventuro Mar 26, 2019
5a0195a
Move operator check out of _send.
nventuro Mar 26, 2019
06fd3a9
Remove ERC777Burnable.
nventuro Mar 26, 2019
b354c5e
Merge branch 'master' into feature/ERC777-#1159
nventuro Mar 26, 2019
4528200
Remove ERC1820Client, now using the interface directly.
nventuro Mar 26, 2019
9d44f7c
Minor internal refactors in contracts.
nventuro Mar 26, 2019
4d383d4
Delete extra test helpers.
nventuro Mar 26, 2019
dc91c59
Simplified tests.
nventuro Mar 27, 2019
5538764
Add basic 777 tests.
nventuro Mar 27, 2019
e9936f7
Add granularity send test.
nventuro Mar 27, 2019
5c0ee8a
Add first operator send tests.
nventuro Mar 27, 2019
824cb94
Add burn tests.
nventuro Mar 27, 2019
de529bb
Refactor send and burn tests.
nventuro Mar 27, 2019
ee159b4
Improve send burn refactor.
nventuro Mar 27, 2019
f9ada94
Greatly improve test module.
nventuro Mar 28, 2019
37ad5d5
Burn instead of send removed tokens.
nventuro Mar 28, 2019
a76a1cc
Add operator tests.
nventuro Mar 28, 2019
1823108
Improve send tests under changing operators.
nventuro Mar 28, 2019
0e78a62
Refactor and merge send and burn tests.
nventuro Mar 28, 2019
6cdaa1f
Add missing and not-implemented tests.
nventuro Apr 4, 2019
29134fe
Make _burn private.
nventuro Apr 4, 2019
d272f5b
Fix typo.
nventuro Apr 4, 2019
b39d65e
Greatly improve tokensToSend tests.
nventuro Apr 6, 2019
a6d39ce
Refactor hook tests.
nventuro Apr 6, 2019
71c8698
Fix hook tests.
nventuro Apr 9, 2019
060119f
Update openzeppelin-test-helpers and ERC1820 address.
nventuro Apr 11, 2019
01c74fb
Fix natspec indentation.
nventuro Apr 11, 2019
100a1cb
Make interface functions external.
nventuro Apr 11, 2019
b17f6c5
Remove redundant private revoke and authorize functions.
nventuro Apr 11, 2019
2945463
Improved readability of if statement.
nventuro Apr 11, 2019
029a021
Remove unnecessary asserts.
nventuro Apr 11, 2019
db2b2f5
Add non-one granularity test.
nventuro Apr 11, 2019
3518525
Fix hook call order in _mint.
nventuro Apr 11, 2019
7e91c3f
Fix _mint not reverting on failure to implement tokensReceived.
nventuro Apr 11, 2019
2cff2ce
Remove special case in operatorFn when from is 0.
nventuro Apr 11, 2019
cec8ebe
Refactor ERC777SenderMock.
nventuro Apr 11, 2019
8e2af46
Add tokensReceived tests.
nventuro Apr 11, 2019
ad40a92
switch to updated ganache-cli-coverage fork
frangio Apr 6, 2019
480407b
Merge branch 'master' into feature/ERC777-#1159
nventuro Apr 11, 2019
1f8b3d6
Fix linter errors.
nventuro Apr 11, 2019
343cc51
Merge branch 'feature/ERC777-#1159' of github.com:catageek/openzeppel…
nventuro Apr 11, 2019
1feeac3
Add mint tests.
nventuro Apr 12, 2019
1b04f88
Fix linter errors.
nventuro Apr 12, 2019
455e84a
Fix tests.
nventuro Apr 15, 2019
3dd46a0
Update test/drafts/ERC777/ERC777.test.js
frangio Apr 15, 2019
d7be2a6
Add changelog entry.
nventuro Apr 16, 2019
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
4 changes: 3 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@
## 2.3.0 (unreleased)

### New features:
* `ERC1820`: added support for interacting with the [ERC1820](https://eips.ethereum.org/EIPS/eip-1820) registry contract (`IERC1820Registry`), as well as base contracts that can be registered as implementers there. ([#1677](https://github.com/OpenZeppelin/openzeppelin-solidity/pull/1677))
* `ERC1820`: added support for interacting with the [ERC1820](https://eips.ethereum.org/EIPS/eip-1820) registry contract (`IERC1820Registry`), as well as base contracts that can be registered as
implementers there. ([#1677](https://github.com/OpenZeppelin/openzeppelin-solidity/pull/1677))
* `ERC777`: initial support for the [ERC777 token](https://eips.ethereum.org/EIPS/eip-777), which has multiple improvements over `ERC20` such as built-in burning, a more straightforward permission system, and optional sender and receiver hooks on transfer (mandatory for contracts!). ([#1684](https://github.com/OpenZeppelin/openzeppelin-solidity/pull/1684))

## 2.2.0 (2019-03-14)

Expand Down
357 changes: 357 additions & 0 deletions contracts/drafts/ERC777/ERC777.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,357 @@
pragma solidity ^0.5.2;

import "./IERC777.sol";
import "./IERC777Recipient.sol";
import "./IERC777Sender.sol";
import "../../math/SafeMath.sol";
import "../../utils/Address.sol";
import "../IERC1820Registry.sol";

/**
* @title ERC777 token implementation
* @author etsvigun <utgarda@gmail.com>, Bertrand Masius <github@catageeks.tk>
*/
contract ERC777 is IERC777 {
using SafeMath for uint256;
using Address for address;

IERC1820Registry private _erc1820 = IERC1820Registry(0x1820a4B7618BdE71Dce8cdc73aAB6C95905faD24);

string private _name;

string private _symbol;

mapping(address => uint256) private _balances;

uint256 private _totalSupply;

uint256 private _granularity;

bytes32 constant private TOKENS_SENDER_INTERFACE_HASH = keccak256("ERC777TokensSender");
bytes32 constant private TOKENS_RECIPIENT_INTERFACE_HASH = keccak256("ERC777TokensRecipient");

// This isn't ever read from - it's only used to respond to the defaultOperators query.
address[] private _defaultOperatorsArray;

// Immutable, but accounts may revoke them (tracked in __revokedDefaultOperators).
mapping(address => bool) private _defaultOperators;

// For each account, a mapping of its operators and revoked default operators.
mapping(address => mapping(address => bool)) private _operators;
mapping(address => mapping(address => bool)) private _revokedDefaultOperators;

constructor(
string memory name,
string memory symbol,
uint256 granularity,
address[] memory defaultOperators
) public {
require(granularity > 0);

_name = name;
_symbol = symbol;
_granularity = granularity;

_defaultOperatorsArray = defaultOperators;
for (uint256 i = 0; i < _defaultOperatorsArray.length; i++) {
_defaultOperators[_defaultOperatorsArray[i]] = true;
}

// register interface
_erc1820.setInterfaceImplementer(address(this), keccak256("ERC777Token"), address(this));
}

/**
* @dev Send the amount of tokens from the address msg.sender to the address to
* @param to address recipient address
* @param amount uint256 amount of tokens to transfer
* @param data bytes information attached to the send, and intended for the recipient (to)
*/
function send(address to, uint256 amount, bytes calldata data) external {
_send(msg.sender, msg.sender, to, amount, data, "");
}

/**
* @dev Send the amount of tokens on behalf of the address from to the address to
* @param from address token holder address.
* @param to address recipient address
* @param amount uint256 amount of tokens to transfer
* @param data bytes information attached to the send, and intended for the recipient (to)
* @param operatorData bytes extra information provided by the operator (if any)
*/
function operatorSend(
address from,
address to,
uint256 amount,
bytes calldata data,
bytes calldata operatorData
)
external
{
require(isOperatorFor(msg.sender, from));
_send(msg.sender, from, to, amount, data, operatorData);
}

/**
* @dev Burn the amount of tokens from the address msg.sender
* @param amount uint256 amount of tokens to transfer
* @param data bytes extra information provided by the token holder
*/
function burn(uint256 amount, bytes calldata data) external {
_burn(msg.sender, msg.sender, amount, data, "");
}

/**
* @dev Burn the amount of tokens on behalf of the address from
* @param from address token holder address.
* @param amount uint256 amount of tokens to transfer
* @param data bytes extra information provided by the token holder
* @param operatorData bytes extra information provided by the operator (if any)
*/
function operatorBurn(address from, uint256 amount, bytes calldata data, bytes calldata operatorData) external {
require(isOperatorFor(msg.sender, from));
_burn(msg.sender, from, amount, data, operatorData);
}

/**
* @dev Authorize an operator for the sender
* @param operator address to be authorized as operator
*/
function authorizeOperator(address operator) external {
require(msg.sender != operator);

if (_defaultOperators[operator]) {
delete _revokedDefaultOperators[msg.sender][operator];
} else {
_operators[msg.sender][operator] = true;
}

emit AuthorizedOperator(operator, msg.sender);
}

/**
* @dev Revoke operator rights from one of the default operators
* @param operator address to revoke operator rights from
*/
function revokeOperator(address operator) external {
require(operator != msg.sender);

if (_defaultOperators[operator]) {
_revokedDefaultOperators[msg.sender][operator] = true;
} else {
delete _operators[msg.sender][operator];
}

emit RevokedOperator(operator, msg.sender);
}

/**
* @return the name of the token.
*/
function name() public view returns (string memory) {
return _name;
}

/**
* @return the symbol of the token.
*/
function symbol() public view returns (string memory) {
return _symbol;
}

/**
* @dev Total number of tokens in existence
*/
function totalSupply() public view returns (uint256) {
return _totalSupply;
}

/**
* @dev Gets the balance of the specified address.
* @param tokenHolder The address to query the balance of.
* @return uint256 representing the amount owned by the specified address.
*/
function balanceOf(address tokenHolder) public view returns (uint256) {
return _balances[tokenHolder];
}

/**
* @dev Gets the token's granularity,
* i.e. the smallest number of tokens (in the basic unit)
* which may be minted, sent or burned at any time
* @return uint256 granularity
*/
function granularity() public view returns (uint256) {
return _granularity;
}

/**
* @dev Get the list of default operators as defined by the token contract.
* @return address[] default operators
*/
function defaultOperators() public view returns (address[] memory) {
return _defaultOperatorsArray;
}

/**
* @dev Indicate whether an address
* is an operator of the tokenHolder address
* @param operator address which may be an operator of tokenHolder
* @param tokenHolder address of a token holder which may have the operator
* address as an operator.
*/
function isOperatorFor(
address operator,
address tokenHolder
) public view returns (bool) {
return operator == tokenHolder ||
(_defaultOperators[operator] && !_revokedDefaultOperators[tokenHolder][operator]) ||
_operators[tokenHolder][operator];
}

/**
* @dev Mint tokens. Does not check authorization of operator
* @dev the caller may ckeck that operator is authorized before calling
* @param operator address operator requesting the operation
* @param to address token recipient address
* @param amount uint256 amount of tokens to mint
* @param userData bytes extra information defined by the token recipient (if any)
* @param operatorData bytes extra information provided by the operator (if any)
*/
function _mint(
address operator,
address to,
uint256 amount,
bytes memory userData,
bytes memory operatorData
)
internal
{
require(to != address(0));
require((amount % _granularity) == 0);

// Update state variables
_totalSupply = _totalSupply.add(amount);
_balances[to] = _balances[to].add(amount);

_callTokensReceived(operator, address(0), to, amount, userData, operatorData);

emit Minted(operator, to, amount, userData, operatorData);
}

/**
* @dev Send tokens
* @param operator address operator requesting the transfer
* @param from address token holder address
* @param to address recipient address
* @param amount uint256 amount of tokens to transfer
* @param userData bytes extra information provided by the token holder (if any)
* @param operatorData bytes extra information provided by the operator (if any)
*/
function _send(
address operator,
address from,
address to,
uint256 amount,
bytes memory userData,
bytes memory operatorData
)
private
{
require(from != address(0));
require(to != address(0));
require((amount % _granularity) == 0);

_callTokensToSend(operator, from, to, amount, userData, operatorData);

// Update state variables
_balances[from] = _balances[from].sub(amount);
_balances[to] = _balances[to].add(amount);

_callTokensReceived(operator, from, to, amount, userData, operatorData);
nventuro marked this conversation as resolved.
Show resolved Hide resolved

emit Sent(operator, from, to, amount, userData, operatorData);
}

/**
* @dev Burn tokens
* @param operator address operator requesting the operation
* @param from address token holder address
* @param amount uint256 amount of tokens to burn
* @param data bytes extra information provided by the token holder
* @param operatorData bytes extra information provided by the operator (if any)
*/
function _burn(
address operator,
address from,
uint256 amount,
bytes memory data,
bytes memory operatorData
)
private
{
require(from != address(0));
require((amount % _granularity) == 0);

_callTokensToSend(operator, from, address(0), amount, data, operatorData);

// Update state variables
_totalSupply = _totalSupply.sub(amount);
_balances[from] = _balances[from].sub(amount);

emit Burned(operator, from, amount, data, operatorData);
}

/**
* @dev Call from.tokensToSend() if the interface is registered
* @param operator address operator requesting the transfer
* @param from address token holder address
* @param to address recipient address
* @param amount uint256 amount of tokens to transfer
* @param userData bytes extra information provided by the token holder (if any)
* @param operatorData bytes extra information provided by the operator (if any)
*/
function _callTokensToSend(
address operator,
address from,
address to,
uint256 amount,
bytes memory userData,
bytes memory operatorData
)
private
{
address implementer = _erc1820.getInterfaceImplementer(from, TOKENS_SENDER_INTERFACE_HASH);
if (implementer != address(0)) {
IERC777Sender(implementer).tokensToSend(operator, from, to, amount, userData, operatorData);
}
}

/**
* @dev Call to.tokensReceived() if the interface is registered. Reverts if the recipient is a contract but
* tokensReceived() was not registered for the recipient
* @param operator address operator requesting the transfer
* @param from address token holder address
* @param to address recipient address
* @param amount uint256 amount of tokens to transfer
* @param userData bytes extra information provided by the token holder (if any)
* @param operatorData bytes extra information provided by the operator (if any)
*/
function _callTokensReceived(
address operator,
address from,
address to,
uint256 amount,
bytes memory userData,
bytes memory operatorData
)
private
{
address implementer = _erc1820.getInterfaceImplementer(to, TOKENS_RECIPIENT_INTERFACE_HASH);
if (implementer != address(0)) {
IERC777Recipient(implementer).tokensReceived(operator, from, to, amount, userData, operatorData);
} else {
require(!to.isContract());
}
}
}
Loading