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

Allow nonpayable functions to override payable functions #11253

Open
frangio opened this issue Apr 13, 2021 · 23 comments
Open

Allow nonpayable functions to override payable functions #11253

frangio opened this issue Apr 13, 2021 · 23 comments
Labels
language design :rage4: Any changes to the language, e.g. new features low effort There is not much implementation work to be done. The task is very easy or tiny. low impact Changes are not very noticeable or potential benefits are limited. medium difficulty must have eventually Something we consider essential but not enough to prevent us from releasing Solidity 1.0 without it.

Comments

@frangio
Copy link
Contributor

frangio commented Apr 13, 2021

Abstract

If an interface defines a view function, it is possible for a derived contract to provide a pure implementation of it.

Could it be possible to extend this so that payable functions can be implemented with non payable functions? It is the same kind of state mutability narrowing.

Motivation

OpenZeppelin/openzeppelin-contracts#2610

ERC721's transferFrom is defined as payable in the spec. Our standard implementation is not payable, because if it were payable we would have to define what to do with the message value, which isn't standardized so we don't want to define a specific way to handle it.

Because our implementation isn't payable, we've also defined the interface as non-payable, but people have pointed out that 1) this is more restrictive than the spec, and 2) there is no way to extend our implementation (through inheritance) to make it payable and add logic to handle msg.value.

Specification

A nonPayable function should be a valid implementation of a payable function.

A child contract should be able to extend our nonPayable implementation with a payable function to add a way to handle msg.value before forwarding to super.transferFrom.

@fulldecent
Copy link
Contributor

This is a duplicate of #3412. But that issue was apparently closed prematurely, so I'm glad to see this back from the dead.

@chriseth
Copy link
Contributor

I remember that we discussed this, but thinking about it for a minute, I cannot see a reason why this should not be allowed. @ekpyron what do you think?

@ekpyron
Copy link
Member

ekpyron commented May 25, 2021

I remember that we discussed this, but thinking about it for a minute, I cannot see a reason why this should not be allowed. @ekpyron what do you think?

Apparently, I was for allowing it at some point in #3412 (comment) and @axic half-agreed #3412 (comment), but was still sceptical :-). Without thinking about it too long, I also still don't see a reason why not.

@cameel cameel added feature language design :rage4: Any changes to the language, e.g. new features labels Jun 1, 2021
@chriseth chriseth changed the title Allow nonpayable implementation of payable function from interface Allow nonpayable function to override of payable functions Jun 2, 2021
@chriseth chriseth changed the title Allow nonpayable function to override of payable functions Allow nonpayable function to override payable functions Jun 2, 2021
@chriseth chriseth changed the title Allow nonpayable function to override payable functions Allow nonpayable functions to override payable functions Jun 2, 2021
@axic
Copy link
Member

axic commented Jun 2, 2021

Tentatively agreed on today's meeting to allow narrowing from payable to non-payable, but keep widening from non-payable to payable disallowed.

Narrowing is similar to the case of view vs pure, with the difference that the compiler inserts code (the check for msg.value == 0), however that code can be already added manually by the user into payable functions.

Widening on the other hand would mean that the expected behaviour (fail on value) is not enforced.

@axic
Copy link
Member

axic commented Jun 2, 2021

As a non-exhaustive list of auditors, pinging @GNSPS @montyly @Austin-Williams @stbeyer for feedback.

@cameel
Copy link
Member

cameel commented Jun 2, 2021

We also discussed marking it as "good first issue". I'm adding the label with "difficulty: medium". The implementation is simple so it should be doable for someone new but will probably require some guidance.

@frangio
Copy link
Contributor Author

frangio commented Jun 3, 2021

How would it work if I defines a payable function, A is I implements it as non-payable, and C is I, A implements it as payable? Is that considered widening?

My first impression is that this should be allowed. C is declaring that it wants to allow receiving value in that function.

I would agree that C is A should not be able to widen it from non-payable to payable.

@chriseth
Copy link
Contributor

chriseth commented Jun 3, 2021

I would say it's the same as in the case of non-view/view/pure: If a contract strengthens the requirement in the inheritance graph, all derived contracts must also implement the stronger version. In your example, C must implement it as non-payable.

If you declare the function in A as non-payable, then you say that if you are implementing a derived contract that claims to conform to the requirements of A's interface, the function also has to reject all ether transfers.

C is I, A essentially says that C conforms to both I and A.

Why would you say that C is A is different from C is I, A?

@frangio
Copy link
Contributor Author

frangio commented Jun 4, 2021

I see... I think this is due to a different interpretation of what payable and non-payable mean. The situation is not exactly the same as non-view/view/pure, because in that case the default is the least strict (non-view), and with non-payable/payable the default is the most strict (non-payable).

In my mind, payable is the meaningful one and is a way to declare that you want to accept value. But if the compiler sees non-payable as a way to declare that you want to reject value, these are two perspectives that will arrive at different expectations for how overrides should behave.

C is I, A says C conforms to both I and A, but then you could say: a) at least one of I,A wants to accept value so C will accept it, or b) at least one of I,A wants to reject value so C will reject it.

I don't really have a strong opinion here... Although the motivation to open this issue was so that we could have IERC721.transferFrom as payable, then implement the core logic in ERC721.transferFrom as non-payable, while still allowing users to use the core logic and add value-handling logic on top. If this is considered widening and will not be allowed, it won't fix this use case. It may be complicated to allow that scenario in a solid way, so I would understand it.

@montyly
Copy link

montyly commented Jun 10, 2021

What are your opinions if we add a nonPayable attribute, that would be required a compilation to narrow a function shadowed from payable to non-payable?

Because I can see the case of a developer using a library, shadowing a payable function, and forgetting to keep it payable. So having it explicit helps to understand that its the intent of the developper.

So the rules would be something like:

  • default: non-payable
  • payable: only possible if it does not shadow a non-payable function
  • nonPayable: only way to shadow a payable function without being payable (otherwise same behavior as the default)

However, as the current proposal does not solve OZ problem, and I am not aware of any large project that needs this feature, I am not sure it's worth adding complexity here, and we can just keep the existing rules ;)

@fulldecent
Copy link
Contributor

Adding a keyword for nonpayable is not necessary.

Nonpayable is more restrictive than payable and does not introduce dangers. Therefore overriding from payable to nonpayable will not result in people accidentally getting something more dangerous than they expected.


The original issue here is good and should be implemented. Even if "only" OpenZeppelin Contracts and the ERC-721 reference implementation are using this then that should be reason enough to consider this change as impactful.

During the ERC-721 standardization process we identified about 30 language design issues (including this) and most were fixed.

@cameel cameel added good first issue candidate Could be a "good first issue" but something is blocking it or it has open questions. and removed good first issue labels Nov 14, 2021
@cameel cameel added low effort There is not much implementation work to be done. The task is very easy or tiny. low impact Changes are not very noticeable or potential benefits are limited. nice to have We don’t see a good reason not to have it but won’t go out of our way to implement it. labels Sep 27, 2022
@ekpyron ekpyron removed the good first issue candidate Could be a "good first issue" but something is blocking it or it has open questions. label Jan 5, 2023
@github-actions
Copy link

github-actions bot commented Apr 5, 2023

This issue has been marked as stale due to inactivity for the last 90 days.
It will be automatically closed in 7 days.

@github-actions github-actions bot added the stale The issue/PR was marked as stale because it has been open for too long. label Apr 5, 2023
@fulldecent
Copy link
Contributor

unstale

@github-actions github-actions bot removed the stale The issue/PR was marked as stale because it has been open for too long. label Apr 6, 2023
@github-actions
Copy link

github-actions bot commented Jul 5, 2023

This issue has been marked as stale due to inactivity for the last 90 days.
It will be automatically closed in 7 days.

@github-actions github-actions bot added the stale The issue/PR was marked as stale because it has been open for too long. label Jul 5, 2023
@fulldecent
Copy link
Contributor

Bump

@github-actions github-actions bot removed the stale The issue/PR was marked as stale because it has been open for too long. label Jul 6, 2023
@github-actions
Copy link

github-actions bot commented Oct 5, 2023

This issue has been marked as stale due to inactivity for the last 90 days.
It will be automatically closed in 7 days.

@github-actions github-actions bot added the stale The issue/PR was marked as stale because it has been open for too long. label Oct 5, 2023
@fulldecent
Copy link
Contributor

I nominate this issue to stay open.

It is last of the topics written in the text of ERC-721 that has not yet been changed in Solidity.

@github-actions github-actions bot removed the stale The issue/PR was marked as stale because it has been open for too long. label Oct 6, 2023
Copy link

github-actions bot commented Jan 5, 2024

This issue has been marked as stale due to inactivity for the last 90 days.
It will be automatically closed in 7 days.

@github-actions github-actions bot added the stale The issue/PR was marked as stale because it has been open for too long. label Jan 5, 2024
@fulldecent
Copy link
Contributor

unstale

@github-actions github-actions bot removed the stale The issue/PR was marked as stale because it has been open for too long. label Jan 6, 2024
@robdoesstuff
Copy link

Also just ran into this. Would be great to see support for it.

Copy link

This issue has been marked as stale due to inactivity for the last 90 days.
It will be automatically closed in 7 days.

@github-actions github-actions bot added the stale The issue/PR was marked as stale because it has been open for too long. label Apr 13, 2024
@robdoesstuff
Copy link

unstale - let's seriously think about this. Just saw another person run into this issue.

@github-actions github-actions bot removed the stale The issue/PR was marked as stale because it has been open for too long. label Apr 17, 2024
@zjesko
Copy link

zjesko commented Jul 15, 2024

+1 should be unstale, I have also come across such a use case

@ekpyron ekpyron added must have eventually Something we consider essential but not enough to prevent us from releasing Solidity 1.0 without it. and removed nice to have We don’t see a good reason not to have it but won’t go out of our way to implement it. labels Jul 17, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
language design :rage4: Any changes to the language, e.g. new features low effort There is not much implementation work to be done. The task is very easy or tiny. low impact Changes are not very noticeable or potential benefits are limited. medium difficulty must have eventually Something we consider essential but not enough to prevent us from releasing Solidity 1.0 without it.
Projects
None yet
Development

No branches or pull requests

10 participants