Skip to main content

Asset Design Considerations

Issuing and distribution accounts

It is best practice on the Stellar network to create two accounts when issuing an asset: 1) the issuing account and 2) the distribution account.

The issuing account creates (or mints) the asset on the network by executing a payment operation. The issuing account will always be linked to the asset’s identity. Any account wanting to hold the asset must first establish a trustline with the issuing account. Read about trustlines in our Trustlines section.

The distribution account is the first recipient of the issued asset and handles all other transactions.

Note that you can also issue an asset by creating an offer or liquidity pool deposit with the issuing account.

It is best practice to issue an asset by sending it from the issuing account to a distribution account for two main reasons: security and auditing.

Security

The distribution account will be a hot account, meaning that some web service out there has direct access to sign its transactions.

For example, if the account you're distributing from is also the issuing account and it is compromised by a malicious actor, the actor can now issue as much of the asset as they want. If the malicious actor redeems the newly issued tokens with an anchor service, the anchor may not have the liquidity to support the customer withdrawals. Stakes are lower if you use a distribution account- if the distribution account is compromised, you can freeze the account’s asset balance and start with a new distribution account without changing the issuing account.

Auditing

Using a distribution account is better for auditing because an issuing account can’t actually hold a balance of its own asset. Sending an asset back to its issuing account burns (deletes) the asset. If you have a standing inventory of the issued asset in a separate account, it’s easier to track and can help with bookkeeping.

Naming an asset

One thing you must decide when issuing an asset is what to call it. An asset code is the asset’s identifying code. There are three possible formats: Alphanumeric 4, Alphanumeric 12, and liquidity pool shares.

Learn about liquidity pool shares in the Liquidity Pool Encyclopedia Entry.

  • Alphanumeric 4-character maximum: Any characters from the set a-z, A-Z, 0-9 are allowed. The code can be shorter than 4 characters, but the trailing characters must all be empty.
  • Alphanumeric 12-character maximum: Any characters from the set a-z, A-Z, 0-9 are allowed. The code can be any number of characters from 5 to 12, but the trailing characters must all be empty.
  • The pool share asset is defined by the liquidity pool identifier (PoolID), which in turn is defined by the two assets its reserves are composed of.

Provided it falls into one of these buckets, you can choose any asset code you like. That said, if you’re issuing a currency, you should use the appropriate ISO 4217 code, and if you’re issuing a stock or bond, the appropriate ISIN number. Doing so makes it easier for Stellar interfaces to properly display and sort your token in their listings and allows potential token holders to understand what your token represents.

Controlling access to an asset with flags

When you issue an asset on Stellar, anyone can hold it by default. In general, that’s a good thing: easy access means better reach and better liquidity. However, if you need to control access to an asset to comply with regulations (or for any other reason), you can easily do so by enabling flags on your issuing account.

Flags are created on the account level using a set_options operation. They can be set at any time in the life cycle of an asset, not just when you issue it.

Flag types

The (0xn) next to each flag type denotes the bit settings for each flag.

Authorization Required (0x1)

When AUTH_REQUIRED_FLAG is enabled, an issuer must approve an account before that account can hold its asset. This setting allows issuers to vet potential token holders and to approve trustlines.

To allow access, the user creates a trustline, and the issuer approves it by changing the AUTHORIZE flag with the Set_Trust_Line_Flag operation.

There are two levels of authorization an asset issuer can grant using the Set_Trust_Line_Flag operation:

  • AUTHORIZED_FLAG: signifies complete authorization allowing an account to transact freely with the asset to make and receive payments and place orders.
  • AUTHORIZED_TO_MAINTAIN_LIABILITIES_FLAG: denotes limited authorization that allows an account to maintain current orders but not to otherwise transact with the asset.

Authorization Revocable (0x2)

When AUTH_REVOCABLE_FLAG is enabled, an issuer can revoke an existing trustline’s authorization, thereby freezing the asset held by an account. Doing so prevents that account from transferring or trading the asset and cancels the account’s open orders for the asset.

AUTH_REVOCABLE_FLAG also allows an issuer to reduce authorization from complete to limited, which prevents the account from transferring or trading the asset but does not cancel the account’s open orders for the asset. This setting is useful for issuers of regulated assets who need to authorize transactions on a case-by-case basis to ensure each conforms to certain requirements.

All changes to asset authorization are performed with the Set Trustline Flags operation.

There are three levels of authorization an asset issuer can remove using the Set_Trust_Line_Flag operation:

  • AUTHORIZED_FLAG: signifies complete authorization allowing an account to transact freely with the asset to make and receive payments and place orders.
  • AUTHORIZED_TO_MAINTAIN_LIABILITIES_FLAG: denotes limited authorization that allows an account to maintain current orders but not to otherwise transact with the asset.
  • CLAWBACK_ENABLED: enables the issuing account to take back (burning) all of the asset. See our section on Clawbacks for more information.

Clawback Enabled (0x8)

With the AUTH_CLAWBACK_ENABLED_FLAG flag set, any subsequent trustlines established with this account will have clawbacks enabled. You can read more about clawbacks (and selectively controlling them on a per-trustline basis) here.

Note that this flag requires that revocable is also set.

Authorization Immutable (0x4)

With this setting, none of the other authorization flags (AUTH_REQUIRED_FLAG, AUTH_REVOCABLE_FLAG) can be set, and the issuing account can’t be merged. You set this flag to signal to potential token holders that your issuing account and its assets will persist on the ledger in an open and accessible state.

Set Trustline Flag operation

The issuing account can configure various authorization and trustline flags for individual trustlines to an asset. The asset parameter is of the TrustLineAsset type. If you are modifying a trustline to a regular asset (i.e. one in a Code:Issuer format), this is equivalent to the asset type. If you are modifying a trustline to a pool share, this is the liquidity pool’s unique ID.

Example flow

Let’s look at how an issuer of a regulated asset might use the AUTHORIZED_TO_MAINTAIN_LIABILITIES_FLAG flag.

If the issuer wants to approve transactions on a case-by-case basis while allowing accounts to maintain offers, they can leave an account in the AUTHORIZED_TO_MAINTAIN_LIABILITIES_FLAG state. That account can own offers but cannot do anything else with the asset.

To initiate a new operation, the holding account requests that the issuer approve and sign a transaction. Once the issuer inspects the operation and decides to approve it, they sandwich it between a set of operations, first granting authorization, then reducing it.

Here’s a payment from A to B sandwiched between set_trust_line_flags operations:

  • Operation 1: Issuer uses SetTrustLineFlags to fully authorize account A, asset X
  • Operation 2: Issuer uses SetTrustLineFlags to fully authorize account B, asset X
  • Operation 3: Payment from A to B
  • Operation 4: Issuer uses SetTrustLineFlags to set account B, asset X to AUTHORIZED_TO_MAINTAIN_LIABILITIES_FLAG state
  • Operation 5: Issuer uses SetTrustLineFlags to set account A, asset X to AUTHORIZED_TO_MAINTAIN_LIABILITIES_FLAG state

The authorization sandwich allows the issuer to inspect the specific payment and to grant authorization for it and it alone. Since operations bundled in a transaction are simultaneous, A and B are only authorized for the specific, pre-approved payment operation. Complete authorization does not extend beyond the specific transaction.

Sample code

In the following code samples, proper error checking is omitted. However, you should always validate your results, as there are many ways that requests can fail. Refer to the Error Handling Encyclopedia Entry for tips on error management strategies.

The following example sets authorization to be both required and revocable:

var StellarSdk = require("stellar-sdk");
var server = new StellarSdk.Horizon.Server(
"https://horizon-testnet.stellar.org",
);

// Keys for issuing account
var issuingKeys = StellarSdk.Keypair.fromSecret(
"SCZANGBA5YHTNYVVV4C3U252E2B6P6F5T3U6MM63WBSBZATAQI3EBTQ4",
);

server
.loadAccount(issuingKeys.publicKey())
.then(function (issuer) {
var transaction = new StellarSdk.TransactionBuilder(issuer, {
fee: 100,
networkPassphrase: StellarSdk.Networks.TESTNET,
})
.addOperation(
StellarSdk.Operation.setOptions({
setFlags: StellarSdk.AuthRevocableFlag | StellarSdk.AuthRequiredFlag,
}),
)
// setTimeout is required for a transaction
.setTimeout(100)
.build();
transaction.sign(issuingKeys);
return server.submitTransaction(transaction);
})
.then(console.log)
.catch(function (error) {
console.error("Error!", error);
});

Limiting the supply of an asset

Warning! This section details how to lock your account with the purpose of limiting the supply of your issued asset. However, locking your account means you’ll never be able to do anything with it ever again- whether that’s adjusting signers, changing the home domain, claiming any held XLM, or any other operation. Your account will be completely frozen.

With that warning in mind:

It is possible to lock down the issuing account of an asset so that the asset’s supply cannot increase. To do this, first set the issuing account’s master weight to 0 using the Set Options operation. This prevents the issuing account from being able to sign transactions and therefore, making the issuer unable to issue any more assets. Be sure to do this only after you’ve issued all desired assets to the distribution account. If the asset has a Stellar Asset Contract, also make sure the admin for the contract was not updated from the default (which is the issuer) using the set_admin contract call. If the admin was not the issuer, then the admin would be able to mint the asset even with the issuing account locked.

Learn more about signature weights in the Signature and Multisig Encyclopedia Entry.

See how to do this in step five of the Issuing an Asset Tutorial.