Flow is a permissionless layer-1 blockchain built to support the high-scale use cases of games, virtual worlds, and the digital assets that power them. The blockchain was created by the team behind CryptoKitties, Dapper Labs, and NBA Top Shot.
One of the best features of Flow is that it supports the paradigm of resource-oriented programming. Resource-oriented programming is a new way of managing memory where resources are held “in-situ” by the resource owner instead of in a separate ledger. This is very relevant for managing scarce and unreplicable digital resources on the blockchain.
In this article, I’ll do a deep dive into resource-oriented programming—and allow you to better manage your resources in your Flow smart contracts.
What is resource-oriented programming?
In resource-oriented programming, objects are labeled as “resources”. Resources are a collection of variables and functions that only have exactly one copy at a time. Resources provide better composability than EVM and WASM.
When something is called a resource, there are very specific rules for interacting with it. The three rules that apply to resources in resource-oriented programming are as follows:
- Each resource exists in only one memory location - Resources cannot be duplicated.
- Ownership of a resource is defined by where it is stored - No central ledger that keeps track of ownership
- Only the owner of the resource can access its methods.
To explain this better: In the real world, if you claim to own a watch, that ownership is proven by the fact that you possess it yourself. There is no central ledger that people refer to check if you are the owner. However, in programming, when we think of ownership, we think of a mapping somewhere in a ledger of the object ID to the owner ID. For example, in the case of ERC721 smart contracts, the ownership of digital assets is stored in a ledger owned by the main smart contract.
Flow changes this paradigm of ownership by storing “resources” in the memory location owned by the owner itself (see image below). This means that if you’re holding a digital asset on the Flow blockchain, it is stored in the memory location of the account that you own.
This has several benefits, which we’ll talk about in the coming section.
A depiction of memory allocation for resources in Cadence
Resources in Cadence
Cadence is the world’s first high-level resource-oriented programming language. Move, created for Facebook’s now defunct Diem, is the only other language that is resource-oriented, but it is more low-level.
In Cadence, resources are defined using the “resource” keyword. Within a resource, you can define the attributes of the resource, like variables and functions. This definition of a resource now becomes a type and can be used to create objects of that type.
An example of creating a resource and creating an instance of that resource is shown below:
// Declare a resource that only includes one function.
pub resource CarAsset {
// A transaction can call this function to get the "Honk Honk!"
// message from the resource.
pub fun honkHorn(): String {
return "Honk Horn!"
}
}
// We're going to use the built-in create function to create a new instance
// of the Car resource
pub fun createCarAsset(): @CarAsset {
return <-create CarAsset()
}
Destroy function
Resources in Cadence can be explicitly destroyed using the “destroy” keyword. A resource object cannot go out of scope and be dynamically lost. It must be destroyed or moved before the end of scope.
Below is a cadence example of how to use destroy:
let d <- create SomeResource(value: 20)
destroy d
The Move Operator (<-)
To make the moves of a resource explicit, the Move operator, such as “<-”, must be used when:
- Initializing the value of a constant or variable with a resource
- Moving resources to a different variable
- Moving resources to the argument of a function
- Returning it from a function
Below is a cadence example of how to use the Move (“<-”) operator
let d <- create SomeResource(value: 20)
let new_resource <- d
Code Example of Creating a Resource in Cadence
Below is an example of a Cadence script that creates a CryptoKitty collection as a resource and also stores individual Kitties as a resource.
contract CryptoKitties {
// Accounts store a collection in their account storage
resource KittyCollection {
// Each collection has functions to
// move stored resources in and out
fun withdraw(kittyId: int): CryptoKitty
fun deposit(kitty: CryptoKitty)
}
// The resource objects that can be stored in the collection
resource CryptoKitty {}
}
transaction(signer: Account) {
// Removes the Kitty from signer's collection, and stores it
// temporarily on the stack.
let theKitty <- signer.kittyCollection.withdraw(kittyId: myKittyId)
// Moves the Kitty into the receiver's account
let receiver = getAccount(receiverAccountId)
receiver.kittyCollection.deposit(kitty: <-theKitty)
}
In the above code, we create a KittyCollection resource that has several CryptoKitty sub-resources within it. We then do a transfer transaction to transfer a Kitty from one user account to another.
Other things to keep in mind
- Resources must be created using the “create” keyword.
- At the end of a function with resources, the resources should either be moved or destroyed.
- Accessing a field or a function of a resource does not destroy it.
- When a resource is moved, the constant or variable currently holding the resource becomes invalid.
- To make a resource type explicit, the prefix @ must be used in type annotations.
Comparison and Benefits
Resource-oriented programming versus ledger-based systems
A common comparison of the ownership structure in resource-oriented programming is with ledger-based ownership. In the ledger model, like in ERC721 smart contracts, every time you have to update ownership, you need to update the mapping of the ID in a shared ledger.
In resource-oriented programming, resources are stored within the account of the owner itself, so transferring a digital asset only means moving the resource from the first party to the second. This does not require updating any ledgers. Resource-oriented programming is more suitable for blockchain use cases like digital assets than the traditional ledger system.
Benefits of resource-oriented programming
There are several benefits that resource-oriented programming brings to the table. Let’s look at some of them below.
Makes capability-based security possible
Resource types provide the foundation on which capability-based security can be established. Capabilities are one of the best methods to create highly secure blockchain authorization systems. This brings the utmost security to the Flow blockchain.
Better protection again reentrancy attacks
Reentrancy attacks are one of the most severe smart contract vulnerabilities often exploited by hackers. In a reentrancy attack, a hacker calls the withdraw function on a smart contract recursively. This way, they are able to drain the entire funds of a smart contract. Due to the architecture of cadence, re-entrancy attacks are much less likely to occur. Read more in this article about the DAO hack and how cadence is safe from it.
Better pricing and cost management
In Ethereum, the CryptoKitties contract has 2 million digital assets collectively taking up more than 100 MB of space, living rent-free on the Ethereum blockchain. In an ideal world, it should be possible to charge the owners of Kitties based on how much of this data belongs to them on-chain. In Flow, since the resource is directly stored in the account of the owner, it is clear who needs to pay the cost for that data.
Conclusion
Resource-oriented programming is a new and innovative programming paradigm that has been brought to the blockchain world by the Flow blockchain. It makes resource management very efficient, easy, and secure. It also helps provide a better cost-sharing model for running blockchain infrastructure.
You can refer to the official docs page on Resources to learn more. I also recommend that you get your hands dirty by going through Flow Docs and this tutorial on Resources.
Top comments (0)