Skip to content

algorandlabs/smart-asa

Repository files navigation

Smart-ASA

Smart ASA reference implementation that combines the simplicity and security of an Algorand Standard Asset with the composability and programmability of Algorand Smart Contracts to obtain a new, powerful, L1 entity that extends a regular ASA up to the limits of your imagination!

Overview

The Smart ASA introduced with ARC-0020 represents a new building block for complex blockchain applications. It offers a more flexible way to work with ASAs providing re-configuration functionalities and the possibility of building additional business logics around operations like ASA transfers, royalties, role-based-transfers, limit-amount-transfers, mints, and burns. This example presents an implementation of the Smart ASA contract as well as an easy to use CLI to interact with its functionalities.

⚠️ Disclamer: This code is not audited!

Reference implementation rational

A Smart ASA is an ASA, called Underlying ASA, controlled by a Smart Contract, called Smart ASA App, that exposes methods to create, configure, transfer, freeze, and destroy the asset. The create method configures the initial the state of the controlling Smart Contract and creates the Underlying ASA. The following sections introduce the both the Underlying ASA and the Application state of this implementation.

Underlying ASA configuration

The create method triggers an AssetConfigTx transaction (inner transaction) that generates a new asset with the following parameters:

Property Value
total 2^64-1
decimals 0
default_frozen True
unit_name S-ASA
asset_name SMART-ASA
url smart-asa-app-id:<Smart ASA App ID>
manager_addr <Smart ASA App Addr>
reserve_addr <Smart ASA App Addr>
freeze_addr <Smart ASA App Addr>
clawback_name <Smart ASA App Addr>

The Underlying ASA is created with maximum supply (max uint64), it is not divisible, and it is frozen by default. The unit and asset names are custom strings that identify the Smart ASA.

The url field is used to bind the Underlying ASA with the Smart ASA App ID who controls it, with the following encoding:

smart-asa-app-id:<App ID>

Finally, the manager, reserve, freeze, and clawback roles of the ASA are assigned to the application address. Therefore, the Underlying ASA can only be controlled by the smart contract.

State Schema

The StateSchema of the Smart Contract has been designed to match 1-to-1 the parameters of an ASA. In addition, this reference implementation requires users to opt-in to the application and initialize their LocalState.

Global State

The GlobalState of the Smart ASA App is defined as follows:

Integer Variables:

  • total: total supply of a Smart ASA. This value cannot be greater than the Underlying ASA total supply or lower than the current cirulating supply (in case of reconfiguration);
  • decimals: number of digits to use after the decimal point. If 0, the Smart ASA is not divisible. If 1, the base unit of the Smart ASA is in tenth, it 2 it is in hundreds, if 3 it is in thousands, and so on;
  • default_frozen: True to freeze Smart ASA holdings by default;
  • smart_asa_id: asset ID of the Underlying ASA;
  • frozen: True to globally freeze Smart ASA transfers for all holders.

Bytes Variables:

  • unit_name: name of a unit of the Smart ASA;
  • name: name of the Smart ASA;
  • url: URL with additional information on the Smart ASA;
  • metadata_hash: Smart ASA metadata hash;
  • manager_addr: Address of the account that can manage the configuration of the Smart ASA and destroy it;
  • reserve_addr: Address of the account used to mint and burn the Smart ASA;
  • freeze_addr: Address of the account used to freeze holdings or even globally freeze the Smart ASA;
  • clawback_addr: Address of the account that can clawback holdings of the Smart ASA.

The Smart ASA App of the reference implementation has been designed to control one ASA at a time. For this reason, the smart_asa_id variable has been added to the GlobalState. It is used to record the current Underlying ASA controlled by the application. This value is also stored into the local state of opted-in users, enforcing cross-checks between local and global states and avoiding issues like unauthorized transfers (see Security Considerations for more details).

As additional feature, this reference implementation also includes the Smart ASA global frozen variable. It can only be updated by the freeze address which has the authority of globally freezing the asset with a single action, rather than freezing accounts one by one.

In this implementation, new functional authority has been assigned to the reserve address of the Smart ASA, which is now the (only) entity in charge of minting and burning Smart ASAs (see the Smart ASA Transfer interface for more details).

Local State

The LocalState initialized by the Smart ASA App for opted-in users is defined as follows:

Integer Variables:

  • smart_asa_id: asset ID of the Underlying ASA of the Smart ASA a user has opted-in;
  • frozen: True to freeze the holdings of the account.

Self Validation

The Smart ASA reference implementation enforces self validation of the StateSchema. On creation, it controls the size of the given schema for both the global and local states. The expected values are:

Global Local
Ints 5 2
Bytes 8 0

Smart Contract ABI's type check

Smart Contract methods has been implemented to comply with the Algorand ABI interface. The validation checks on the ABI types are carried on the client side. The Smart Contract enforces the following on-chain checks:

  • address length must be equal to 32 bytes;
  • Bool values must be equal to 0 or 1.

Smart ASA Methods

Smart ASA reference implementation follows the ABI specified by ARC-20 to ensure full composability and interoperability with the rest of Algorand's ecosystem (e.g. wallets, chain explorers, external dApp, etc.).

The implementation of the ABI relies on the new PyTeal ABI Router component, which automatically generates ABI JSON by using simple Python decorators for Smart Contract methods. PyTeal ABI Router takes care of ABI types and methods' signatures encoding as well.

Smart ASA App Create

Smart ASA Create is a BareCall (no argument needed) that instantiate the Smart ASA App, verifying the consistency of the SateSchema assigned to the create Application Call. This method initializes the whole Global State to default upon creation.

Smart ASA App Opt-In

Smart ASA Opt-In represents the account opt-in to the Smart ASA. The argument asset represents the Underlying ASA. This method initializes the LocalState of the user. If the Smart ASA is default_frozen then the opting-in users are frozen too.

{
  "name": "asset_app_optin",
  "args": [{"name": "asset", "type": "asset"}],
  "returns": {"type": "void"}
}

Smart ASA App Close-Out

Smart ASA Close-Out is the close out of an account from the Smart ASA. The argument close_asset represents the Underlying ASA to be closed, while close_to is the account to which all the remainder balance is sent on closing. This method removes the Smart ASA App LocalState from the calling account as well as closing out the Underlying ASA. In order to replicate the regulare close-out behaviour of a regular ASA a Smart ASA closing procedure acts as follows:

  1. If the Underlying ASA has been destroyed, then no check on is performed on close_to account;
  2. If the user account is frozen or the whole Underlying ASA is frozen, then the close_to MUST be the Smart ASA Creator (Smart ASA App);
  3. If the close_to account is not the Smart ASA Creator, then it MUST opted-in to the Smart ASA.

For Smart ASA default_frozen = False, a freezed malicious user could close-out and opt-in again to change is frozen state. Checking the Smart ASA holdings before approving a close-out addresses this issue.

{
  "name": "asset_app_closeout",
  "args": [
    {"name": "close_asset", "type": "asset"},
    {"name": "close_to", "type": "account"}
  ],
  "returns": {"type": "void"}
}

Smart ASA Creation

Smart ASA Create is the creation method of a Smart ASA. It creates a new Underlying ASA and instantiates the controlling Smart Contract with the given properties.

{
  "name": "asset_create",
  "args": [
    {"name": "total", "type": "uint64"},
    {"name": "decimals", "type": "uint32"},
    {"name": "default_frozen", "type": "bool"},
    {"name": "unit_name", "type": "string"},
    {"name": "name", "type": "string"},
    {"name": "url", "type": "string"},
    {"name": "metadata_hash", "type": "string"},
    {"name": "manager_addr", "type": "address"},
    {"name": "reserve_addr", "type": "address"},
    {"name": "freeze_addr", "type": "address"},
    {"name": "clawback_addr", "type": "address"}
  ],
  "returns": {"type": "uint64"}
}

Smart ASA Configuration

Smart ASA Configuration is the update method of a Smart ASA. It updates the parameters of an existing Smart ASA. Only the manager has the authority to reconfigure the asset by invoking this method.

It is worth noting that Smart ASA extends ASA configurability to any field! With Smart ASA users can now reconfigure properties like name, unit_name, url and even properties like total, decimals or default_frozen! This makes the Smart ASA a perfect fit for "evolvable" assets, like NFTs.

The ABI method requires specifying always all Smart ASA fields, even those one not to be changed, by assigning the current value to the unchanged fields. The Smart ASA Client of this reference implementation abstracts this taking care of unspecified fields by replicating current Smart ASA state for the unchanged fields as default.

The reference implementation applies the following restrictions:

  • manager_addr, reserve_addr, freeze_addr and clawback_addr addresses can no longer be configured once set to ZERO_ADDRESS;
  • total cannot be configured to a value lower than the current circulating supply.
{
  "name": "asset_config",
  "args": [
    {"name": "config_asset", "type": "asset"},
    {"name": "total", "type": "uint64"},
    {"name": "decimals", "type": "uint32"},
    {"name": "default_frozen", "type": "bool"},
    {"name": "unit_name", "type": "string"},
    {"name": "name", "type": "string"},
    {"name": "url", "type": "string"},
    {"name": "metadata_hash", "type": "string"},
    {"name": "manager_addr", "type": "address"},
    {"name": "reserve_addr", "type": "address"},
    {"name": "freeze_addr", "type": "address"},
    {"name": "clawback_addr", "type": "address"}
  ],
  "returns": {"type": "void"}
}

Smart ASA Transfer

Smart ASA Transfer is the asset transfer method of a Smart ASA. It defines the transfer of an asset between an asset_sender and asset_receiver specifying the asset_amount to be transferred. This method automatically distinguishes four types of transfer, such as mint, burn, clawback, and regular transfer.

{
  "name": "asset_transfer",
  "args": [
    {"name": "xfer_asset", "type": "asset"},
    {"name": "asset_amount", "type": "uint64"},
    {"name": "asset_sender", "type": "account"},
    {"name": "asset_receiver", "type": "account"}
  ],
  "returns": {"type": "void"}
}

Mint

In the reference implementation only the reserve address can mint a Smart ASA. A minting succeeds if the following conditions are satisfied:

  • the Smart ASA is not globally frozen;
  • asset_sender is the Smart ASA App;
  • asset_receiver is not frozen;
  • asset_receiver Smart ASA ID in Local State is up-to-date;
  • asset_amount does not exceed the outstanding available supply of the Smart ASA.

Reference implementation checks that smart_asa_id is up-to-date in Local State since the Smart ASA App could create a new Underlying ASA (if the previous one has been dystroied by the Manager Address). This requires users to opt-in again and initialize accordingly a coherent frozen status for the new Smart ASA (which could potentially have been created as default_frozen).

Burn

In the reference implementation only the reserve address can burn a Smart ASA. A burning succeeds if the following conditions are satisfied:

  • the Smart ASA is not globally frozen;
  • asset_sender is the reserve address;
  • asset_sender is not frozen;
  • asset_sender Smart ASA ID in Local State is up-to-date;
  • asset_receiver is the Smart ASA App.

Clawback

The clawback address of a Smart ASA can invoke a clawback transfer from and to any asset holder (or revoke an asset). A clawback succeeds if the following conditions are satisfied:

  • asset_sender Smart ASA ID in Local State is up-to-date;
  • asset_receiver Smart ASA ID in Local State is up-to-date;.

Checking that Smart ASA ID in Local State is up-to-date both for asset_sender and asset_receiver, by looking at their respective Local States, implicitly checks that both asset_sender and asset_receiver opted-in the Smart ASA App. This ensures that minting and burning can not be executed as clawback, since the Smart ASA App can not opt-in to itself.

Transfer

A regular transfer of a Smart ASA can be invoked by any opted-in asset holder. It succeeds if the following conditions are satisfied:

  • the Smart ASA is not globally frozen;
  • asset_sender is not frozen;
  • asset_sender Smart ASA ID in Local State is up-to-date;
  • asset_receiver is not frozen;
  • asset_receiver Smart ASA ID in Local State is up-to-date.

Smart ASA Global Freeze

Smart ASA Global Freeze is the freeze method of a Smart ASA. It enables the freeze address to globally freeze a Smart ASA. A frozen Smart ASA cannot be transferred, minted or burned.

{
  "name": "asset_freeze",
  "args": [
    {"name": "freeze_asset", "type": "asset"},
    {"name": "asset_frozen", "type": "bool"}
  ],
  "returns": {"type": "void"}
}

Smart ASA Account Freeze

Smart ASA Account Freeze is the account freeze method of a Smart ASA. It enables the freeze address to freeze a Smart ASA holder. Freezed accounts cannot receive nor send the asset.

{
  "name": "account_freeze",
  "args": [
    {"name": "freeze_asset", "type": "asset"},
    {"name": "freeze_account", "type": "account"},
    {"name": "asset_frozen", "type": "bool"}
  ],
  "returns": {"type": "void"}
}

Smart ASA Destroy

Smart ASA Destroy is the destroy method of a Smart ASA. In this reference implementation only the manager can invoke the Smart ASA destroy. This method clears the GlobalState schema of a Smart ASA, destroying any previous configuration.

A Smart ASA can be destroyed if and only if the circulating supply = 0. After a destroy, users remain opted-in to the Smart ASA App, but with an outdated smart_asa_id in their local state. See Security Considerations to understand the side effects of a Smart ASA destroy.

{
  "name": "asset_destroy",
  "args": [
    {"name": "destroy_asset", "type": "asset"}
  ],
  "returns": {"type": "void"}
}

Smart ASA Getters

Getters methods have bee implemented for each StateSchema parameter of the Smart ASA. Refer the ARC-0020 for a comprehensive list of available getters.

Smart ASA reference implementation introduces two new getters:

  • get_circulating_supply: returns the current circulating supply of a smart ASA;
  • get_optin_min_balance: returns the minimum balance (in ALGO) required to opt-in the Smart ASA.

Getters ABI interface example:

{
  "name": "get_<param>",
  "args": [
    {"name": "asset", "type": "asset"}
  ],
  "returns": {"type": "uint64"}
}

Smart ASA CLI

Install

The Pipfile contains all the dependencies to install the Smart ASA CLI using pipenv entering:

pipenv install

The Smart ASA CLI requires an Algorand sandbox up and running (try it in dev mode first!).

Usage

The Smart ASA CLI plays the same role as goal asset to facilitate a seamless understanding of this new "smarter" ASA.

The CLI has been built with docopt, which provides an intuitive and standard command line usage:

  • <...> identify mandatory positional arguments;
  • [...] identify optional arguments;
  • (...|...) identify mandatory mutually exclusive arguments;
  • [...|...] identify optional mutually exclusive arguments;
  • --arguments could be followed by a <value> (if required) or not;

All the <account>s (e.g. <creator>, <manager>, etc.) must be addresses of a wallet account managed by sandbox's KMD.

Using the command line you can perform all the actions over a Smart ASA, just like an ASA!

Smart ASA (ARC-20 reference implementation)

Usage:
  smart_asa create  <creator> <total> [--decimals=<d>] [--default-frozen=<z>]
                    [--name=<n>] [--unit-name=<u>] [--metadata-hash=<s>]
                    [--url=<l>] [--manager=<m>] [--reserve=<r>]
                    [--freeze=<f>] [--clawback=<c>]
  smart_asa config  <asset-id> <manager> [--new-total=<t>] [--new-decimals=<d>]
                    [--new-default-frozen=<z>] [--new-name=<n>]
                    [--new-unit-name=<u>] [--new-metadata-hash=<s>]
                    [--new-url=<u>] [--new-manager=<m>] [--new-reserve=<r>]
                    [--new-freeze=<f>] [--new-clawback=<c>]
  smart_asa destroy <asset-id> <manager>
  smart_asa freeze  <asset-id> <freeze> (--asset | --account=<a>) <status>
  smart_asa optin   <asset-id> <account>
  smart_asa optout  <asset-id> <account> <close-to>
  smart_asa send    <asset-id> <from> <to> <amount>
                    [--reserve=<r> | --clawback=<c>]
  smart_asa info    <asset-id> [--account=<a>]
  smart_asa get     <asset-id> <caller> <getter> [--account=<a>]
  smart_asa         [--help]

Commands:
  create     Create a Smart ASA
  config     Configure a Smart ASA
  destroy    Destroy a Smart ASA
  freeze     Freeze whole Smart ASA or specific account, <status> = 1 is forzen
  optin      Optin Smart ASAs
  optout     Optout Smart ASAs
  send       Transfer Smart ASAs
  info       Look up current parameters for Smart ASA or specific account
  get        Look up a parameter for Smart ASA

Options:
  -h, --help
  -d, --decimals=<d>           [default: 0]
  -z, --default-frozen=<z>     [default: 0]
  -n, --name=<n>               [default: ]
  -u, --unit-name=<u>          [default: ]
  -l, --url=<l>                [default: ]
  -s, --metadata-hash=<s>      [default: ]
  -m, --manager=<m>            Default to Smart ASA Creator
  -r, --reserve=<r>            Default to Smart ASA Creator
  -f, --freeze=<f>             Default to Smart ASA Creator
  -c, --clawback=<c>           Default to Smart ASA Creator

Create Smart ASA NFT

Let's create a beautiful πŸ”΄ Smart ASA NFT (non-fractional for the moment)...

python3 smart_asa.py create KAVHOSWPO3XLBL5Q7FFOTPHAIRAT6DRDXUYGSLQOAEOPRSAXJKKPMHWLLQ 1 --name Red --unit-name πŸ”΄

 --- Creating Smart ASA App...
 --- Smart ASA App ID: 2988

 --- Funding Smart ASA App with 1 ALGO...

 --- Creating Smart ASA...
 --- Created Smart ASA with ID: 2991

The Smart ASA is created directly by the Smart ASA App, so upon creation the whole supply is stored in Smart ASA App account. A minting action is required to put units of Smart ASA in circulation (see Mint Smart ASA NFT).

python3 smart_asa.py info 2991

        Asset ID:         2991
        App ID:           2988
        App Address:      T6QBA5AXSJMBG55Y2BVDR6MN5KTXHHLU7LWDY3LGZNAPGIKDOWMP4GF5PU
        Creator:          KAVHOSWPO3XLBL5Q7FFOTPHAIRAT6DRDXUYGSLQOAEOPRSAXJKKPMHWLLQ
        Asset name:       Red

        Unit name:        πŸ”΄

        Maximum issue:    1 πŸ”΄
        Issued:           0 πŸ”΄
        Decimals:         0
        Global frozen:    False
        Default frozen:   False
        Manager address:  KAVHOSWPO3XLBL5Q7FFOTPHAIRAT6DRDXUYGSLQOAEOPRSAXJKKPMHWLLQ
        Reserve address:  KAVHOSWPO3XLBL5Q7FFOTPHAIRAT6DRDXUYGSLQOAEOPRSAXJKKPMHWLLQ
        Freeze address:   KAVHOSWPO3XLBL5Q7FFOTPHAIRAT6DRDXUYGSLQOAEOPRSAXJKKPMHWLLQ
        Clawback address: KAVHOSWPO3XLBL5Q7FFOTPHAIRAT6DRDXUYGSLQOAEOPRSAXJKKPMHWLLQ

Fractionalize Smart ASA NFT

One of the amazing new feature of Smart ASAs is that they are completely re-configurable after creation! Exactly: you can even reconfigure their total or their decimals!

So let's use this new cool feature to fractionalize the Smart ASA NFT after its creation by setting the new <total> to 100 and <decimals> to 2!

python3 smart_asa.py config 2991 KAVHOSWPO3XLBL5Q7FFOTPHAIRAT6DRDXUYGSLQOAEOPRSAXJKKPMHWLLQ --new-total 100 --new-decimals 2

 --- Configuring Smart ASA 2991...
 --- Smart ASA 2991 configured!
python3 smart_asa.py info 2991

        Asset ID:         2991
        App ID:           2988
        App Address:      T6QBA5AXSJMBG55Y2BVDR6MN5KTXHHLU7LWDY3LGZNAPGIKDOWMP4GF5PU
        Creator:          KAVHOSWPO3XLBL5Q7FFOTPHAIRAT6DRDXUYGSLQOAEOPRSAXJKKPMHWLLQ
        Asset name:       Red

        Unit name:        πŸ”΄

        Maximum issue:    100 πŸ”΄ <-- 😱
        Issued:           0 πŸ”΄
        Decimals:         2      <-- 😱
        Global frozen:    False
        Default frozen:   False
        Manager address:  KAVHOSWPO3XLBL5Q7FFOTPHAIRAT6DRDXUYGSLQOAEOPRSAXJKKPMHWLLQ
        Reserve address:  KAVHOSWPO3XLBL5Q7FFOTPHAIRAT6DRDXUYGSLQOAEOPRSAXJKKPMHWLLQ
        Freeze address:   KAVHOSWPO3XLBL5Q7FFOTPHAIRAT6DRDXUYGSLQOAEOPRSAXJKKPMHWLLQ
        Clawback address: KAVHOSWPO3XLBL5Q7FFOTPHAIRAT6DRDXUYGSLQOAEOPRSAXJKKPMHWLLQ

Smart ASA NFT opt-in

We can now opt-in the Smart ASA using the optin command that manages both the undelying ASA opt-in and the Smart ASA App opt-in under the hood.

Note that opt-in to Smart ASA App is required only if the Smart ASA need local state (e.g. account frozen).

python3 smart_asa.py optin 2991 KAVHOSWPO3XLBL5Q7FFOTPHAIRAT6DRDXUYGSLQOAEOPRSAXJKKPMHWLLQ

 --- Opt-in Smart ASA 2991...

 --- Smart ASA 2991 state:
{'frozen': 0, 'smart_asa_id': 2991}

Mint Smart ASA NFT

Only Smart ASA Reserve Address can mint units of Smart ASA from the Smart ASA App, with the following restrictions:

  • Smart ASA can not be over minted (putting in circulation more units than total);
  • Smart ASA can not be minted if the asset is global frozen;
  • Smart ASA can not be minted if the minting receiver account is frozen;
python3 smart_asa.py send 2991 T6QBA5AXSJMBG55Y2BVDR6MN5KTXHHLU7LWDY3LGZNAPGIKDOWMP4GF5PU
KAVHOSWPO3XLBL5Q7FFOTPHAIRAT6DRDXUYGSLQOAEOPRSAXJKKPMHWLLQ 100
--reserve KAVHOSWPO3XLBL5Q7FFOTPHAIRAT6DRDXUYGSLQOAEOPRSAXJKKPMHWLLQ

 --- Minting 100 units of Smart ASA 2991
 from T6QBA5AXSJMBG55Y2BVDR6MN5KTXHHLU7LWDY3LGZNAPGIKDOWMP4GF5PU
 to KAVHOSWPO3XLBL5Q7FFOTPHAIRAT6DRDXUYGSLQOAEOPRSAXJKKPMHWLLQ...
 --- Confirmed!
python3 smart_asa.py info 2991

        Asset ID:         2991
        App ID:           2988
        App Address:      T6QBA5AXSJMBG55Y2BVDR6MN5KTXHHLU7LWDY3LGZNAPGIKDOWMP4GF5PU
        Creator:          KAVHOSWPO3XLBL5Q7FFOTPHAIRAT6DRDXUYGSLQOAEOPRSAXJKKPMHWLLQ
        Asset name:       Red

        Unit name:        πŸ”΄

        Maximum issue:    100 πŸ”΄
        Issued:           100 πŸ”΄ <-- πŸ‘€
        Decimals:         2
        Global frozen:    False
        Default frozen:   False
        Manager address:  KAVHOSWPO3XLBL5Q7FFOTPHAIRAT6DRDXUYGSLQOAEOPRSAXJKKPMHWLLQ
        Reserve address:  KAVHOSWPO3XLBL5Q7FFOTPHAIRAT6DRDXUYGSLQOAEOPRSAXJKKPMHWLLQ
        Freeze address:   KAVHOSWPO3XLBL5Q7FFOTPHAIRAT6DRDXUYGSLQOAEOPRSAXJKKPMHWLLQ
        Clawback address: KAVHOSWPO3XLBL5Q7FFOTPHAIRAT6DRDXUYGSLQOAEOPRSAXJKKPMHWLLQ

Smart NFT ASA global freeze

Differently from regular ASA, Smart ASA can now be globally frozen by Freeze Account, meaning that the whole Smart ASA in atomically frozen regardless the particular frozen state of each account (which continues to be managed in the same way as regular ASA).

Let's freeze the whole Smart ASA before starting administrative operations on it:

python3 smart_asa.py freeze 2991 KAVHOSWPO3XLBL5Q7FFOTPHAIRAT6DRDXUYGSLQOAEOPRSAXJKKPMHWLLQ --asset 1

 --- Freezing Smart ASA 2991...
python3 smart_asa.py info 2991

        Asset ID:         2991
        App ID:           2988
        App Address:      T6QBA5AXSJMBG55Y2BVDR6MN5KTXHHLU7LWDY3LGZNAPGIKDOWMP4GF5PU
        Creator:          KAVHOSWPO3XLBL5Q7FFOTPHAIRAT6DRDXUYGSLQOAEOPRSAXJKKPMHWLLQ
        Asset name:       Red

        Unit name:        πŸ”΄

        Maximum issue:    100 πŸ”΄
        Issued:           100 πŸ”΄
        Decimals:         2
        Global frozen:    True   <-- 😱
        Default frozen:   False
        Manager address:  KAVHOSWPO3XLBL5Q7FFOTPHAIRAT6DRDXUYGSLQOAEOPRSAXJKKPMHWLLQ
        Reserve address:  KAVHOSWPO3XLBL5Q7FFOTPHAIRAT6DRDXUYGSLQOAEOPRSAXJKKPMHWLLQ
        Freeze address:   KAVHOSWPO3XLBL5Q7FFOTPHAIRAT6DRDXUYGSLQOAEOPRSAXJKKPMHWLLQ
        Clawback address: KAVHOSWPO3XLBL5Q7FFOTPHAIRAT6DRDXUYGSLQOAEOPRSAXJKKPMHWLLQ

Smart NFT ASA rename

Now that the whole Smart ASA is globally frozen, let's take advantage again of Smart ASA full reconfigurability to change its --name and --unit-name!

python3 smart_asa.py config 2991 KAVHOSWPO3XLBL5Q7FFOTPHAIRAT6DRDXUYGSLQOAEOPRSAXJKKPMHWLLQ --new-name Blue --new-unit-name πŸ”΅

 --- Configuring Smart ASA 2991...
 --- Smart ASA 2991 configured!
python3 smart_asa.py info 2991

        Asset ID:         2991
        App ID:           2988
        App Address:      T6QBA5AXSJMBG55Y2BVDR6MN5KTXHHLU7LWDY3LGZNAPGIKDOWMP4GF5PU
        Creator:          KAVHOSWPO3XLBL5Q7FFOTPHAIRAT6DRDXUYGSLQOAEOPRSAXJKKPMHWLLQ
        Asset name:       Blue   <-- 😱

        Unit name:        πŸ”΅     <-- 😱

        Maximum issue:    100 πŸ”΅
        Issued:           100 πŸ”΅
        Decimals:         2
        Global frozen:    True
        Default frozen:   False
        Manager address:  KAVHOSWPO3XLBL5Q7FFOTPHAIRAT6DRDXUYGSLQOAEOPRSAXJKKPMHWLLQ
        Reserve address:  KAVHOSWPO3XLBL5Q7FFOTPHAIRAT6DRDXUYGSLQOAEOPRSAXJKKPMHWLLQ
        Freeze address:   KAVHOSWPO3XLBL5Q7FFOTPHAIRAT6DRDXUYGSLQOAEOPRSAXJKKPMHWLLQ
        Clawback address: KAVHOSWPO3XLBL5Q7FFOTPHAIRAT6DRDXUYGSLQOAEOPRSAXJKKPMHWLLQ

Smart NFT ASA global unfreeze

The Smart ASA is all set! Let's unfreeze it globally!

python3 smart_asa.py freeze 2991 KAVHOSWPO3XLBL5Q7FFOTPHAIRAT6DRDXUYGSLQOAEOPRSAXJKKPMHWLLQ --asset 0

 --- Unfreezing Smart ASA 2991...
python3 smart_asa.py info 2991

        Asset ID:         2991
        App ID:           2988
        App Address:      T6QBA5AXSJMBG55Y2BVDR6MN5KTXHHLU7LWDY3LGZNAPGIKDOWMP4GF5PU
        Creator:          KAVHOSWPO3XLBL5Q7FFOTPHAIRAT6DRDXUYGSLQOAEOPRSAXJKKPMHWLLQ
        Asset name:       Blue

        Unit name:        πŸ”΅

        Maximum issue:    100 πŸ”΅
        Issued:           100 πŸ”΅
        Decimals:         2
        Global frozen:    False  <-- 😱
        Default frozen:   False
        Manager address:  KAVHOSWPO3XLBL5Q7FFOTPHAIRAT6DRDXUYGSLQOAEOPRSAXJKKPMHWLLQ
        Reserve address:  KAVHOSWPO3XLBL5Q7FFOTPHAIRAT6DRDXUYGSLQOAEOPRSAXJKKPMHWLLQ
        Freeze address:   KAVHOSWPO3XLBL5Q7FFOTPHAIRAT6DRDXUYGSLQOAEOPRSAXJKKPMHWLLQ
        Clawback address: KAVHOSWPO3XLBL5Q7FFOTPHAIRAT6DRDXUYGSLQOAEOPRSAXJKKPMHWLLQ

Smart NFT ASA burn

Another exclusive capability of Smart ASA Reserve Address is burning the Smart ASA with the following limitation:

  • Smart ASA can not be burned if the asset is global frozen;
  • Smart ASA can not be burned if the Reserve account is frozen;
python3 smart_asa.py send 2991 KAVHOSWPO3XLBL5Q7FFOTPHAIRAT6DRDXUYGSLQOAEOPRSAXJKKPMHWLLQ
T6QBA5AXSJMBG55Y2BVDR6MN5KTXHHLU7LWDY3LGZNAPGIKDOWMP4GF5PU 100
--reserve KAVHOSWPO3XLBL5Q7FFOTPHAIRAT6DRDXUYGSLQOAEOPRSAXJKKPMHWLLQ

 --- Burning 100 units of Smart ASA 2991
 from KAVHOSWPO3XLBL5Q7FFOTPHAIRAT6DRDXUYGSLQOAEOPRSAXJKKPMHWLLQ
 to T6QBA5AXSJMBG55Y2BVDR6MN5KTXHHLU7LWDY3LGZNAPGIKDOWMP4GF5PU...
 --- Confirmed!
python3 smart_asa.py info 2991

        Asset ID:         2991
        App ID:           2988
        App Address:      T6QBA5AXSJMBG55Y2BVDR6MN5KTXHHLU7LWDY3LGZNAPGIKDOWMP4GF5PU
        Creator:          KAVHOSWPO3XLBL5Q7FFOTPHAIRAT6DRDXUYGSLQOAEOPRSAXJKKPMHWLLQ
        Asset name:       Blue

        Unit name:        πŸ”΅

        Maximum issue:    100 πŸ”΅
        Issued:           0 πŸ”΅    <-- πŸ‘€
        Decimals:         2
        Global frozen:    False
        Default frozen:   False
        Manager address:  KAVHOSWPO3XLBL5Q7FFOTPHAIRAT6DRDXUYGSLQOAEOPRSAXJKKPMHWLLQ
        Reserve address:  KAVHOSWPO3XLBL5Q7FFOTPHAIRAT6DRDXUYGSLQOAEOPRSAXJKKPMHWLLQ
        Freeze address:   KAVHOSWPO3XLBL5Q7FFOTPHAIRAT6DRDXUYGSLQOAEOPRSAXJKKPMHWLLQ
        Clawback address: KAVHOSWPO3XLBL5Q7FFOTPHAIRAT6DRDXUYGSLQOAEOPRSAXJKKPMHWLLQ

Smart ASA destroy

Similarly to regular ASA, Smart ASA can be destroyed by Smart ASA Manager Address if and only if the Smart ASA Creator hold the total supply.

python3 smart_asa.py destroy 2991 KAVHOSWPO3XLBL5Q7FFOTPHAIRAT6DRDXUYGSLQOAEOPRSAXJKKPMHWLLQ

 --- Destroying Smart ASA 2991...
 --- Smart ASA 2991 destroyed!

Security Considerations

Prevent malicious Close-Out and Clear State

A malicious user could attempt to Close-Out or Clear its Local State to hack the frozen state of a Smart ASA. Consider the following scenario:

  • Smart ASA default_frozen = false;
  • Eve has regularly opted-in to the Smart ASA;
  • Eve receives 5 Smart ASA from Bob (Smart ASA manager and freezer) and get freezed afterwards;
  • Eve can now Close-Out or Clear its state and Opt-In again to clear its frozen state and spend the Smart ASA.

To avoid this situation, the reference implementation introduces:

  • Close-Out condition: succeeds if and only if Eve has no holdings of the Smart ASA;
  • Opt-In condition: set frozen status of the account to True if upon the opt-in, after a Clear State, the account holds an amount of Smart ASA.

Conscious Smart ASA Destroy

Upon a call to asset_destroy, the GlobalState of the Smart ASA App is reset and the Underlying ASA destroyed. However, the LocalState of opted-in users is not affected. Let's consider the case a manager invokes an asset_destroy over Smart ASA A and afterwards an asset_create to instantiate Smart ASA B with the same Smart ASA App.

  • Eve was opted-in to Smart ASA App and was not frozen;
  • Bob (manager) destroys Smart ASA A (assuming circulating_supply = 0);
  • Bob creates Smart ASA B with param default_frozen = True;
  • Eve is opted-in with frozen = False;
  • Eve can freely receive and spend Smart ASA B.

To avoid this issue, the reference implementation includes the current smart_asa_id both in GlobalState and LocalState. Smart ASA transfers can now be approved only for users opted-in with the current Underlying ASA.

Conclusions

Smart ASA reference implementation is a building block that shows how regular ASA can be turned into a more poweful and sophisticated L1 tool. By adopting ABI the Smart ASA will be easily interoperable and composable with the rest of Algorand's ecosystem (e.g. wallets, chain explorers, external dApp, etc.).

This reference implementation is intended to be used as initial step for more specific and customized transferability logic like: royalties, DAOs' assets, NFTs, in-game assets etc.

We encourage the community to expand and customize this new tool to fit specific dApp!

Enjoy experimenting and building with Smart ASA!