-
Notifications
You must be signed in to change notification settings - Fork 54
Moves burnCallback default implementation to the Balance interface
#178
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
Conversation
Bumps [golang.org/x/crypto](https://github.com/golang/crypto) from 0.31.0 to 0.35.0. - [Commits](golang/crypto@v0.31.0...v0.35.0) --- updated-dependencies: - dependency-name: golang.org/x/crypto dependency-version: 0.35.0 dependency-type: indirect ... Signed-off-by: dependabot[bot] <[email protected]>
this sounds like a bug in Cadence imo |
|
Yeah, I think that pre-conditions in interfaces should execute regardless of whether there is a default implementation, but I can see both sides of the argument. @turbolent Feel free to explain or link to any docs y'all have |
|
The changes look good to me 👍 To answer the concerns regarding the behaviour: An interface function with only conditions, and no statements, means the conditions are inherited in the implementation function. If an interface functions has statements, then it is considered a default function. If an implementation of the interface (type conforms to the interface) defines a function, then it is used instead of the default function, it overrides the default function. This intended behaviour was unfortunately not correctly implemented in Cadence, and conditions of default functions were included when executing the function in the implementation of the interface. This bug was recently fixed (onflow/cadence#3775, in particular onflow/cadence@6f24819). Existing code might have relied on this wrong behaviour, for example the Fungible Token. Unfortunately, it is not clearly documented what it means to define an interface function that has conditions and statements, neither in the documentation nor in FLIPs, which leads to users assuming certain behaviour – either that conditions are inherited; or they are not, and only apply to the default function. Users might want to both define conditions that should be executed for every implementation, and at the same time also provide a default implementation. One can argue for either behaviour (conditions being inherited or not), and neither is more "correct" than the other. For now we rectified the intended behaviour in the implementation. We will also look into how we can make this intent (providing a default function and also enforcing conditions) less error-prone / a footgun, e.g. by improving documentation, tooling (linting), and considering language improvements. For now, this intent can be best implemented in a Cadence program by having two interfaces, i.e. splitting the function interface function with conditions and statements into two parts: One interface has the conditions that should be inherited in all implementations, and another interface that provides the default function. |
|
thanks @turbolent yeah this is tricky; I think current implementation is the one that can make sense ( considering post conditions ) |
contracts/FungibleToken.cdc
Outdated
| /// The interface that provides a standard field | ||
| /// for representing balance | ||
| /// | ||
| access(all) resource interface Balance { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It feels like this should inherit the Burner.Burnable interface. Not a big deal in the context of Vault implementations, but I'm wondering about other implementations that want a balance (e.g. a fractional NFT or Game resource).
For instance, let's say I have something like an in-game resource that has a balance.
access(all) resource NFT : FungibleToken.Balance, ... {
access(all) var balance: UFix64
...
}The NFT will inherit Balance and therefore the default burnerCallback method. But when the NFT is burned, Burner.burn won't actually execute the burnCallback due to this line in Burner
if let s <- r as? @{Burnable} So in the end, the NFT will just be destroyed and the Balance.burnCallback() won't be called. I think if we're going to include functionality that's explicitly related to the Burnable interface, it should probably be inherited here. But I'm not 100% convicted and I'm curious to hear other thoughts.
Edit: I just saw @turbolent's explanation above which makes sense.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I added the Burnable inheritance to Balance, so this won't be an issue
Description
A recent update to Cadence made it so that pre-conditions in a default implementation do not execute if the implementation is overridden. This moves the default implementation for
burnCallbackto theBalanceinterface so that the pre conditions will always execute.Need to be sure that this upgrade won't have any negative affects:
BalanceinterfaceBalanceinterface, which there probably weren't, then the addition ofburnCallbackand its default implementation will not be a problem because it isaccess(contract), so only theBurnerandFungibleTokencontracts can call it.For contributor use:
masterbranchFiles changedin the Github PR explorer