status | flip | authors | updated |
---|---|---|---|
accepted |
69 |
Satyam Agrawal ([email protected]) |
2023-02-06 |
The purpose of this proposal is to enable the Fungible Token (FT) Receiver
s to return a list of vault types that they can accept.
This would enhance the discoverability of FT vault types and reduce the likelihood of unintended failures in the deposit method because the deposited vault is not supported.
This is because Cadence code trying to perform a deposit would now be able to query the acceptable vault types before making a deposit.
The Fungible Token Standard mandates that a single Vault can only receive one type of token,
meaning that a Flow Vault can only receive Flow tokens and a FUSD Vault can only receive FUSD tokens.
The FungibleToken.Receiver
interface, on the other hand, is designed to be able to accept any Vault type in its deposit
method.
Currently, there is no programmatic way to determine the type of token that a Receiver
is able receive, leading to the possibility of a failed deposit due to an incorrect assumption about the Vault that the Receiver
is linked to.
To address this issue, a new proxy receiver, the FungibleTokenSwitchboard
, was created to allow for the receipt of multiple Vault types through a single FungibleToken.Receiver
capability.
The SwitchboardPublic
interface also includes the getVaultTypes
function, which solves the problem of knowing what vaults the receiver accepts, but only for the switchboard and only for code that is aware of the switchboard.
This solution is not complete. A {FungibleToken.Receiver}
capability can represent any type of receiver such the FungibleTokenSwitchboard
, a standard receiver, or any other custom Receiver
.
As the Flow ecosystem expands, the number and complexity of proxy receivers developed to solve various problems is likely to increase.
The current FungibleToken.Receiver
interface only contains the deposit
method. It does not offer a clear-cut method for knowing the Vault
types that a particular receiver is able to receive.
This issue is compounded by the fact that proxy receivers, which implement the FungibleToken.Receiver
interface, can also return Capability<&{FungibleToken.Receiver}>
.
As a result, the true nature of the receiver, whether it be derived from the standard Vault or a proxy receiver, remains ambiguous.
Adding type information to Receiver
capabilities has the potential to streamline the FT receiving process and minimize transaction failures.
This is because developers now have the ability to programmatically verify the compatibility between the given receiver capability and the intended Vault type,
allowing them to take proactive measures accordingly such as depositing the tokens elsewhere instead of the transaction just failing.
The essence of this proposal lies in modifying the FungibleToken.Receiver
interface to include a method to return the expected Vault
types that it can receive and include a default implementation so that the upgrade will not be breaking for existing fungible tokens.
So new Receiver
interface would look like this -
/// The interface that enforces the requirements for depositing
/// tokens into the implementing type.
///
/// We do not include a condition that checks the balance because
/// we want to give users the ability to make custom receivers that
/// can do custom things with the tokens, like split them up and
/// send them to different places.
///
pub resource interface Receiver {
/// Takes a Vault and deposits it into the implementing resource type
///
/// @param from: The Vault resource containing the funds that will be deposited
///
pub fun deposit(from: @Vault)
/// Returns the type of implementing resource i.e If `FlowToken.Vault` implements
/// this then it would return `{Type<@FlowToken.Vault>(): true}` and if any custom receiver
/// uses the default implementation then it would return empty dictionary as its parent
/// resource doesn't conform with the `FungibleToken.Vault` resource.
///
/// @return Dictionary of supported vault types by the implemented resource.
///
pub fun getSupportedVaultTypes() :{Type: Bool} {
// Below check is implemented to make sure that run-time type would
// only get returned when the parent resource conforms with `FungibleToken.Vault`.
if self.getType().isSubtype(of: Type<@FungibleToken.Vault>()) {
return {self.getType(): true}
} else {
// Return empty dictionary as the default value for the resource who don't
// implements `FungibleToken.Vault`, such as `FungibleTokenSwitchboard`, `TokenForwarder` etc.
return {}
}
}
}
while the existing interface can be accessed here.
The proposed interface features a default implementation of the getSupportedVaultTypes
function, ensuring that existing FTs will not encounter any breakage and will have a ready-made implementation available. This default implementation also eliminates the need for upgrading existing FTs.
Implementation of proposed Receiver
type for FungibleTokenSwitchboard
contract.
/// The resource that stores the multiple fungible token receiver
/// capabilities, allowing the owner to add and remove them and anyone to
/// deposit any fungible token among the available types.
///
pub resource Switchboard: FungibleToken.Receiver, SwitchboardPublic {
/// Dictionary holding the fungible token receiver capabilities,
/// indexed by the fungible token vault type.
///
access(contract) var receiverCapabilities: {Type: Capability<&{FungibleToken.Receiver}>}
// ...
// snip
// ...
/// A getter function to know which tokens a certain switchboard
/// resource is prepared to receive.
///
/// @return The keys from the dictionary of stored
/// `{FungibleToken.Receiver}` capabilities that can be effectively
/// borrowed.
///
pub fun getSupportedVaultTypes(): {Type: Bool} {
let supportedTypes: {Type: Bool} = []
for vaultType in self.receiverCapabilities.keys {
if self.receiverCapabilities[vaultType]!.check() {
supportedTypes.insert(key: vaultType, true)
}
}
return supportedTypes
}
}
There are no equivalent alternatives to the proposed solution. While some options such as implementing the PaymentHandler
exist, they are overly complex and do not offer the same level of flexibility, comprehensiveness, and ease of comprehension as the proposed solution.
There is no real performance implications on proposed solution.
This proposal builds on the existing FT interface.
Adhering to this standard will require contract authors to implement a new methods as part of their FT. However, default implementation covers the need of all FTs.
Once the proposed solution is adopted then it is recommended to validate vault type before transferring the FTs from one account to another.
All the custom receiver contracts owners will need to update them to add implementations for the new methods.
This feature can be rolled out with no fear of changes to the user.