From 3c593bf8cd1097b793338121997485b32d17cfd4 Mon Sep 17 00:00:00 2001 From: Josh Hannan Date: Fri, 16 Dec 2022 16:12:08 -0600 Subject: [PATCH 1/5] first draft of ftv2 FLIP --- application/20221219-ft-v2.md | 257 ++++++++++++++++++++++++++++++++++ 1 file changed, 257 insertions(+) create mode 100644 application/20221219-ft-v2.md diff --git a/application/20221219-ft-v2.md b/application/20221219-ft-v2.md new file mode 100644 index 00000000..b4f07f94 --- /dev/null +++ b/application/20221219-ft-v2.md @@ -0,0 +1,257 @@ +--- +status: draft +flip: NNN (do not set) +authors: Joshua Hannan (joshua.hannan@dapperlabs.com) +sponsor: Joshua Hannan (joshua.hannan@dapperlabs.com) +updated: 2022-12-19 +--- + +# Fungible Token Standard V2 + +## Objective + +This FLIP proposes multiple updates to the Flow Fungible Token Standard contracts, +primarily about encapsulating functionality within token resources instead of +with the contracts. There are other smaller quality of life changes to the token standard, +such as integration of metadata, improving error handling, +and including a transfer method and interface. +Some of these changes are dependent on other Cadence FLIPs being approved, +primarily [interface inheritance](https://github.com/onflow/flips/pull/40), +removal of nested type requirements, +and [allowing interfaces to emit events](https://github.com/onflow/cadence/issues/2069). + +Most of the changes proposed here would be breaking for all fungible token implementations +on the Flow blockchain, but should not be for third-party integrations such as +event listeners and apps that interface with the contracts. + +## Motivation + +The current fungible token standard for Flow +was designed in mid 2019, at a time when Cadence itself was still being designed. +The current standard, though functional, leaves much to be desired. + +The current token standard uses contract interfaces. +They are designed in a way which requires each concrete contract +to provide exactly one Vault type. +This means that any project that needs multiple tokens must deploy multiple contracts. +In the case of very simple tokens, this is a lot of complexity for very little value. + +Related to this problem, functionality and metadata associated with some tokens, +such as paths, events, and empty vault creation methods, +is only accessible directly through the contract itself, when it should also +be accessible directly through an instance of a token resource and/or interface. + +In the case of events, currently there is no way for the standard to ensure that +implementations are emitting standardized and correct events, which this upgrade will address. + +Additionally, the usage of nested type requirements creates some confusing +interactions, which are not useful and make learning about +and interacting with token smart contracts more difficult. + +## User Benefit + +With these upgrades, users will now be able to: +* Define multiple tokens in a single contract +* Query all the data about a token directly through the token resource and core interfaces +* Have standard events emitted correctly for important operations + +## Design Proposal + +[The original proposal](https://forum.onflow.org/t/streamlined-token-standards-proposal/3075/1) +is on the Flow Forum. +A [pull request with the suggested code changes](https://github.com/onflow/flow-ft/pull/77) +is in the flow fungible token github repository. + +The main code changes and their implications are described here. +The linked proposals provide more context. + +### Move Event Definitions and emissions to resource interfaces + +Instead of requiring events to be defined in the token implementations contracts, +they will only be defined in the fungible token standard smart contract and will +be emitted in post-conditions from the correct methods +in the resource interfaces defined in the standard. + +This feature is still being designed, so there is not a code sample yet. +See https://github.com/onflow/cadence/issues/2069 for potential examples + +### Add Type and Metadata parameters to events + +Standard events contain more information about the FT that is being transferred, +such as the type of the FT and important metadata about the FT. + +Here is an example of what this could look like: + +```cadence +pub event TokensDeposited(amount: UFix64, to: Address?, type: Type, ftView: FungibleTokenMetadataViews.FTView) +``` + +### Include Transferable Interface with transfer method + +For managing simple transfers, tokens can now implement the transferable interface +and the transfer method. + +```cadence + pub resource interface Transferable { + /// Function for a direct transfer instead of having to do a deposit and withdrawal + /// + pub fun transfer(amount: UFix64, recipient: Capability<&{FungibleToken.Receiver}>) + } +``` + +### Add `getAcceptedTypes()` method to `Receiver` interface + +It is useful to be able to query a fungible token receiver to see what types of tokens +it can accept. + +```cadence + pub resource interface Receiver { + + /// deposit takes a Vault and deposits it into the implementing resource type + /// + pub fun deposit(from: @AnyResource{Vault}) + + /// getAcceptedTypes optionally returns a list of vault types that this receiver accepts + pub fun getAcceptedTypes(): {Type: Bool} + } +``` + +### Remove the requirement for a `balance` field in `Vault` + +The requirement to include a `balance` field in Vault implementations is restrictive +because developers may want the vault balance to be a derived field. +This proposal replaces `balance` with a `getBalance()` method. + +```cadence + pub resource interface Balance { + + /// Method to get the balance + /// The balance could be a derived field, + /// so there is no need to require an explicit field + pub fun getBalance(): UFix64 + } +``` + +This field removal may not be worth the change, +because it will break a lot of scripts and transactions that rely +on borrowing the balance interface capability and querying the `balance` field. + +### Move `createEmptyVault()` to inside the Vault definition in addition to the contract + +It is useful to be able to create a new empty vault directly from a Vault object +instead of having to import the contract and call the method from the contract. + +```cadence +pub resource interface Vault { + + /// createEmptyVault allows any user to create a new Vault that has a zero balance + /// + pub fun createEmptyVault(): @AnyResource{Vault} { + post { + result.getBalance() == 0.0: "The newly created Vault must have zero balance" + } + } +} +``` + +### Add Metadata Views methods to the standard + +Metadata Views for fungible tokens should be easily accessible +from interfaces defined by the standard. Unlike the NFT standard though, +there isn't an obvious interface to add the methods to. + +The two options are: + +1. Add the methods `getViews` and `resolveView` to a new metadata interface. +This would be more logical, but it would require all accounts to update their +linked public capabilities to include the metadata interface, which is cumbersome. + +2. Add the methods `getViews` and `resolveView` to the `Balance` interface. +Calling it `Balance` would no longer be completely accurate, but it would not require +any users to update the links in their accounts. This is the option that seems the best. + + +### Drawbacks + +The main drawback of this upgrade is the breaking changes. +It could cause downtime for some projects who aren't prepared to perform the upgrade +right after stable cadence is enabled, +but that applies to any breaking change in stable cadence. +The updates that developers will have to do are fairly straightforward +and will not require much work. + +Please share any other drawbacks that you may discover with these changes. + +### Alternatives Considered + +1. Keep the standard the same: + * If nested type requirements are removed, this may not be possible + * This would avoid the breaking changes, which would be nice in the short term, but would not be setting up cadence developers for success in the long term. + +### Performance Implications + +All of the methods in the fungible token interface are expected to be O(1), +so there is no performance requirements to enforce with the methods. + +Adding metadata parameters to the standard events could cause event payloads +to be quite large if the metadata contained in the events is large. +We don't believe this will be a significant problem though. + +### Dependencies + +* Adds no new dependencies +* Dependent projects + * All fungible tokens on Flow and some projects that utilize them, + including but not limited to: + * flow-ft + * flow-core-contracts + * NFT Storefront + * kitty items + * usdc, fusd, blocto token, incrementfi, duc, etc.... + +### Engineering Impact + +* Build and test time will stay the same. they are relatively small changes. +* The Flow smart contract engineering team will maintain the code +* The code can be tested on its own once a compatible version of cadence is released. + +### Best Practices + +* Some of the changes illustrate Cadence best practices, such as encapsulating functionality +within resources, avoiding public fields, and giving developers flexibility to write composable code. + +### Tutorials and Examples + +* Coming soon once the proposal reaches a more final state + +### Compatibility + +* FCL, emulator, and other such tools should not be affected besides potentially +having to update standard transactions if they aren't compatible. + +### User Impact + +* The upgrade will go out at the same time as stable cadence if approved + +## Related Issues + +### Scoped Providers + +A critical piece of tooling for fungible tokens would be a struct that contains +a provider capability but restricts the capability to only be able to withdraw +a specified amount of tokens from the underlying vault. +Currently, providers have no limit, but all tokens should be able +to create scoped providers. + +This feature is out of the scope of this proposal, but should definitely be a standard +that lives alongside the main fungible token standard. +We hope to shepard a proposal for these soon. + +## Prior Art + +In combination with the upgrades to the NFT standard, we'd like for users to +be able to utilize more sophisticated functionality in their tokens, such as +what was enabled with an upgrade like ERC-1155 and other such upgrades in Ethereum. +We would greatly appreciate if any developers with ethereum experience could think +about these upgrades from the perspective of being able to create the same kinds +of projects that are possible with other token standards in other languages. \ No newline at end of file From 8e17a4fe1dff4242b52b37e1ac8a7f3e4b198409 Mon Sep 17 00:00:00 2001 From: Josh Hannan Date: Fri, 26 Jan 2024 14:10:01 -0600 Subject: [PATCH 2/5] update FLIP to final design --- application/20221219-ft-v2.md | 165 +++++++++++++++++----------------- 1 file changed, 85 insertions(+), 80 deletions(-) diff --git a/application/20221219-ft-v2.md b/application/20221219-ft-v2.md index b4f07f94..448147e6 100644 --- a/application/20221219-ft-v2.md +++ b/application/20221219-ft-v2.md @@ -3,26 +3,30 @@ status: draft flip: NNN (do not set) authors: Joshua Hannan (joshua.hannan@dapperlabs.com) sponsor: Joshua Hannan (joshua.hannan@dapperlabs.com) -updated: 2022-12-19 +updated: 2024-01-26 --- # Fungible Token Standard V2 ## Objective -This FLIP proposes multiple updates to the Flow Fungible Token Standard contracts, -primarily about encapsulating functionality within token resources instead of -with the contracts. There are other smaller quality of life changes to the token standard, -such as integration of metadata, improving error handling, -and including a transfer method and interface. -Some of these changes are dependent on other Cadence FLIPs being approved, +This FLIP proposes multiple updates to the Flow Fungible Token +Standard contracts as part of the Cadence 1.0 upgrade, +primarily about adding support for defining multiple token types +in one contract, adding standard events, integrating `ViewResolver` +and adding the `Burner` utility smart contract. + +Some of these changes are dependent on other Cadence FLIPs that +made fundamental changes to the language +that have been approved and implemented, primarily [interface inheritance](https://github.com/onflow/flips/pull/40), removal of nested type requirements, +[removal of custom destructors](https://github.com/onflow/flips/blob/main/cadence/20230811-destructor-removal.md), and [allowing interfaces to emit events](https://github.com/onflow/cadence/issues/2069). -Most of the changes proposed here would be breaking for all fungible token implementations -on the Flow blockchain, but should not be for third-party integrations such as -event listeners and apps that interface with the contracts. +The changes proposed here will be breaking for all fungible token implementations +on the Flow blockchain, as well as contracts that utilize tokens based on the standard, +third-party integrations such as event listeners, and apps that interface with the contracts. ## Motivation @@ -30,30 +34,30 @@ The current fungible token standard for Flow was designed in mid 2019, at a time when Cadence itself was still being designed. The current standard, though functional, leaves much to be desired. -The current token standard uses contract interfaces. -They are designed in a way which requires each concrete contract +The current token standard uses contract interfaces and nested type requirements. +They are designed in a way that requires each concrete contract to provide exactly one Vault type. This means that any project that needs multiple tokens must deploy multiple contracts. In the case of very simple tokens, this is a lot of complexity for very little value. Related to this problem, functionality and metadata associated with some tokens, -such as paths, events, and empty vault creation methods, +such as paths and empty vault creation methods is only accessible directly through the contract itself, when it should also be accessible directly through an instance of a token resource and/or interface. +Many contracts also do not implement MetadataViews properly because +there is no requirement for it in the standard. + In the case of events, currently there is no way for the standard to ensure that implementations are emitting standardized and correct events, which this upgrade will address. -Additionally, the usage of nested type requirements creates some confusing -interactions, which are not useful and make learning about -and interacting with token smart contracts more difficult. - ## User Benefit With these upgrades, users will now be able to: -* Define multiple tokens in a single contract -* Query all the data about a token directly through the token resource and core interfaces -* Have standard events emitted correctly for important operations +* Define multiple tokens in a single contract. +* Query all the data about a token directly through the contract as well as the `Vault` resource and core interfaces. +* Have standard events emitted correctly for important operations (`Withdrawn`, `Deposited`, and `Burned`) +* Define a `burnCallback()` method that effectively serves as a custom destructor if used in conjunction with the `Burner` contract. ## Design Proposal @@ -63,40 +67,57 @@ A [pull request with the suggested code changes](https://github.com/onflow/flow- is in the flow fungible token github repository. The main code changes and their implications are described here. -The linked proposals provide more context. +The linked proposals provide more context, but the original forum post is somewhat out of date. ### Move Event Definitions and emissions to resource interfaces Instead of requiring events to be defined in the token implementations contracts, -they will only be defined in the fungible token standard smart contract and will -be emitted in post-conditions from the correct methods -in the resource interfaces defined in the standard. - -This feature is still being designed, so there is not a code sample yet. -See https://github.com/onflow/cadence/issues/2069 for potential examples +they will only be [defined in the fungible token standard smart contract](https://github.com/onflow/flow-ft/blob/v2-standard/contracts/FungibleToken.cdc#L50) and will +be [emitted in post-conditions](https://github.com/onflow/flow-ft/blob/v2-standard/contracts/FungibleToken.cdc#L100) +from the correct methods in the resource interfaces defined in the standard. -### Add Type and Metadata parameters to events +Ex: -Standard events contain more information about the FT that is being transferred, -such as the type of the FT and important metadata about the FT. +```cadence +access(all) contract interface FungibleToken { + /// The event that is emitted when tokens are withdrawn from a Vault + access(all) event Withdrawn(type: String, amount: UFix64, from: Address?, fromUUID: UInt64, withdrawnUUID: UInt64) + + access(all) resource interface Provider { + access(Withdraw) fun withdraw(amount: UFix64): @{Vault} { + post { + // Emit directly in the interface instead of the implementation + emit Withdrawn(type: self.getType().identifier, amount: amount, from: self.owner?.address, fromUUID: self.uuid, withdrawnUUID: result.uuid) + } + } + } +} +``` -Here is an example of what this could look like: +This means that the `FungibleToken` events will be the source of truth from now on, +though their types will still contain the name of the implementing contract +when they are emitted, such as: ```cadence -pub event TokensDeposited(amount: UFix64, to: Address?, type: Type, ftView: FungibleTokenMetadataViews.FTView) +A.0x1654653399040a61.FlowToken.Withdrawn ``` -### Include Transferable Interface with transfer method +Therefore, all Fungible Token implementations can and probably should remove their +own `TokensWithdrawn` and `TokensDeposited` events, as they are now redundant. + + +### Add Type, Metadata, and UUID parameters to events -For managing simple transfers, tokens can now implement the transferable interface -and the transfer method. +Standard events contain more information about the FT that is being transferred, +such as the type of the FT and important metadata about the FT. +This includes UUIDs of the vaults involved in the token movements +in case this information is useful. + +Here is an example of the proposal: ```cadence - pub resource interface Transferable { - /// Function for a direct transfer instead of having to do a deposit and withdrawal - /// - pub fun transfer(amount: UFix64, recipient: Capability<&{FungibleToken.Receiver}>) - } +/// The event that is emitted when tokens are withdrawn from a Vault +access(all) event Withdrawn(type: String, amount: UFix64, from: Address?, fromUUID: UInt64, withdrawnUUID: UInt64) ``` ### Add `getAcceptedTypes()` method to `Receiver` interface @@ -107,36 +128,27 @@ it can accept. ```cadence pub resource interface Receiver { - /// deposit takes a Vault and deposits it into the implementing resource type - /// - pub fun deposit(from: @AnyResource{Vault}) + /// getSupportedVaultTypes optionally returns a list of vault types that this receiver accepts + access(all) view fun getSupportedVaultTypes(): {Type: Bool} - /// getAcceptedTypes optionally returns a list of vault types that this receiver accepts - pub fun getAcceptedTypes(): {Type: Bool} + /// Returns whether or not the given type is accepted by the Receiver + /// A vault that can accept any type should just return true by default + access(all) view fun isSupportedVaultType(type: Type): Bool } ``` -### Remove the requirement for a `balance` field in `Vault` +### Add a requirement for a `isAvailableToWithdraw(): Bool` function in `Vault` -The requirement to include a `balance` field in Vault implementations is restrictive -because developers may want the vault balance to be a derived field. -This proposal replaces `balance` with a `getBalance()` method. +Vaults need a way to be able to say if a requested amount of tokens can be withdrawn. +Instead of checking the balance first or risking a panic on a failed withdrawal, +code can call `isAvailableToWithdraw()` to check first to be safe. -```cadence - pub resource interface Balance { +This is especially useful in Fungible Token provider implementations +that have the ability to withdraw from multiple different vaults +because it does not necessarily need to iterate through all the vaults +before finding out if the balance is withdrawable. - /// Method to get the balance - /// The balance could be a derived field, - /// so there is no need to require an explicit field - pub fun getBalance(): UFix64 - } -``` - -This field removal may not be worth the change, -because it will break a lot of scripts and transactions that rely -on borrowing the balance interface capability and querying the `balance` field. - -### Move `createEmptyVault()` to inside the Vault definition in addition to the contract +### Add `createEmptyVault()` inside the Vault definition in addition to the contract It is useful to be able to create a new empty vault directly from a Vault object instead of having to import the contract and call the method from the contract. @@ -157,19 +169,11 @@ pub resource interface Vault { ### Add Metadata Views methods to the standard Metadata Views for fungible tokens should be easily accessible -from interfaces defined by the standard. Unlike the NFT standard though, -there isn't an obvious interface to add the methods to. - -The two options are: - -1. Add the methods `getViews` and `resolveView` to a new metadata interface. -This would be more logical, but it would require all accounts to update their -linked public capabilities to include the metadata interface, which is cumbersome. - -2. Add the methods `getViews` and `resolveView` to the `Balance` interface. -Calling it `Balance` would no longer be completely accurate, but it would not require -any users to update the links in their accounts. This is the option that seems the best. +from interfaces defined by the standard. This proposal enforces +all FungibleToken implementations to also implement `ViewResolver` +and for all Fungible Tokens to implement `ViewResolver.Resolver`. +This way, standard metadata views methods are enforced by default. ### Drawbacks @@ -187,16 +191,17 @@ Please share any other drawbacks that you may discover with these changes. 1. Keep the standard the same: * If nested type requirements are removed, this may not be possible * This would avoid the breaking changes, which would be nice in the short term, but would not be setting up cadence developers for success in the long term. +2. Make `FungibleToken` a contract instead of an interface: + * This would allow the contract to have utility methods to have more fine-grained control + over how standard events are emitted, but would not allow the standard + to enforce that implementations use the `ViewResolver` interface + and have the `createEmptyVault(vaultType: Type)` function. ### Performance Implications All of the methods in the fungible token interface are expected to be O(1), so there is no performance requirements to enforce with the methods. -Adding metadata parameters to the standard events could cause event payloads -to be quite large if the metadata contained in the events is large. -We don't believe this will be a significant problem though. - ### Dependencies * Adds no new dependencies @@ -231,7 +236,7 @@ having to update standard transactions if they aren't compatible. ### User Impact -* The upgrade will go out at the same time as stable cadence if approved +* The upgrade will go out at the same time as Cadence 1.0 (Crescendo) if approved. ## Related Issues From 18bff1eb71e0c885e60a54e315afc301a86640d7 Mon Sep 17 00:00:00 2001 From: Josh Hannan Date: Tue, 20 Feb 2024 09:04:53 -0600 Subject: [PATCH 3/5] change status to implemented --- application/20221219-ft-v2.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/application/20221219-ft-v2.md b/application/20221219-ft-v2.md index 448147e6..f2e74e7b 100644 --- a/application/20221219-ft-v2.md +++ b/application/20221219-ft-v2.md @@ -1,9 +1,9 @@ --- -status: draft -flip: NNN (do not set) -authors: Joshua Hannan (joshua.hannan@dapperlabs.com) -sponsor: Joshua Hannan (joshua.hannan@dapperlabs.com) -updated: 2024-01-26 +status: Implemented +flip: 55 +authors: Joshua Hannan (joshua.hannan@flowfoundation.org) +sponsor: Joshua Hannan (joshua.hannan@flowfoundation.org) +updated: 2024-02-20 --- # Fungible Token Standard V2 From 9b7d85f74619c661d204587e7170f108ede521c0 Mon Sep 17 00:00:00 2001 From: Josh Hannan Date: Fri, 8 Mar 2024 15:46:11 -0600 Subject: [PATCH 4/5] gio's comments --- application/20221219-ft-v2.md | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/application/20221219-ft-v2.md b/application/20221219-ft-v2.md index f2e74e7b..f6bba315 100644 --- a/application/20221219-ft-v2.md +++ b/application/20221219-ft-v2.md @@ -204,7 +204,11 @@ so there is no performance requirements to enforce with the methods. ### Dependencies -* Adds no new dependencies +* Adds the `Burner` contract. + * Contracts won't have to import the `Burner` contract + directly because the conformance is handled by + `FungibleToken.Vault`, so it isn't a direct dependency, + but important to be aware of * Dependent projects * All fungible tokens on Flow and some projects that utilize them, including but not limited to: @@ -227,7 +231,8 @@ within resources, avoiding public fields, and giving developers flexibility to w ### Tutorials and Examples -* Coming soon once the proposal reaches a more final state +* Check out the Cadence 1.0 migration guide for instructions on how to update contracts to the new standards: + * https://cadence-lang.org/docs/cadence_migration_guide/ ### Compatibility @@ -250,7 +255,7 @@ to create scoped providers. This feature is out of the scope of this proposal, but should definitely be a standard that lives alongside the main fungible token standard. -We hope to shepard a proposal for these soon. +We hope to shepherd a proposal for these soon. ## Prior Art From 40e8fc0f823392208cc67bbd1b04b78b5967d1f0 Mon Sep 17 00:00:00 2001 From: Josh Hannan Date: Mon, 11 Mar 2024 11:45:19 -0500 Subject: [PATCH 5/5] update date --- application/20221219-ft-v2.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/application/20221219-ft-v2.md b/application/20221219-ft-v2.md index f6bba315..74900442 100644 --- a/application/20221219-ft-v2.md +++ b/application/20221219-ft-v2.md @@ -3,7 +3,7 @@ status: Implemented flip: 55 authors: Joshua Hannan (joshua.hannan@flowfoundation.org) sponsor: Joshua Hannan (joshua.hannan@flowfoundation.org) -updated: 2024-02-20 +updated: 2024-03-11 --- # Fungible Token Standard V2