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

ERC777.sol: _callTokensToSend should be called after _move, not before, to follow Checks-Effects-Interactions #3463

Closed
lukehutch opened this issue Jun 11, 2022 · 4 comments

Comments

@lukehutch
Copy link

EIP-777 says "The tokensToSend hook MUST be called before the state is updated—i.e. before the balance is decremented."

However, this violates Checks-Effects-Interactions, and introduces a very real risk of vulnerability for any contract that implements ERC777.

In this (now-closed) OpenZeppelin kitchen-sink security issue, this potential vulnerability is discussed, and it was agreed that probably the ERC777 standard should not be followed in this instance: @frangio says, agreeing with @guylando and @nventuro:

#1749 (comment)

Yes, I agree, we're gonna have to change our implementation to update allowances before calling tokensReceived.

However this was never actually changed in the OpenZeppelin implementation of ERC777:

https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC777/ERC777.sol#L376
https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC777/ERC777.sol#L400

This leaves any contract overriding OpenZeppelin's ERC777 implementation potentially vulnerable to exploitation via sender hook.

@frangio
Copy link
Contributor

frangio commented Jun 11, 2022

I'm not sure there is anything we can do about this. The EIP is very clear about the order in which things should be done. And other smart contracts are written with this assumption in mind, e.g.:

// If _asset is ERC777, `transferFrom` can trigger a reenterancy BEFORE the transfer happens through the
// `tokensToSend` hook. On the other hand, the `tokenReceived` hook, that is triggered after the transfer,
// calls the vault, which is assumed not malicious.
//
// Conclusion: we need to do the transfer before we mint so that any reentrancy would happen before the
// assets are transfered and before the shares are minted, which is a valid state.

Honestly at this point the best option might be #2620.

@mpereiraesaa
Copy link

mpereiraesaa commented Jun 11, 2022

Modifying the implementation of an EIP basically breaking some standard is never the way to go even if that way we might fix some bad behavior. In any case it can be avoided or deprecated like suggested by @frangio .

We should always write smart contracts understanding the risks of some standards while trying to integrate a token.

@yashdeharia
Copy link

yashdeharia commented Jun 15, 2022

@mpereiraesaa ... you are right

@lukehutch
Copy link
Author

lukehutch commented Jun 15, 2022

@mpereiraesaa @frangio It looks to me that ERC1363 and ERC4524 are much better (simpler and more robust) standards. Maybe OpenZeppelin should replace the ERC777 implementation with those? I'll close this bug and move the discussion to #2620, thanks.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants