Code Monkey home page Code Monkey logo

erc777's People

Contributors

0xjac avatar antontranelis avatar jbaylina avatar mikgross avatar ssteiger avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

erc777's Issues

Migration error in ReferenceToken constructor

What

Setting up a mock migration in truffle suite to deploy the ReferenceToken (on 0.4.24) results in a Deployment Failed error.

Deploying 'ReferenceToken'
   --------------------------
Error:  *** Deployment Failed ***

"ReferenceToken" hit a require or revert statement somewhere in its constructor. Try:
   * Verifying that your constructor params satisfy all require conditions.
   * Adding reason strings to your require statements.

Why

This is due to the doMint function in the constructor which calls the following line
callRecipient(msg.sender, 0x0, _tokenHolder, _amount, "", _operatorData, true);

As preventLocking is set to true here, only contracts who have implemented ERC777TokensRecipient (on 820 client) can receive the tokens.

Solutions?

  1. Remove requirement for token mint recipient to implement interface
  2. Pass an extra variable to doMint from the constructor to tell it to set preventLocking == false
  3. Set interface implementation of ERC777TokensRecipient

removed gas loops, does not deploy due "This contract does not implement all functions and thus cannot be created."

hi
777 gets roughly 27 static warnings in remix.
you can remove 12 of them by defining a boundry on bytes (at line 252 of this contract: https://gist.github.com/lrgeoemtry/df096412147a1859bae07cc31f51b267) of "bytes32"

doing this though makes it so i cannot deploy the contract getting this error:
"This contract does not implement all functions and thus cannot be created."
I'm going to keep hacking away at it, but im hopeful the community can help.

the other 15 static analysis warnings are 2 reentrancy issues which we modified with mutex (492-505 & 528-509)
and "similar variable names" which is readability deprecation but w/e

any help with this is much appreciated as currently sending a 777 token is like .1 rEth and as expensive as hell.

Implement `revokeAllOperators`

For convenience and security reasons, a new function should be added:

function revokeAllOperators() public;

This method revokes all the operators for msg.sender.

Use pragma solidity ^

The contracts cannot currently compile when using a derived contract with pragma solidity ^0.4.24 because of the version difference and lack of the ^ in a few base contracts.

I see you are updating to solidity 0.4.24 in the cleanup branch which is very much welcome.

However, in that branch there are a few files that specify "pragma solidity 0.4.24" without the ^ carrot. Shouldn't these be "pragma solidity ^0.4.24" so that derived contracts can compile ERC777 these on future solidity versions after 0.4.24 when they come out?

Inconsistency with the standard for `operatorSend` and `operatorBurn`

There is a discrepancy between the standard and the reference implementation regarding the operatorSend and operatorBurn functions:

The current standard (https://github.com/ethereum/EIPs/blob/b41669ede219b9599a6a1c637e578b8a444ad6b1/EIPS/eip-777.md):

The value of from MAY be 0x0, then the from (token holder) used for the send MUST be msg.sender (the operator).

The current reference implementation of both operatorSend and operatorBurn:

https://github.com/jacquesd/ERC777/blob/896d077eb4b1beb0fc2258f9d4dff4b60478a479/contracts/ERC777BaseToken.sol#L118-L121

https://github.com/jacquesd/ERC777/blob/896d077eb4b1beb0fc2258f9d4dff4b60478a479/contracts/ERC777BaseToken.sol#L127-L130

Add delegate & authorizer feature

Delegate & Authorizer

A delegate is an address which has been authorized to send the tokens of a principal address. When calling send on the token contract from the delegate, the tokens will be sent from the principal account not the delegate. In addition, each send involving a delegate will be explicitly authorized by an authorizer contract.

Delegate/Authorizer vs Operator

There is a key difference between an operator and a delegate.

To send tokens with an operator, the operator must call operatorSend on the token contract. This should either be done automatically as a result of some action or by calling a function directly on the operator.

To send tokens with a delegate, the delegate only needs to call send on the token contract. The token contract will figure out on his own that send was called from a delegate and first call the authorizer contract for authorization, then send from the principal address being represented by the delegate. This allows for checks to be used with wallets which just implement send.

Delegate/Authorizer vs tokensToSend

tokensToSend is registered via EIP-820 together with tokensReceived. This means that the same function is called for all ERC-777 tokens. This is fine for tokensReceived to notify the recipient and prevent locking because the logic is the same for all tokens.

This is not true for tokensToSend which is aimed at checking whether a send is authorized or not. In this case the logic may not be the same for all tokens. Maybe a specific tokens should be rate-limited, an other one should be limited to be sent to a particular address and so on. Because there is only a single tokensToSend function for all tokens, this function must be modified and the contract redeployed for every new tokens which must be handled.

Authorizers on the other hand are registered directly on the token contract and are only applied for the specific token. This keeps the logic simple and prevent constant modification and redeployment of the contract.

Another advantage of delegates is that they use a separate account than the principal account. In the case of tokensToSend, since it is deployed for the account via EIP-820, the same key used to send tokens can also unregister the tokensToSend, essentially making the check a self-imposed voluntary check which can be bypassed at any time.

Delegate is a separate address, tied to an authorizer; but only the principal account key has the power to change the authorizer. This means that the delegate can never bypass the checks.

Use Cases

Debit card

Consider a principal account holding a large amount of tokens. The owner of the account must carry the private key with him at all time to use his tokens which may be dangerous.

A delegate with a daily quota could be authorized to send some of the tokens. The owner could carry this delegate on his phone and use his phone for limited transactions on a daily basis, the same way a debit card works. And when the owner needs to do an occasional large transaction, he can use the principal private key which is stored securely somewhere less practical to access (for example on a hardware wallet in a safe.).

Cold to hot wallet transfer

Consider a cold wallet holding a large amount of tokens and a second hot wallet used for daily transactions.

The cold wallet can have a delegate which is only authorized to send to the hot wallet. The key of the cold wallet is stored securely and almost never used. The delegate is used to replenish the hot wallet from the cold wallet.

In addition backups of the delegate key can be given to trusted entities for safe keeping. This way even if the cold wallet key is lost, the delegate key can be used to recover the funds.

And if the delegate key is compromised the only thing an attacker can do is send the tokens from the cold to the hot wallet. This is a bit safer than entrusting the key of the cold wallet directly.

Specifications

IAuthorizer

The authorizer must be a contract implementing the following interface:

interface IAuthorizer {
  function authorizeSend(address to, uint256 value, bytes userData) public;
}
authorize
function authorizeSend(address to, uint256 value, bytes userData) public

Authorizes a send for the from address being represented by the delegate.

To prevent the send, the function MUST throw.

parameters

  • to: tokens recipient
  • value: amount of tokens transferred
  • userData: information attached to the transaction by the sender

Ierc777 (Token Contract)

In addition, the Ierc777 interface must be extended to add the following methods and events:

Methods

function authorizeDelegate(address delegate, address authorizer) public;
function revokeDelegate(address delegate) public;
function delegateFor(address delegate) public constant returns (address);
function authorizerOf(address delegate) public constant returns (address);

event AuthorizedDelegate(address indexed delegate, address indexed authorizer, address indexed tokenHolder);
event RevokedDelegate(address indexed delegate, address indexed authorizer, address indexed tokenHolder);
authorizeDelegate
function authorizeDelegate(address delegate, address authorizer) public

Authorize a third party delegate to send msg.sender's tokens upon authorization of authorizer.

The function MUST throw if:

  • delegate is msg.sender
  • delegate is already a delegate for another principal
  • delegate holds tokens (non-zero balance)
  • authorizer is an operator
  • authorizer is already an authorizer for some delegate

NOTE: While authorizer MUST be a contract, there is no requirement to check it when authorizing the delegate and authorizer. When sending tokens however the authorizeSend function of the authorizer MUST be called, which will result in a throw if authorizer is not a contract or does not implement Authorizer.

parameters

  • delegate: Address that will to be authorized to send msg.sender's tokens.
  • authorizer: Contract which will authorize _delegate to send tokens.

revokeDelegate
function revokeDelegate(address delegate) public

Revoke a third party _delegate's rights to send msg.sender's tokens.

The function MUST throw if:

  • delegate is not the delegate of msg.sender.

NOTE: While the authorizer does not need to be specified when revoking, internally the authorizer MUST also be revoked (i.e. not marked as the authorizer for delegate ).

parameters

  • delegate: Address of the delegate for msg.sender.

TODO Improvement: Since, there can be only one delegate per account, a two-way mapping of the delegate could be kept such that msg.sender does not need to specify the delegate when revoking


delegateFor
function delegateFor(address delegate) public constant returns (address)

Get the address for which delegate is authorized to send tokens or 0x0 if delegate is not a delegate.

parameters

  • delegate: Address for which to return the principal address.

returns: Address for which _delegate is authorized to send tokens or 0x0 if _delegate is not a delegate.


authorizerOf
function authorizerOf(address _delegate) public constant returns (address)

Get the address for which delegate is authorized to send tokens or 0x0 if delegate is not a delegate.

parameters

  • delegate: Delegate address for which to return the authorizer contract address.

returns: Authorizer contract address of _delegate or 0x0 if _delegate is not a delegate.


Events

AuthorizedDelegate
event AuthorizedDelegate(address indexed delegate, address indexed authorizer, address indexed tokenHolder)

Indicate that the delegate address is representing tokenHolder and send its token (i.e. is a delegate for tokenHolder).

This event MUST be fired on a successful call to authorizeDelegate.

parameters

  • delegate: Address which is now representing tokenHolder.
  • authorizer: Contract which will authorize _delegate to send tokens.
  • tokenHolder: address being represented by delegate.

RevokedDelegate
event RevokedDelegate(address indexed delegate, address indexed authorizer, address indexed tokenHolder)

Indicate that the delegate address is not representing tokenHolder anymore and cannot send its tokens (i.e. is not delegate for tokenHolder).

This event MUST be fired on a successful call to revokeDelegate.

parameters

  • delegate: Address which is not representing tokenHolder anymore.
  • authorizer: Contract which will authorize _delegate to send tokens.
  • tokenHolder: address not being represented by delegate anymore.


Furthermore the send', 'operatorSend', 'transfer' and 'transferFrom method specification as well as the Burned event specification must be extended with the following constraint:

If from is a delegate, then from.authorize MUST be called and pass the correct values for to, value and userData.

NOTE: While intuitively the authorize call happens before sending the tokens, in the implementation, this call SHOULD be after the update of balances and before the call to tokensReceived to respect the Checks-Effects-Interactions (CEI) pattern and prevent potential reentry attacks.

When from is a delegate, the tokensReceived, Sent and Burnt must be called with the true from (i.e. the token holder being represented by the delegate) not the delegate itself. Delegates are transparent from the point of view of the recipient and MUST NOT be communicated.

Add Docs for the Reference Token

The documentation in the ReferenceToken should be generated into a nice HTML page for devs to more easily understand the code.

Add `tokensToSend` Callback

interface ERC777TokensSender {
    function tokensToSend(
        address operator,
        address from,
        address to,
        uint value,
        bytes userData,
        bytes operatorData
    ) public;
}

This proposal adds the above interface which must be registered via EIP-820, similar to tokensReceived.
The difference from tokensReceived is that tokensToSend is called for the from address before sending the tokens. This allows the token holder to perform a a final check, logging or some other action before sending the tokens.

Clarify ERC20 compatiblity

Be more precise in the spec about ERC20 Compatibility and define the use of the erc20compatible boolean

Inconsistency with the standard for `burn` and `operatorBurn`

There is a discrepancy between the standard and the reference implementation regarding the burn and operatorBurn functions:

The current standard (https://github.com/ethereum/EIPs/blob/b41669ede219b9599a6a1c637e578b8a444ad6b1/EIPS/eip-777.md) says:

The data field of the tokensToSend hook MUST be empty.

But, the current reference implementation doesn't check (or enforce) the emptiness (of _holderData):

https://github.com/jacquesd/ERC777/blob/896d077eb4b1beb0fc2258f9d4dff4b60478a479/contracts/ERC777BaseToken.sol#L202

Organise and add more tests

All tests are located in a single file (test/ReferenceToken-test.js) which is growing. It is becoming hard to read each test case.

The tests should be split in different files, for example:

  • Base Tests (Read only functions: name, symbol, ...)
  • Send/Receive Tests
  • Operator Tests
  • ERC-20 Compatibility Tests

Should truffle be moved into dev-dependencies?

Current truffle that is referenced in dependencies has vulnerabilites:

┌───────────────┬──────────────────────────────────────────────────────────────┐
│ Critical      │ Command Injection                                            │
├───────────────┼──────────────────────────────────────────────────────────────┤
│ Package       │ growl                                                        │
├───────────────┼──────────────────────────────────────────────────────────────┤
│ Patched in    │ >=1.10.2                                                     │
├───────────────┼──────────────────────────────────────────────────────────────┤
│ Dependency of │ erc777                                                       │
├───────────────┼──────────────────────────────────────────────────────────────┤
│ Path          │ erc777 > truffle > mocha > growl                             │
├───────────────┼──────────────────────────────────────────────────────────────┤
│ More info     │ https://nodesecurity.io/advisories/146                       │
└───────────────┴──────────────────────────────────────────────────────────────┘
┌───────────────┬──────────────────────────────────────────────────────────────┐
│ Low           │ Regular Expression Denial of Service                         │
├───────────────┼──────────────────────────────────────────────────────────────┤
│ Package       │ debug                                                        │
├───────────────┼──────────────────────────────────────────────────────────────┤
│ Patched in    │ >= 2.6.9 < 3.0.0 || >= 3.1.0                                 │
├───────────────┼──────────────────────────────────────────────────────────────┤
│ Dependency of │ erc777                                                       │
├───────────────┼──────────────────────────────────────────────────────────────┤
│ Path          │ erc777 > truffle > mocha > debug                             │
├───────────────┼──────────────────────────────────────────────────────────────┤
│ More info     │ https://nodesecurity.io/advisories/534                       │
└───────────────┴──────────────────────────────────────────────────────────────┘

This leads to false positives while auditing projects which are using erc777 as dependency in npm even if their own truffle is updated.

Mint function calls `callRecipient` while burn doesn't

https://github.com/jacquesd/eip777/blob/master/contracts/ReferenceToken.sol#L140

    function mint(address _tokenHolder, uint256 _amount, bytes _operatorData) public onlyOwner {
        requireMultiple(_amount);
        mTotalSupply = mTotalSupply.add(_amount);
        mBalances[_tokenHolder] = mBalances[_tokenHolder].add(_amount);

        callRecipient(msg.sender, 0x0, _tokenHolder, _amount, "", _operatorData, true);

        Minted(msg.sender, _tokenHolder, _amount, _operatorData);
        if (mErc20compatible) { Transfer(0x0, _tokenHolder, _amount); }
    }

burn:

    function burn(address _tokenHolder, uint256 _amount, bytes _userData, bytes _operatorData) public onlyOwner {
        requireMultiple(_amount);
        require(balanceOf(_tokenHolder) >= _amount);

        mBalances[_tokenHolder] = mBalances[_tokenHolder].sub(_amount);
        mTotalSupply = mTotalSupply.sub(_amount);

        Burned(msg.sender, _tokenHolder, _amount, _userData, _operatorData);
        if (mErc20compatible) { Transfer(_tokenHolder, 0x0, _amount); }
    }
  1. If you still need it, why burn method doesn't call callRecipient if _operatorData is provided?

I believe mint/burn function has to provide 2 methods with different params(overloaded function):
Traditional way without bytes _data
and new way with bytes _data (overloaded function)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.