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

FLIP 242: Improve Public Capability Acquisition #242

Merged
merged 6 commits into from
Apr 24, 2024
Merged
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
66 changes: 66 additions & 0 deletions cadence/20240123-capcon-get-capability-api-improvement.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
---
status: draft
flip: 242
authors: Austin Kline ([email protected])
sponsor: Bastian Müller
updated: 2024-01-23
---

# FLIP 242: Improve Public Capability Acquisition

## Objective

This FLIP proposes some improvements to the way that public capabilities are obtained with the new Capability Controllers API when no capability is published under the requested path.

## Motivation

When Cadence 1.0 goes live, Capability Controllers will be the new way to obtain and manage capabilities. With it, comes a change to what happens if a public capability is requested, using the function `Account.Capabilities.get`, and no capability is published under the requested path.
Whereas before you would get a capability which would simply fail to borrow (unless it was later linked), now a nil object will be returned.

This will present some challenges for existing applications which use capabilities even if they are not borrowable. This includes:

1. [Royalties](https://github.com/onflow/flow-nft/blob/master/contracts/MetadataViews.cdc#L303) - The Royalties Metadata standard assumes you can build a capability, there is no standard based on the kind of token you want to send a user. As such, many
platforms which follow the royalties standard take the return capability's address and reconstruct the correct one afterwards.
2. [Lost and Found](https://github.com/Flowtyio/lost-and-found/blob/main/contracts/LostAndFound.cdc#L720) - Helps users distribute resources to accounts. If a provided capability is not valid
(`check` returns false), it will store the resource for a user to redeem at a later date. If Capabilities now return nil when they aren't present, this utility contract will be much more challenging
to use. Lost and Found is used by a few marketplace platforms to ensure that things like Royalties can be distributed even if the receiving account is not configured, it is a safeguard against malicious
behavior as well for other contracts like Flowty Loans.

## User Benefit

There is value in a Capability not being nil, even if it is not borrowable.
Nil is not the same as invalid, because we still have the address field which is something that is used often by developers.
Modifying the behavior for obtaining capabilities to instead give an invalid capability allows developers to make use of that address and react to something not being valid.
It also introduces fewer panic scenarios that are out of general-purpose platforms' control (like if an NFT Collection panics on Royalty creation because of this).

## Design Proposal

The return type of the function `Account.Capabilities.get` is changed from the optional type `Capability<T>?` to the non-optional type `Capability<T>`.
When the no capability is published under the requested path, the function returns an *invalid* capability.

This "invalid" capability should:

1. Always return `false` when `check` is called.
2. Always return `nil` when `borrow` is called.
3. Have an ID of 0.

Capability Controller IDs are stated to be a *non-zero*, which
means the value of zero can be used to mark capabilities as invalid.

## Alternatives Considered

It was suggested that anything which is expecting a non-nil Capability could instead request their own struct which would be carried along with the Capability itself

```
struct OptCap {
let address: Address
let cap: Capability?
}
```

This might work in cases where the method being called is entirely in control of the caller, but it will not cover cases like Royalties where they are being blindly resolved using the
Flow Metadata Standard. In cases like that, the caller has no way to protect itself again an optional capability that will panic when being created

## Questions and Discussion

There is some [context in Discord](https://discord.com/channels/613813861610684416/621847426201944074/1194733333658218647) which led to the creation of this FLIP.