Skip to content

These new docs are in beta. Please submit bugs to GitHub issues.


Issue an Asset

There is no dedicated operation to create an asset on Stellar. Instead, assets are created with a payment operation: an issuing account makes a payment using the asset it’s issuing, and that payment actually creates the asset on the network.

It’s a pretty simple process that requires four operations: one to create an issuing account, one to create a distribution account, one to establish a trustline, and one to make a payment.

Note: you don’t actually have to issue assets to a dedicated distribution account: you can issue them to any account with the requisite trustline. However, using a distribution account is the recommended practice, and it makes the process a lot easier to explain. So that’s how we’ll do it in this guide.

The code to create those operations and submit them as transactions is below. Here, we’ll walk through each step so that the process makes sense. You can breeze through to get a general understanding, or you can use the Stellar laboratory, which is an interface that allows you to create and submit transactions, to actually follow along and issue a token right here right now.

One caveat: if you are creating a token on the public network, there is an additional prerequisite. You need a funded account to provide the XLM necessary to create the issuing and distribution accounts.

Create the Issuing and Distribution Accounts

The issuing account is the origin of the asset, and will be forever linked to the asset’s identity. The distribution account is the first recipient of the asset. In the final step of this process, you’ll create the asset by sending a payment from the issuing account to the distribution account.

There are two steps to account creation:

  1. Generate a keypair
  2. Fund the account using a create_account operation

You can generate a keypair for free, but an account doesn’t exist on the Stellar ledger until it is funded with XLM to cover the minimum balance.

If you’re issuing an asset on the testnet, you can fund your accounts by getting free test XLM from Friendbot. If you’re issuing an asset in production, you will need to use an existing account to send enough live XLM to cover the minimum balance, transaction fees, and, in the case of the distribution account, a trustline. If you’re not sure where to acquire XLM, consult our Lumen Buying Guide.

Rule of thumb for production: don’t skirt too close to the minimum network balance. If you do, you may not have enough XLM to do what you need to do. Since the network minimum balance and transaction fees are low, it doesn’t require much to get started. 5 XLM should be sufficient. 100 XLM is even better.

Create a Trustline

Stellar requires accounts to explicitly opt-in to holding an asset by creating a trustline, and in this step the distribution account submits a change_trust operation to do just that. The trustline goes from the distribution account to the issuing account.

The distribution account specifies the issuer’s public key and the asset code in the change_trust operation, and since it’s the first time the asset code appears on the ledger, that means the distribution account actually names the asset, not the issuing account.

Currently, there are two supported formats for asset codes.

  • 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.

Any asset code works provided it falls into one of those two buckets. 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, at a glance, what your token represents.

Make a Payment

This is the step where the magic happens. The issuing account makes a payment to the distribution account using the newly named asset, and tokens exist where before there were none. Presto!

As long as the issuing account remains unlocked, it can continue to create new tokens by making payments to the distribution account, or to any other account with the requisite trustline. If you want to change that and put a hard limit on the number of tokens an issuing account can issue, check out our instructions on how to lock an account.

If you’re planning to do, really, anything with your asset, your next step is to complete a stellar.toml file to provide wallets, exchanges, market listing services, and potential token holders with the information they need to understand what it represents.

Once you’ve done that, you can also create a sell offer to get your asset onto the Stellar decentralized exchange, and put some effort into market making to create liquidity for it.

Sample Code

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

// Keys for accounts to issue and receive the new asset
var issuingKeys = StellarSdk.Keypair.fromSecret(
  "SCZANGBA5YHTNYVVV4C3U252E2B6P6F5T3U6MM63WBSBZATAQI3EBTQ4",
);
var receivingKeys = StellarSdk.Keypair.fromSecret(
  "SDSAVCRE5JRAI7UFAVLE5IMIZRD6N6WOJUWKY4GFN34LOBEEUS4W2T2D",
);

// Create an object to represent the new asset
var astroDollar = new StellarSdk.Asset("AstroDollar", issuingKeys.publicKey());

// First, the receiving account must trust the asset
server
  .loadAccount(receivingKeys.publicKey())
  .then(function (receiver) {
    var transaction = new StellarSdk.TransactionBuilder(receiver, {
      fee: 100,
      networkPassphrase: StellarSdk.Networks.TESTNET,
    })
      // The `changeTrust` operation creates (or alters) a trustline
      // The `limit` parameter below is optional
      .addOperation(
        StellarSdk.Operation.changeTrust({
          asset: astroDollar,
          limit: "1000",
        }),
      )
      // setTimeout is required for a transaction
      .setTimeout(100)
      .build();
    transaction.sign(receivingKeys);
    return server.submitTransaction(transaction);
  })
  .then(console.log)

  // Second, the issuing account actually sends a payment using the asset
  .then(function () {
    return server.loadAccount(issuingKeys.publicKey());
  })
  .then(function (issuer) {
    var transaction = new StellarSdk.TransactionBuilder(issuer, {
      fee: 100,
      networkPassphrase: StellarSdk.Networks.TESTNET,
    })
      .addOperation(
        StellarSdk.Operation.payment({
          destination: receivingKeys.publicKey(),
          asset: astroDollar,
          amount: "10",
        }),
      )
      // 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);
  });

Last updated Jul. 13, 2020

Page Outline