Tokenize an asset using mosaics.
Imagine that a private company, ComfyClothingCompany, decides that it wants to go public. Instead of a traditional IPO, the company decides to do an STO to issue tokens through the Symbol platform.
In this guide, we are going to help this company to create the tokens for their STO.
symbol.xym
to pay for transaction fees.ComfyClothingCompany decides to create their STO mosaic with the following configuration:
Property | Value | Description |
---|---|---|
Divisibility | 0 | Although brokerages and investment firms can fractionalize shares, the traditional minimum number of shares an investor can purchase from the open market is 1. |
Duration | 0 | Shares of the company should exist as long as the company is in business. The ComfyClothingCompany sets this property to 0, creating a non-expiring mosaic. |
Supply mutable | True | ComfyClothingCompany sets the initial supply of the mosaic to a typical startup amount of 10,000,000 authorized shares. As the company grows, it could choose to increase the number of shares, so the supply mutable is set to true . |
Transferable | True | Once the initial shares are distributed, the shares will be on the market to be traded in public. Thus, the transferability property needs to be set to true. |
Restrictable | True | Since STOs are regulated, the mosaic creator should be able to restrict which accounts can transact with the mosaic. |
// replace with network type
const networkType = NetworkType.TEST_NET;
// replace with private key
const privateKey = '1111111111111111111111111111111111111111111111111111111111111111';
const account = Account.createFromPrivateKey(privateKey, networkType);
// replace with duration (in blocks)
const duration = UInt64.fromUint(0);
// replace with custom mosaic flags
const isSupplyMutable = true;
const isTransferable = true;
const isRestrictable = true;
// replace with custom divisibility
const divisibility = 0;
const nonce = MosaicNonce.createRandom();
const mosaicDefinitionTransaction = MosaicDefinitionTransaction.create(
Deadline.create(),
nonce,
MosaicId.createFromNonce(nonce, account.address),
MosaicFlags.create(isSupplyMutable, isTransferable, isRestrictable),
divisibility,
duration,
networkType);
// replace with network type
const networkType = symbol_sdk_1.NetworkType.TEST_NET;
// replace with private key
const privateKey = '1111111111111111111111111111111111111111111111111111111111111111';
const account = symbol_sdk_1.Account.createFromPrivateKey(privateKey, networkType);
// replace with duration (in blocks)
const duration = symbol_sdk_1.UInt64.fromUint(0);
// replace with custom mosaic flags
const isSupplyMutable = true;
const isTransferable = true;
const isRestrictable = true;
// replace with custom divisibility
const divisibility = 0;
const nonce = symbol_sdk_1.MosaicNonce.createRandom();
const mosaicDefinitionTransaction = symbol_sdk_1.MosaicDefinitionTransaction.create(symbol_sdk_1.Deadline.create(), nonce, symbol_sdk_1.MosaicId.createFromNonce(nonce, account.address), symbol_sdk_1.MosaicFlags.create(isSupplyMutable, isTransferable, isRestrictable), divisibility, duration, networkType);
// replace with node endpoint
try (final RepositoryFactory repositoryFactory = new RepositoryFactoryVertxImpl(
"http://api-01.us-east-1.096x.symboldev.network:3000")) {
final NetworkType networkType = repositoryFactory.getNetworkType().toFuture().get();
// replace with private key
final String privateKey = "1111111111111111111111111111111111111111111111111111111111111111";
final Account account = Account
.createFromPrivateKey(privateKey, networkType);
// replace with duration (in blocks)
final BlockDuration duration = new BlockDuration(10);
// replace with custom mosaic flags
final boolean isSupplyMutable = true;
final boolean isTransferable = true;
final boolean isRestrictable = true;
// replace with custom divisibility
final int divisibility = 0;
final MosaicNonce nonce = MosaicNonce.createRandom();
final MosaicDefinitionTransaction mosaicDefinitionTransaction = MosaicDefinitionTransactionFactory
.create(networkType,
nonce,
MosaicId.createFromNonce(nonce, account.getPublicAccount()),
MosaicFlags.create(isSupplyMutable, isTransferable, isRestrictable),
divisibility,
duration)
.build();
// replace with mosaic units to increase
const delta = 1000000;
const mosaicSupplyChangeTransaction = MosaicSupplyChangeTransaction.create(
Deadline.create(),
mosaicDefinitionTransaction.mosaicId,
MosaicSupplyChangeAction.Increase,
UInt64.fromUint(delta * Math.pow(10, divisibility)),
networkType);
// replace with mosaic units to increase
const delta = 1000000;
const mosaicSupplyChangeTransaction = symbol_sdk_1.MosaicSupplyChangeTransaction.create(symbol_sdk_1.Deadline.create(), mosaicDefinitionTransaction.mosaicId, symbol_sdk_1.MosaicSupplyChangeAction.Increase, symbol_sdk_1.UInt64.fromUint(delta * Math.pow(10, divisibility)), networkType);
// replace with mosaic units to increase
final int delta = 1000000;
final MosaicSupplyChangeTransaction mosaicSupplyChangeTransaction = MosaicSupplyChangeTransactionFactory
.create(
networkType,
mosaicDefinitionTransaction.getMosaicId(),
MosaicSupplyChangeActionType.INCREASE,
BigDecimal.valueOf(delta * Math.pow(10, divisibility)).toBigInteger())
.build();
Note
Symbol works with absolute amounts. To get an absolute amount, multiply the number of assets you want to create by 10divisibility. For example, if the mosaic has divisibility 2, to create 10 units (relative) you should define 1000 (absolute) instead.
3. Announce both transactions together using an AggregateTransaction.
Include the network generation hash to make the transaction only valid for your network.
Open nodeUrl + '/node/info'
in a new browser tab and copy the meta.networkGenerationHash
value.
const aggregateTransaction = AggregateTransaction.createComplete(
Deadline.create(),
[
mosaicDefinitionTransaction.toAggregate(account.publicAccount),
mosaicSupplyChangeTransaction.toAggregate(account.publicAccount)],
networkType,
[],
UInt64.fromUint(2000000));
// replace with meta.networkGenerationHash (nodeUrl + '/node/info')
const networkGenerationHash = '1DFB2FAA9E7F054168B0C5FCB84F4DEB62CC2B4D317D861F3168D161F54EA78B';
const signedTransaction = account.sign(aggregateTransaction, networkGenerationHash);
// replace with node endpoint
const nodeUrl = 'http://api-01.us-east-1.096x.symboldev.network:3000';
const repositoryFactory = new RepositoryFactoryHttp(nodeUrl);
const transactionHttp = repositoryFactory.createTransactionRepository();
transactionHttp
.announce(signedTransaction)
.subscribe((x) => console.log(x), (err) => console.error(err));
const aggregateTransaction = symbol_sdk_1.AggregateTransaction.createComplete(symbol_sdk_1.Deadline.create(), [
mosaicDefinitionTransaction.toAggregate(account.publicAccount),
mosaicSupplyChangeTransaction.toAggregate(account.publicAccount)
], networkType, [], symbol_sdk_1.UInt64.fromUint(2000000));
// replace with meta.networkGenerationHash (nodeUrl + '/node/info')
const networkGenerationHash = '1DFB2FAA9E7F054168B0C5FCB84F4DEB62CC2B4D317D861F3168D161F54EA78B';
const signedTransaction = account.sign(aggregateTransaction, networkGenerationHash);
// replace with node endpoint
const nodeUrl = 'http://api-01.us-east-1.096x.symboldev.network:3000';
const repositoryFactory = new symbol_sdk_1.RepositoryFactoryHttp(nodeUrl);
const transactionHttp = repositoryFactory.createTransactionRepository();
transactionHttp
.announce(signedTransaction)
.subscribe((x) => console.log(x), (err) => console.error(err));
final AggregateTransaction aggregateTransaction = AggregateTransactionFactory
.createComplete(
networkType,
Arrays.asList(
mosaicDefinitionTransaction.toAggregate(account.getPublicAccount()),
mosaicSupplyChangeTransaction.toAggregate(account.getPublicAccount())
))
.maxFee(BigInteger.valueOf(2000000)).build();
final String generationHash = repositoryFactory.getGenerationHash().toFuture().get();
final SignedTransaction signedTransaction = account
.sign(aggregateTransaction, generationHash);
final TransactionRepository transactionRepository = repositoryFactory
.createTransactionRepository();
transactionRepository.announce(signedTransaction).toFuture().get();
}
Once the transaction gets confirmed, you can try to transfer one unit of the created mosaic to another account, modify the mosaic properties or link a namespace to the mosaic.
Open a terminal window and run the following command to create a new mosaic.
symbol-cli transaction mosaic
Did you find what you were looking for? Give us your feedback.