How to Add a New Built-in dApp to Nautilus Wallet: 6-Step Implementation Guide

To add a new built-in dApp to Nautilus Wallet, create a Vue component in src/dapps/, register the dApp in src/dapps/dappsManifest.ts, and add a route in src/extension/popup/router.ts after providing translations and an icon.

Nautilus Wallet is an open-source browser extension wallet for the Ergo blockchain that ships with built-in dApps like SigmaUSD and Wallet Optimizer. If you are contributing to the nautls/nautilus-wallet repository, adding a new built-in dApp to Nautilus Wallet follows a predictable pipeline that integrates your component into the existing Vue 3 architecture.

Prerequisites and Overview

Before adding a built-in dApp, ensure you have the repository cloned and dependencies installed (pnpm install). The wallet uses Vue 3 with TypeScript, Vue Router, and Lucide icons. Built-in dApps are not external websites; they are first-party Vue components rendered inside the wallet's popup interface.

The process involves six distinct steps: creating the UI component, adding translation keys, providing an icon, registering in the manifest, configuring the router, and optionally adding tests.

Step-by-Step Process for Adding a Built-in dApp

1. Create the dApp UI Component

Create a new directory under src/dapps/ containing your Vue single-file component. Use the existing built-in dApps as templates.

<!-- src/dapps/my-dapp/MyDappView.vue -->
<script setup lang="ts">
import { ref } from "vue";

const message = ref("Hello from My dApp!");
</script>

<template>
  <div class="p-4">
    <h2 class="text-xl font-bold">{{ message }}</h2>
    <!-- dApp-specific UI goes here -->
  </div>
</template>

<style scoped>
/* optional component-scoped CSS */
</style>

The component is lazy-loaded by the router, so you can import services, composables, or heavy libraries without impacting initial load time. Reference src/dapps/sigma-usd/SigmaUSDView.vue or src/dapps/wallet-optimization/WalletOptimizationDApp.vue for production patterns.

2. Add Translation Entries

Add a title key for your dApp in every locale file under src/i18n/locales/. At minimum, update en-US.json.

// src/i18n/locales/en-US.json
{
  "dapps": {
    "myDapp": {
      "title": "My Awesome dApp"
    }
  }
}

Repeat this entry in all supported locale files (e.g., es.json, ru.json). The tileKeypath in the manifest will reference this key as myDapp.title.

3. Provide an Icon

You have two options for the dApp tile icon:

Option A: SVG Asset

Place an SVG file in your dApp directory and import it as a Vue component:

// In src/dapps/dappsManifest.ts
import MyDappLogo from "./my-dapp/my-dapp-logo.svg";

Option B: Lucide Icon

Import any icon from the lucide-vue-next library:

import { Coffee } from "lucide-vue-next";

The icon must be a Vue component that can be rendered dynamically with <component :is="...">.

4. Register the dApp in the Manifest

Open src/dapps/dappsManifest.ts and add your dApp to the dappsManifest array. Use the component() helper defined in the same file to wrap your icon.

// src/dapps/dappsManifest.ts
import { component } from "./dappsManifest"; // helper function
import MyDappLogo from "./my-dapp/my-dapp-logo.svg";

export const dappsManifest: DAppManifestItem[] = [
  // ...existing entries (SigmaUSD, Wallet Optimizer)
  {
    tileKeypath: "myDapp.title",      // matches translation key
    icon: component(MyDappLogo),       // or component(Coffee, { size: 24 })
    path: "/dapps/my-dapp"            // URL route
  }
];

The manifest drives the tile grid displayed in src/views/dapps/DappsList.vue. Each entry automatically generates a clickable card that navigates to the specified path.

5. Add the Router Entry

Expose your component under /dapps/<path> by adding a child route in src/extension/popup/router.ts. Insert the object into the children array of the /dapps route (around lines 60-68).

// src/extension/popup/router.ts
{
  path: "my-dapp",
  name: "my-dapp",
  component: () => import("@/dapps/my-dapp/MyDappView.vue")
}

The router automatically constructs the full path /dapps/my-dapp, which must match the path declared in the manifest. The lazy-load function ensures the component code is only fetched when the user navigates to the dApp.

6. (Optional) Add Tests and Documentation

Maintain code quality by adding unit tests under tests/unit/ that mount your component and verify basic rendering. If your dApp exposes a public API for other connectors, document it in docs/dapp-connector/.

Complete Working Example

Here is the minimal set of changes required to add a dApp named "Portfolio Tracker":

Translation (src/i18n/locales/en-US.json):

{
  "dapps": {
    "portfolioTracker": {
      "title": "Portfolio Tracker"
    }
  }
}

Manifest (src/dapps/dappsManifest.ts):

import { component } from "./dappsManifest";
import PortfolioIcon from "./portfolio-tracker/portfolio-icon.svg";

export const dappsManifest: DAppManifestItem[] = [
  {
    tileKeypath: "portfolioTracker.title",
    icon: component(PortfolioIcon),
    path: "/dapps/portfolio-tracker"
  }
];

Router (src/extension/popup/router.ts):

{
  path: "portfolio-tracker",
  name: "portfolio-tracker",
  component: () => import("@/dapps/portfolio-tracker/PortfolioTrackerView.vue")
}

After running pnpm dev, the "Portfolio Tracker" tile appears in the dApps gallery and navigates to your component.

Key Files Reference

Purpose File Path Source Link
dApp manifest registry src/dapps/dappsManifest.ts View on GitHub
Popup router configuration src/extension/popup/router.ts View on GitHub
dApp list UI (tile grid) src/views/dapps/DappsList.vue View on GitHub
Translation files src/i18n/locales/*.json View en-US
Example: SigmaUSD dApp src/dapps/sigma-usd/SigmaUSDView.vue View on GitHub
Example: Wallet Optimizer src/dapps/wallet-optimization/WalletOptimizationDApp.vue View on GitHub

Summary

  • Create a Vue 3 component under src/dapps/<your-dapp>/ to house the user interface.
  • Translate by adding a title key under dapps.<name>.title in every locale file.
  • Icon your dApp using either an SVG import or a lucide-vue-next component.
  • Register the dApp in src/dapps/dappsManifest.ts with tileKeypath, icon, and path.
  • Route the component in src/extension/popup/router.ts as a child of /dapps with matching path.
  • Test your implementation by running pnpm dev and verifying the tile appears in the dApps gallery.

Frequently Asked Questions

Do I need to manually update the dApps list view to show my new tile?

No. The DappsList.vue component automatically renders tiles based on the dappsManifest array defined in src/dapps/dappsManifest.ts. As long as you register your dApp in the manifest with a valid tileKeypath and icon, the UI will display it without additional changes to the list view component.

Can I use external npm libraries in my built-in dApp?

Yes. Since built-in dApps are standard Vue 3 components, you can import external libraries from npm. The component is lazy-loaded via the router's dynamic import, so heavy dependencies will only be fetched when the user navigates to your dApp. Follow the existing patterns in SigmaUSDView.vue for service integration.

How do I handle state management within a built-in dApp?

Built-in dApps can use Vue's Composition API with ref and reactive for local state, or integrate with the wallet's existing composables located in the src/composables/ directory. For global state shared across the extension, import the relevant stores from src/stores/. The Wallet Optimizer dApp demonstrates complex state synchronization with the wallet's UTXO set.

What is the difference between a built-in dApp and a connected dApp?

Built-in dApps are first-party Vue components bundled directly into the extension source code and rendered inside the wallet's popup UI. Connected dApps are external websites that communicate with the wallet via the dApp Connector API (ERG-47). Built-in dApps have full access to internal services and don't require connection permissions, while connected dApps operate in a sandboxed browser context.

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 →