How to Add Custom Assets or Tokens to Nautilus Wallet: A Developer Guide

Developers can add custom tokens to Nautilus Wallet by creating an IAssetInfo metadata object, persisting it via assetInfoDbService.addIfNotExists(), and refreshing the runtime store with assetsStore.loadMetadata() to display the token in the UI.

While Nautilus Wallet automatically discovers assets from the Ergo blockchain, developers often need to manually register test-net tokens, private tokens, or assets not exposed by the GraphQL endpoint. This guide explains how to programmatically add custom assets or tokens to the nautls/nautilus-wallet browser extension, ensuring they appear in the asset list with proper metadata and icons without requiring server-side changes.

Define the Token Metadata

To register a custom token, first create a metadata object that implements the IAssetInfo interface defined in src/types/database.ts. This interface requires the token ID, name, decimal precision, and asset subtype.

import type { IAssetInfo } from '@/types/database';
import { AssetSubtype } from '@/types/internal';

const MY_TOKEN: IAssetInfo = {
  id: '0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef', // token-ID (hex string)
  name: 'MyCustomToken',
  decimals: 2,
  subtype: AssetSubtype.Fungible,
  // optional visual assets
  artworkUrl: 'https://example.com/my-token.png',
  artworkCover: 'https://example.com/my-token-cover.png'
};

The AssetSubtype enum (located in src/types/internal.ts) categorizes the token as Fungible, PictureArtwork, or other types, affecting how the UI renders the asset.

Persist Metadata to Local Storage

Once defined, persist the metadata using assetInfoDbService.addIfNotExists() from src/database/assetInfoDbService.ts. This method inserts records only when the token ID is not already present, preventing duplicate entries.

import { assetInfoDbService } from '@/database/assetInfoDbService';

async function registerCustomToken() {
  await assetInfoDbService.addIfNotExists([MY_TOKEN]);
}

This step stores the token permanently in the browser's local IndexedDB, making it available across wallet sessions.

Load Metadata into the Runtime Store

After persistence, you must refresh the in-memory cache so the UI can display the token name, decimals, and icon. Call assetsStore.loadMetadata() from src/stores/assetsStore.ts, which checks the local database first via assetInfoDbService.getAnyOf() and falls back to the GraphQL service only for missing items.

import { useAssetsStore } from '@/stores/assetsStore';

async function refreshMetadata() {
  const assetsStore = useAssetsStore();
  await assetsStore.loadMetadata([MY_TOKEN.id]);   // forces remote-fetch only if needed
}

This step bridges the gap between the database layer and the reactive UI store, ensuring the token appears instantly in the Assets view.

Register Custom Icons (Optional)

To display a custom SVG or PNG instead of the default placeholder, map the token ID to an icon filename in assetIconMap from src/mappers/assetIconMap.ts. Place the actual image file under public/icons/assets/.

// src/mappers/assetIconMap.ts
export const assetIconMap = new Map<string, string>([
  // …existing entries…
  [
    '0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef',
    'my-token.svg'   // place the file under public/icons/assets/
  ]
]);

The <AssetIcon> component uses this map to resolve the correct graphic for each token in the asset list.

Complete Implementation Example

Combine these steps into a reusable utility module. This example shows how to register a token programmatically and refresh the UI from a Vue component.

// src/utils/customToken.ts
import type { IAssetInfo } from '@/types/database';
import { AssetSubtype } from '@/types/internal';
import { assetInfoDbService } from '@/database/assetInfoDbService';
import { useAssetsStore } from '@/stores/assetsStore';

// 1️⃣ Token metadata
const MY_TOKEN: IAssetInfo = {
  id: '0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef',
  name: 'MyCustomToken',
  decimals: 2,
  subtype: AssetSubtype.Fungible,
  artworkUrl: 'https://example.com/my-token.png'
};

// 2️⃣ Persist metadata (run once, e.g., on extension install)
export async function registerMyToken() {
  await assetInfoDbService.addIfNotExists([MY_TOKEN]);
}

// 3️⃣ Refresh the runtime store (call after registration or when the app boots)
export async function refreshMyTokenMetadata() {
  const assetsStore = useAssetsStore();
  await assetsStore.loadMetadata([MY_TOKEN.id]);
}

You can trigger registration from a UI button or automatically on startup for test-net environments:

<!-- src/components/AddCustomTokenButton.vue -->
<template>
  <Button @click="addToken" variant="outline">
    Add MyCustomToken
  </Button>
</template>

<script setup lang="ts">
import { registerMyToken, refreshMyTokenMetadata } from '@/utils/customToken';
import { useToast } from '@/components/ui/toast';

const toast = useToast();

async function addToken() {
  try {
    await registerMyToken();          // persists the metadata
    await refreshMyTokenMetadata();   // updates the in-memory store
    toast.success('MyCustomToken added successfully');
  } catch (e) {
    toast.error('Failed to add token: ' + (e as Error).message);
  }
}
</script>

Summary

Frequently Asked Questions

What is the IAssetInfo interface in Nautilus Wallet?

The IAssetInfo interface is defined in src/types/database.ts and serves as the contract for token metadata within the wallet. It requires properties including id (the token's unique hex string), name, decimals for precision, and subtype (imported from src/types/internal.ts). Optional fields like artworkUrl allow you to specify remote images for the token icon.

How does assetInfoDbService prevent duplicate token entries?

The addIfNotExists method in src/database/assetInfoDbService.ts checks whether a token ID already exists in the local IndexedDB before inserting. It accepts an array of IAssetInfo objects and only adds new records, ensuring that calling the registration function multiple times does not create duplicate entries or overwrite existing metadata.

Can I add custom icons for my tokens?

Yes. Map the token's hex ID to a local icon filename in assetIconMap from src/mappers/assetIconMap.ts. Place the SVG or PNG file in the public/icons/assets/ directory. The wallet's <AssetIcon> component automatically resolves and displays the custom graphic when rendering the asset list, falling back to a default placeholder if no mapping exists.

Do I need to restart the wallet after adding a custom token?

No. After persisting the metadata with assetInfoDbService.addIfNotExists(), simply invoke assetsStore.loadMetadata([tokenId]) to refresh the in-memory cache immediately. The token will appear in the Assets view without requiring a browser extension reload or application restart, as the store update triggers reactive UI changes.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →