Skip to main content

List your dapp on the Chromia Testnet Vault

This guide provides step-by-step instructions for listing your decentralized application (dapp) on the Chromia Testnet Vault. Following these steps will make your dapp discoverable to users and streamline the onboarding process.

Prerequisites

  • A deployed dapp on the Chromia Testnet
  • Access to your dapp's codebase
  • Media files for your dapp (icons, screenshots, etc.)
  • tCHR tokens: Follow the instructions in Get test tokens (tCHR) to obtain tokens.

Listing steps

To list your dapp, you need to implement the following query and supporting functions in your dapp's codebase:

1. Provide the dapp metadata

You can choose one of these methods to store your dapp's metadata:

1.1. Provide the dapp metadata in a hardcoded way

Refer to the vault listing repository for a complete project example.

Implement the following code to hardcode the metadata:

./main.rell
enum dapp_content_type {
landscape,
portrait,
promotional,
video,
icon
}

struct dapp_media {
name: text;
url: text;
type: dapp_content_type;
}

query find_dapp_details(dapp_rowid: rowid, requested_content_types: list<dapp_content_type>? = null) {
return (
rowid = 123, // random value
name = "Hello World name", // name of the project
description = "Hello World Description", // description of the project
launch_url = "https://hello_world.io", // launch ur of the project
genre = "AARPG",
chain_list = [
(
name = "hello_world", // name of the blockchain which is listed in the chromia.yml file of your dapp
brid = byte_array("AB01..........................25FA"), // brid of the deployed blockchain. This brid can be found in the termanal when the dapp gets deployed into the container
role = "hello world role"
)
],
content = get_dapp_media(requested_content_types)
).to_gtv_pretty();
}

function get_dapp_media(requested_content_types: list<dapp_content_type>? = null): list<dapp_media>? {

val ec_media: map<dapp_content_type, dapp_media> = [
dapp_content_type.icon: dapp_media(name = "icon1", url = "https://link.to.icon1.png", dapp_content_type.icon), // url of the media uploaded to Filehub
dapp_content_type.landscape: dapp_media(name = "landscape1", url = "https://link.to.landscape1.png", dapp_content_type.landscape), // url of the media uploaded to Filehub
dapp_content_type.portrait: dapp_media(name = "portrait1", url = "https://link.to.portrait1.png", dapp_content_type.portrait), // url of the media uploaded to Filehub
dapp_content_type.promotional: dapp_media(name = "promotional1", url = "https://link.to.promotional1.png", dapp_content_type.promotional), // url of the media uploaded to Filehub
];

if (not empty(requested_content_types)) {

val media = list<dapp_media>();

for (type_requested in requested_content_types) {
if (ec_media.contains(type_requested)) {
media.add(ec_media[type_requested]);
}
}

return if (media.size() > 0) media else null;
}

return null;
}

1.2. Provide the dapp metadata from the dapp database

Refer to the database-based vault listing repo.

To store metadata in the database:

  1. Define admin-only operations to manage data:

    ./main.rell
    struct module_args {
    admin_pubkey: byte_array;
    }
    function require_admin_signer() = require(op_context.is_signer(chain_context.args.admin_pubkey), "The operation requires admin signer");
  2. Add metadata configuration in chromia.yml:

    ./chromia.yml
    # Add `admin_pubkey` and `dapp_name`
    blockchains:
    hello_world:
    module: main
    moduleArgs:
    main:
    admin_pubkey: 022...70
    dapp_name: My Dapp Name

    For more details, see Module Args.

  3. Create the following entities:

    ./main.rell
    enum dapp_content_type {
    landscape,
    portrait,
    promotional,
    video,
    icon
    }

    entity dapp {
    key name;
    mutable description: text = "";
    mutable launch_url: text = "";
    mutable genre: text = "";
    }

    entity dapp_media {
    key dapp, name;
    mutable url: text = "";
    type: dapp_content_type;
    }

    entity blockchain {
    key dapp, brid: byte_array;
    index brid;
    index mutable name: text;
    mutable role: text;
    }
  4. Create operations and helper functions to seed dapp and blockchain data:

    ./main.rell
    operation create_or_update_dapp(description: text, launch_url: text, genre: text) {
    require_admin_signer();
    functions.create_or_update_dapp(chain_context.args.dapp_name, description, launch_url, genre);
    }

    operation create_or_update_blockchain(brid: byte_array, chain_name: text, role: text) {
    require_admin_signer();
    val dapp = get_dapp_by_name(chain_context.args.dapp_name);
    functions.create_or_update_blockchain(dapp, brid, chain_name, role);
    }

    function get_dapp_by_name(name) = dapp @ { name };

    namespace functions {
    function create_or_update_blockchain(dapp, brid: byte_array, name: text, role: text) {
    val blockchain = blockchain @? { brid };
    if (empty(blockchain)) {
    create blockchain ( dapp, brid, name, role );
    } else update blockchain ( name, role );
    }

    function create_or_update_dapp(name, description: text, launch_url: text, genre: text) {
    val dapp = dapp @? { name };
    if (empty(dapp)) {
    create dapp ( name, description, launch_url, genre );
    } else {
    update dapp ( description, launch_url, genre );
    }
    }
    }
  5. Integrate the find_dapp_details query into your dapp's codebase. This query retrieves and formats your dapp's details:

    ./main.rell
    query find_dapp_details(dapp_rowid: rowid, requested_content_types: list<dapp_content_type>? = null){
    val dapp = get_dapp_by_name(chain_context.args.dapp_name);
    return map_dapp_details(dapp, requested_content_types);
    }

    Ignore the rowid in the query, but keep it in the query signature.

  6. Implement supporting functions for the query:

    ./main.rell
    function map_dapp_details(dapp, requested_content_types: list<dapp_content_type>? = null) {
    val blockchains = find_and_map_dapp_blockchains(dapp);
    val dapp_media = if (not empty(requested_content_types)) find_and_map_dapp_media(
    dapp,
    requested_content_types
    ) else null;

    return (
    rowid = dapp.rowid,
    name = dapp.name,
    description = dapp.description,
    launch_url = dapp.launch_url,
    genre = dapp.genre,
    chain_list = blockchains,
    content = dapp_media
    ).to_gtv_pretty();
    }

    function find_and_map_dapp_blockchains(dapp) =
    blockchain @* { dapp } (
    @omit @sort .rowid,
    name = .name,
    brid = .brid,
    role = .role
    );

    function find_and_map_dapp_media(dapp, requested_content_types: list<dapp_content_type>) =
    dapp_media @* {
    dapp,
    .type in requested_content_types
    } (
    @omit @sort .rowid,
    name = .name,
    url = .url,
    type = .type
    );
  7. (Optional) Implement functionality to handle media:

    ./main.rell
    namespace functions {
    function create_or_update_dapp_media(dapp_name: text, name, url: text, type: dapp_content_type) {
    val dapp = dapp @? {dapp_name == .name};
    require(not empty(dapp), "Dapp not found");
    val media = dapp_media @? {dapp!!, name};
    if (empty(media)) {
    create dapp_media (dapp, name, url, type);
    } else {
    update media(url);
    }
    }
    }

    operation create_or_update_dapp_media(name, url: text, type: dapp_content_type) {
    require_admin_signer();
    functions.create_or_update_dapp_media(chain_context.args.dapp_name, name, url, type);
    }
  8. Seed the metadata by calling the operations implemented above:

    • Create dapp metadata in the database:

          chr tx \
      --network testnet \
      --blockchain <hello_world> \
      -brid <004...BF> \
      --secret ~/.chromia/dep_test \
      create_or_update_dapp <description> <launch_url> <genre>
    • Create blockchain related data:

          chr tx \
      --network testnet \
      --blockchain <hello_world> \
      -brid <004...BF> \
      --secret ~/.chromia/dep_test \
      create_or_update_blockchain 'x"D2...3B2"' hello_world "hello world role"
    • Create media related data:

          chr tx \
      --network testnet \
      --blockchain <hello_world> \
      -brid <004...BF> \
      --secret ~/.chromia/dep_test \
      create_or_update_dapp_media icon icon.png 0
    • Check if the dapp metadata saved:

          chr query \
      --network testnet \
      --blockchain <hello_world> \
      -brid <004...BF> \
      --output-format json \
      find_dapp_details \
      dapp_rowid=0 \
      'requested_content_types=[]'

For more detailed information, refer to the chr command documentation and the Postchain client installation guide.

2. Update the deployment

chr deployment update --settings chromia.yml --network testnet --blockchain hello_world

3. Prepare your media content (optional)

Upload your media files (such as screenshots, icons, etc.). While any image URL can be used, it is recommended to upload your images to Filehub for better integration.

4. Automatic listing

Once you implement the query and functions, your dapp will be automatically listed in the Chromia Testnet Vault based on the information it provides.

5. Verification for a checkmark (optional)

To receive a verified checkmark on the Vault:

  1. Contact the Chromia team admin for final verification.
  2. The admin will review your dapp to ensure it meets the required quality standards.

Example: storing the data

To store the required data, use the setUpMocks.ts script with the Filehub media links. For more details, check the following GitLab repository: GitLab: dapp-aggregator setupMocks.ts.