Ownership and Capabilities — NFTango Part 2

Overmind
Overmind_xyz
Published in
4 min readJun 13, 2023

When we deal with digital assets, ownership is at the core of security and governance. Solidity’s ownership journey started with checking msg.sender in code where necessary, then was formally standardized as EIP-173 in 2018, and has since evolved into Roles.sol, Whitelist.sol and a wide array of custom access control contracts developed for ever increasing complex use cases. In contrast, the concept of ownership is more built into the programming language of Move itself. Move natively implements Type Abilities and uses Capabilities as a security model. First and most frequently used is SignerCapability in account.move, then Mint/Burn Capabilities of coin.move. No doubt Move will go through its own ownership odyssey as well.

A refresher from Part 1 of the series, NFTango.move creates a NFTangoStore resource to store game state and contains game logic in the same contract while NFTangoManager.sol creates a NFTango.sol contract to store game state and has the game logic in itself.

If we compare ownership of the NFTangoStore resource struct, we see that in Move, the game creator owns the game state (aka. the NFTangoStore resource), while in Solidity, the Manager contract has to maintain an extra global mapping of game creator address to game instance. Similarly, if we compare the ownership of the betted NFTs, Move has a separate resource account to host the NFTs while Solidity simply hosts all NFTs in one central Manager contract.

Capabilities vs Approvals

While it was not impossible to have NFTango.sol own the NFTs while logic is controlled by the manager, there would be lot of approvals that need to be called, and consequently allowances to be checked. Even keeping the NFTs in the Manager contract still requires approvals from the player and opponent to transfer their NFTs to the contract itself. Move has a neat concept called Signer Capability in accounts.move which allows resources to store the Capability to sign transactions on the resource account’s behalf. This shift in power from the contract account to the resource is another example of how Solidity centers around contracts (giving approval) and Move around Resources (controlling signing capability of accounts).

Capabilities can be thought of as a resource, a part of a resource and fundamentally a security model implementation in Move. Its native implementation means there are no additional contracts like Ownable.sol or Roles.sol needed for access control.

Diving deeper, Capabilities are used everywhere in Move. Look at the MintCapability and BurnCapability in coin.move — these are usually implemented as ERC20 extensions in Solidity through a Roles check, just look at all the functions related to the Minter Role in OpenZeppelin’s ERC20Mintable Contract. In Move, developers can define their own Capabilities as a module and tightly control how it is distributed and secured, check out the experimental Capability.move module which has certain protocols to limit the validity of the Capability to a transaction. For those following our quests, be on the look out for a Capability based quest!

Move Type Abilities and Security

In Move, we trivially moved the NFTangoStore resource to the game creator’s address and anyone can check it in Global Storage by providing the creator’s address by using the borrow_global call. In Solidity, owning a contract is serious business, according to Ownable.sol, since we can handily gate certain functions in the contract by being the owner. However, Move has implicit resource security through Type Abilities.

There are four Abilities — store, key, copy, drop. While it is easy to read up on what each Ability does in the Move Book, it is difficult to infer what security is offered to resources when executed. Take NFTangoStore as an example — it only has the “key” resource. What does the lack of copy, drop and store imply?

Copy allows NFTangoStore values to be, well, copied in multiple places in memory. We do not want this behaviour because there should be no instances where a game with players and NFTs on the line can be duplicated. In more technical terms, everyone can reference or point to the same NFTango resource, but as soon as an attempt to de-reference or read the values inside the Store occurs, the values will move to the new pointer instead of being copied.

Drop similarly allows objects to be destroyed. In this particular game, we will not be destroying any game instances after completion, but simply marking claimed as true. This way, we have an archive of games, possibly for auditing purposes later. Without drop, the game instance cannot be accidentally overwritten once created.

Store allows resources to be stored into Global Storage. Sometimes, objects should not be stored for the long term. Some Capabilities for example, may only be necessary for the lifespan of one transaction and should not be moved anywhere and hence does not require store. However, if we look at SignerCapability in account.move, it does have the store capability which allows us to store a reference to our resource account in our NFTangoStore resource. A caveat here, having key implies store. So technically, our NFTangoStore struct has key and store abilities. As evidenced by the ample borrow_global and move_to calls made throughout the module.

In conclusion…

Resources are protected by Abilities as they are moved around. The security around the moving means less focus on ownership, however, native built in Capabilities more than make up for ease of use when it comes to access control — often cheaper in gas units too.

--

--

Overmind
Overmind_xyz

The first web3 solve-to-earn platform where developers compete on coding puzzles to earn prizes and on-chain credentials. Live on #Aptos.