Implementing FA2
By: Tq Tezos
Tags: Smart Contracts, Tezos, Smartpy, Ligo

Implementing FA2

Overview

As outlined in March, FA2 (TZIP-12) is a multi-asset interface for tokens and multi-token contracts on Tezos. FA2 broadens the potential for tokenization on Tezos significantly, supporting a wide range of token types (e.g. fungible, non-fungible, non-transferrable, etc.) and use cases. In practice, it aims to support novel implementation patterns alongside well-known patterns like single-asset fungible tokens (i.e. ERC-20) or NFTs.

Since our first release of FA2, we’ve updated the TZIP-12 specification to include a Michelson interface, refined prose around token supply behaviors, and provided standardized error messages.

With this post, we’re releasing an initial implementation of a multi-asset contract based on FA2 in SmartPy, available in the Dev version of the SmartPy IDE.

We also briefly discuss the pros and cons of several implementation patterns for FA2 permissioning, one of the main concerns of the effort.

Upcoming releases will provide several new LIGO implementations of FA2 (e.g. single and multi-asset for NFTs, fungible, etc.), a benchmarking of initial FA2 implementations, as well as tutorial quick-starts in the assets portal.

In addition, ongoing work continues to ease the lives of tooling developers and extend FA2 in terms of permissioning (e.g. whitelist, allowance), metadata (e.g. rich metadata spec for NFTs, implementation guidelines), and new functionality enabled by potential protocol amendments.

Key Resources

FA2 Implementation Patterns

FA2 has been designed to facilitate multiple implementation patterns around permissioning. As noted while introducing FA2, we’ve identified and outlined three implementation patterns: the monolith, the wrapper, and the transfer hook. We describe and compare these patterns below.

As included in the introductory FA2 post, FA2 facilitates multiple implementation patterns

Monolith

In a monolith contract, permissions are contained within a core FA2 contract. This is likely familiar to those used to the most commonly used ERC-20 implementations on Ethereum which include Approve and Allowance within the same contract. Unsurprisingly, FA1.2 contracts deployed to date have followed this pattern as well.

Although benchmarks are ongoing, current gas constraints in Tezos suggest the monolith pattern is currently the most viable option in terms of gas efficiency for the time being given the cost of inter-contract calls (compared to wrapper- and hook-based options).

However, monolith architectures are less modular and make permissioning less flexible to upgrade out-of-the-box (although operators make this easier). Changing or upgrading the permissioning logic of a monolith contract may require redeploying the contract and/or an extensive migration.

That said, FA2 seeks to reduce this tradeoff by specifying operators (similar to the notion in ERC-777/1155) and allowing the user to assign permissions over their tokens to another contract. Importantly, these can also include generalized permissioning contracts (e.g. a smart contract wallet) or application-specific permissioning contracts (e.g. specific to an exchange) which may be more easily upgraded or adapted as needed.

Wrapper

In an on-chain wrapper implementation, a separate “wrapper” contract applies permissions and forwards calls to the core FA2 contract which manages the token’s ledger (mapping addresses to balances).

Wrappers enable modularity, ideally with composable contract pieces (e.g. whitelisting or allowance wrappers) which extend core functionality and can be upgraded or replaced over time. Upgradability is a big advantage of the wrapper pattern, because permissioning can be upgraded without touching the core ledger contract.

On the downside, expressive wrappers currently face practical limitations in Tezos as they require inter-contract calls for both transfer and view operations. And from a client perspective, such architectures can be more complex as the client needs to be aware of both the wrapper permissioning contract and the core ledger contract. A wrapper-based approach to permissioning may also produce fragmentation and a weaker network effect for a standard by requiring wallets and other third-parties to support multiple wrapper variants.

Transfer Hook

In a transfer hook pattern, a core FA2 contract calls another contract which defines a permissioning policy regarding who can send and receive tokens. The permissioning policy contract can include granular permissioning rules, including allowance, whitelist, and other functionality.

Among upsides of the hook pattern is a separation of concerns, namely that core transfer logic is fixed in the core contract while permissioning rules in the hook contract can be upgraded easily. In other words, the permissioning rules of the token contract can be upgraded with ease without requiring any storage migration of the FA2 ledger state.

As in the case of contract wrappers, gas is a limitation for such architectures in Tezos today given the inter-contract calls’ cost and sensitivity to the size of the contract they’re interacting with. In other words, more complex permissioning policies in a hook contract are very clearly felt by the user, especially as use of the contract generates increasing contract size.

Comparing Wrappers and Hooks

The table below enables easy comparison of on-chain wrapper, source-code wrappers, and transfer hooks.

A comparison of the on-chain wrappers, source code wrappers, and transfer hooks

What’s next?

Upcoming releases include new FA2 implementations (e.g. NFT, fungible), benchmarks of FA2 performance, FA2 tutorials, and permissioning plug-ins, such as whitelisting. We’ve also recently provided reference implementations in both SmartPy and LIGO for an independent security audit and continue to welcome product feedback on anything FA2 or assets-related.

A Special Thanks*

Special thanks for invaluable conversations, advice, and feedback about blockchain-based assets from those listed below (and anyone we’ve forgotten)

* does not indicate endorsement of TZIP-12

Gabriel, Tom Jack, and team from LIGO

Benjamin, Raphael, and Bruno from Nomadic Labs

The SmartPy team

Nicolas and Santiago from OpenZeppelin

Greg and team at 0x

Matej and Istvan from Stove Labs

Devin and Alex from OpenSea

Alex from Blockwatch

Tarun Chitra from Gauntlet Networks

Arthur Breitman

Serokell

Tezit

Jared from Compound

Michael from Baking Bad

Luke and Brian from Coinbase Custody

Gavi and Viktor from Anchorage

Vertalo

Philippe from Horizon Games

James and Tyler from camlCase

Chris Goes and team at Metastate

Mike Radin from Cryptonomic

ZenGo

Marco from Tezos Foundation

Madfish Solutions

Jev from ECAD Labs

Klas from Kukai

Pascal and Mike from Airgap

Ron, Mason, and James from Tokensoft