Creating Tokens

Before minting any token supply, you must first design the token itself.

"Tokens" are digital assets that can be traded, sold, or used on the Enjin Blockchain.

📘

What you'll need:

  • Some Enjin Coin on Enjin Matrixchain to process transactions and at least 0.01 ENJ to add to the tokens' Backing Value.
    You can obtain cENJ (Canary ENJ) for testing from the Canary faucet.
  • A Platform Account.
  • A Collection to place the tokens in.

Enjin Blockchain allows you to create customized Token ID structures. This flexibility enables you to organize your tokens in various ways that suit your needs.

  • For Fungible Tokens (FTs): These tokens are identical in value and function, and therefore, they all share the same Token ID. This commonality in ID reflects their interchangeable nature. For instance, all units of a specific cryptocurrency like Bitcoin would have the same identification as they hold the same value and are indistinguishable from one another in terms of usage and worth.
  • For Non-Fungible Tokens (NFTs): Every NFT has a unique Token ID that sets it apart from other tokens, even within the same collection. This unique ID is crucial for establishing the individuality and provenance of each NFT, which could represent anything from digital art to ownership rights over a virtual asset.

🚧

Token ID Structure Best Practices

Before minting the mainnet versions of your Tokens, that will be used in your live economy. Make sure to take a look at the best practices for Token ID structure.

There are two ways to use the Create Asset functionalities:

Option A. Using the Enjin Dashboard

In the Platform menu, navigate to "Tokens". Then, click the "Create Token" button.

From here, you can customize your collection's Mint Policy, Market Policy, and Attributes.

  • Create Token Section - Basic token options. Make sure to select the Collection ID you wish to mint the token in, the token ID, and the recipient in the corresponding fields.
    Make sure to check out the TokenID Structure Best Practices.
  • Cap - The token cap (if required).
  • Token Royalty Settings - The market behavior for the token.
  • Attributes - Set the token details which are details stored in pairs, like a title and its content. Certain attributes, such as the URI, name, and description, have special roles that are understood by many platforms and marketplaces. If you're new, simply link to a JSON file that lists all the token's details. Make sure to check out the Managing Metadata page.

Once you're satisfied with the options, click on the "Create" button at the bottom right corner to create the request.

The Transaction Request will then appear in the "Transactions" menu.

A notification appears after you create a <<glossary:Mutation>>.

A notification appears after you create a Mutation.

Clicking "**View**" on the notification will take you to your Transactions List.

Clicking "View" on the notification will take you to your Transactions List.

Since this transaction is a Mutation, you will need to sign the transaction using your Wallet.

  • If a Wallet Daemon is running and configured, the transaction request will be signed automatically.
  • If a wallet is connected such as the Enjin Wallet or Polkadot.js, the transaction must be signed manually by clicking the "Sign" button and approving the signature request in your wallet.

Once your token is created, lets give it a new look by Adding Metadata

Option B. Using the Enjin API & SDKs

CreateToken mutation enables you to create a new token within an existing collection. This operation is essential for introducing new digital assets, and it allows you to define various attributes and characteristics for the newly created token.

mutation CreateToken
(
  $recipient: String!
  $collection_id: BigInt!
  $token_id: BigInt
  $initial_supply: BigInt
  $cap: TokenMintCapType!
) {
  CreateToken(
    recipient: $recipient #The recipient of the initial supply
    collectionId: $collection_id #Set the collection ID
    params:{
      tokenId: {integer: $token_id} #Set the token ID
      initialSupply: $initial_supply #Mint initial supply
      cap: {type: $cap}} #Define supply type
  ) {
    id
    method
    state
  }
}
using System.Text.Json;
using Enjin.Platform.Sdk;

// Define the token parameters
var tokenParams = new CreateTokenParams()
    .SetTokenId(new EncodableTokenIdInput().SetInteger(0)) //Set the token ID
    .SetInitialSupply(1) //Mint initial supply
    .SetCap(new TokenMintCap().SetType(TokenMintCapType.Infinite)); //Define supply type

// Setup the mutation
var createToken = new CreateToken()
    .SetRecipient("cxLU94nRz1en6gHnXnYPyTdtcZZ9dqBasexvexjArj4V1Qr8f") //The recipient of the initial supply
    .SetCollectionId(2406) //Set the collection ID
    .SetParams(tokenParams); //Set the previously defined token params

// Define and assign the return data fragment to the mutation
var createTokenFragment = new TransactionFragment()
    .WithId()
    .WithMethod()
    .WithState();

createToken.Fragment(createTokenFragment);

// Create and auth a client to send the request to the platform
var client = PlatformClient.Builder()
    .SetBaseAddress("https://platform.canary.enjin.io")
    .Build();
client.Auth("Your_Platform_Token_Here");

// Send the request and write the output to the console.
// Only the fields that were requested in the fragment will be filled in,
// other fields which weren't requested in the fragment will be set to null.
var response = await client.SendCreateToken(createToken);
Console.WriteLine(JsonSerializer.Serialize(response.Result.Data));
#include "EnjinPlatformSdk/CoreMutations.hpp"
#include <iostream>

using namespace enjin::platform::sdk;
using namespace std;

int main() {

    // Setup mutation data
    shared_ptr<CreateTokenParams> tokenParams = make_shared<CreateTokenParams>();

    shared_ptr tokenId = make_shared<EncodableTokenIdInput>();
    tokenId->SetInteger(make_shared<SerializableString>("0"));

    shared_ptr<TokenMintCap> tokenMintCap = make_shared<TokenMintCap>();
    tokenMintCap->SetType(TokenMintCapType::Infinite);

    tokenParams->SetTokenId(tokenId);
    tokenParams->SetInitialSupply(make_shared<SerializableString>("1"));
    tokenParams->SetCap(tokenMintCap);

    // Setup mutation
    CreateToken createToken = CreateToken();
    createToken
        .SetRecipient(make_shared<SerializableString>("cxLU94nRz1en6gHnXnYPyTdtcZZ9dqBasexvexjArj4V1Qr8f"))
        .SetCollectionId(make_shared<SerializableString>("2406"))
        .SetParams(tokenParams);

    // Define and assign the return data fragment to the mutation
    shared_ptr<TransactionFragment> transactionFragment = make_shared<TransactionFragment>();
    transactionFragment
        ->WithId()
        .WithMethod()
        .WithState();

    createToken.SetFragment(transactionFragment);

    // Create and auth a client to send the request to the platform
    unique_ptr<PlatformClient> client = PlatformClient::Builder()
            .SetBaseAddress("https://platform.canary.enjin.io")
            .Build();
    client->Auth("Your_Platform_Token_Here");

    // Send the request then get the response and write the output to the console.
    // Only the fields that were requested in the fragment will be filled in,
    // other fields which weren't requested in the fragment will be set to null.
    future<shared_ptr<IPlatformResponse<GraphQlResponse<Transaction>>>> futureResponse = SendCreateToken(*client, createToken);

    // Get the platform response holding the HTTP data
    PlatformResponsePtr<GraphQlResponse<Transaction>> response = futureResponse.get();

    // Get the result, a GraphQL response, holding the GraphQL data
    const optional<GraphQlResponse<Transaction>>& gqlResult = response->GetResult();

    // Write the result data to the console
    if (gqlResult.has_value() && gqlResult->IsSuccess())
    {
        const optional<Transaction>& transaction = gqlResult->GetData()->GetResult();

        std::cout << to_string(transaction->GetId().value()) << std::endl;
        std::cout << ToString(transaction->GetMethod().value()) << std::endl;
    }

    // Write any error messages to the console
    if (gqlResult.has_value() && gqlResult->HasErrors())
    {
        const optional<vector<GraphQlError>>& errors = gqlResult->GetErrors();

        for (const GraphQlError& error : errors.value()) {
            std::cout << error.GetMessage().value() << std::endl;
        }
    }

    client.reset();

    return 0;
}

fetch('https://platform.canary.enjin.io/graphql', {
  method: 'POST',
  headers: {'Content-Type': 'application/json','Authorization': 'Your_Platform_Token_Here'},
  body: JSON.stringify({
    query: `
      mutation CreateToken
      (
        $recipient: String!
        $collection_id: BigInt!
        $token_id: BigInt
        $initial_supply: BigInt
        $cap: TokenMintCapType!
      ) {
        CreateToken(
          recipient: $recipient #The recipient of the initial supply
          collectionId: $collection_id #Set the collection ID
          params:{
            tokenId: {integer: $token_id} #Set the token ID
            initialSupply: $initial_supply #Mint initial supply
            cap: {type: $cap}} #Define supply type
        ) {
          id
          method
          state
        }
      }
    `,
    variables: {
      recipient: "cxLU94nRz1en6gHnXnYPyTdtcZZ9dqBasexvexjArj4V1Qr8f",
      collection_id: 2406,
      token_id: 0,
      initial_supply: 1,
      cap: "INFINITE"
    }
  }),
})
.then(response => response.json())
.then(data => console.log(data));
const axios = require('axios');

axios.post('https://platform.canary.enjin.io/graphql', {
  query: `
    mutation CreateToken
    (
      $recipient: String!
      $collection_id: BigInt!
      $token_id: BigInt
      $initial_supply: BigInt
      $cap: TokenMintCapType!
    ) {
      CreateToken(
        recipient: $recipient #The recipient of the initial supply
        collectionId: $collection_id #Set the collection ID
        params:{
          tokenId: {integer: $token_id} #Set the token ID
          initialSupply: $initial_supply #Mint initial supply
          cap: {type: $cap}} #Define supply type
      ) {
        id
        method
        state
      }
    }
  `,
  variables: {
    recipient: "cxLU94nRz1en6gHnXnYPyTdtcZZ9dqBasexvexjArj4V1Qr8f",
    collection_id: 2406,
    token_id: 0,
    initial_supply: 1,
    cap: "INFINITE"
  }
}, {
  headers: { 'Content-Type': 'application/json', 'Authorization': 'Your_Platform_Token_Here' }
})
.then(response => console.log(response.data))
.catch(error => console.error(error));
import requests

query = '''
  mutation CreateToken
  (
    $recipient: String!
    $collection_id: BigInt!
    $token_id: BigInt
    $initial_supply: BigInt
    $cap: TokenMintCapType!
  ) {
    CreateToken(
      recipient: $recipient #The recipient of the initial supply
      collectionId: $collection_id #Set the collection ID
      params:{
        tokenId: {integer: $token_id} #Set the token ID
        initialSupply: $initial_supply #Mint initial supply
        cap: {type: $cap}} #Define supply type
    ) {
      id
      method
      state
    }
  }
'''

variables = {
  'recipient': 'cxLU94nRz1en6gHnXnYPyTdtcZZ9dqBasexvexjArj4V1Qr8f',
  'collection_id': 2406,
  'token_id': 0,
  'initial_supply': 1,
  'cap': 'INFINITE'
}

response = requests.post('https://platform.canary.enjin.io/graphql',
	json={'query': query, 'variables': variables},
	headers={'Content-Type': 'application/json', 'Authorization': 'Your_Platform_Token_Here'}
)
print(response.json())

A WebSocket event will also be fired so you can pick up the changes in real-time by listening to the app channel on the WebSocket.

🚧

For Token ID management, head to Best Practices > TokenID Structure

📘

More Fields and Arguments Available!

The examples here illustrate basic uses of the CreateToken mutation. However, this mutation supports many more arguments and settings not shown in these examples.

For a comprehensive overview of all available settings and their descriptions for the CreateToken mutation, please refer to our GraphQL Schema on Apollo.

This resource will help you to fully utilize the capabilities of the CreateToken mutation and tailor it to your specific needs.

For instance, you'll find settings such as adding attributes/royalties/supply type and much more with the CreateTokenParams object, or the ability to sign using a managed wallet with the signingAccount argument.

👍

To add metadata to your token, go to the Adding Metadata tutorial.