NLO

SDK Overview

Embed NLO in three steps

Add NLO's hosted deposit flow to your app in three steps. Your users deposit once through the widget, while NLO's patent-pending orchestration engine scans liquidity pools and vaults, routes capital, and manages the Ultra-Safe strategy behind the scenes.

What NLO handles. The hosted iframe handles wallet connection, supported assets, chain switching, ERC-20 approvals, the deposit transaction, and attribution. Your app receives SDK events for UX, analytics, quests, and post-deposit actions.
V1 is a Widget SDK, not a public REST API. Partners embed the widget and listen for SDK events. Treat browser callbacks as best-effort and reconcile important actions against the on-chain tx_hash. Direct REST APIs, webhooks, and fully white-labelled flows are planned for later tiers.

What is Ultra-Safe?

Ultra-Safe is the NLO strategy your users deposit into through the widget. NLO handles opportunity scanning, routing, execution, and rebalancing automatically — so you can embed managed DeFi yield without building the infrastructure yourself.

Discovery 25,000+ pools scanned

NLO continuously reviews liquidity pools and 1,000+ vault opportunities to identify where capital can be routed.

Aggregation One SDK surface

Your app embeds one hosted flow while NLO aggregates the routing, wallet approvals, deposit execution, and attribution.

Self-custody Non-custodial

Users deposit from their own wallet into an on-chain vault and hold the position themselves. NLO never takes custody of user funds.

1. Install SDK

The SDK is published as an npm package — works in React, Vue, Next.js, Angular, Svelte, and vanilla JS bundlers. Requires Node 18+.

Terminal
npm install @nlofinance/widget

On a fresh install (no existing .nlowidget.json in the project root) the package prints a one-line banner reminding you to run the signup wizard:

Output
👋 Welcome to the NLO partner widget.
   Run this to set up your partner account:

     npx nlo-init

The banner is suppressed on subsequent installs (when .nlowidget.json exists) and on non-interactive installs (npm ci, Docker, CI).

Once installed and your partner slug is created, jump to 3. Render the widget for copy-paste-ready React or Vue components.

2. Create partner slug

npx nlo-init is an interactive wizard shipped with the SDK. Run it once per project, in a real terminal:

Terminal
npx nlo-init

What it prompts for

FieldValidationNotes
company_name 2–64 chars Drives your generated slug (e.g. "Your Company"your-company). Collisions auto-suffix to your-company-2, etc.
website_url Valid http(s):// URL The origin allowed to render the widget. The iframe's Origin check enforces this — only requests from this domain attribute commissions to your account.
payout_wallet 0x + 40 hex chars EVM address where NLO will pay accrued commissions. Validated against /^0x[0-9a-fA-F]{40}$/.
contact_email Standard email format For payout notifications and account recovery. Not displayed publicly.

Wizard output

Terminal session
$ npx nlo-init

[NLO Widget] 👋 Welcome. Let's set up your NLO partner account.

? Company name: … Your Company
? Production domain (https://...): … https://example.com
? Payout wallet (0x... 42 chars): … 0x0000000000000000000000000000000000000000
? Contact email: … you@example.com

[NLO Widget] Submitting signup to https://nlo.finance …
[NLO Widget] ✓ Done! Your partner slug: your-company
[NLO Widget] ✓ Saved to .nlowidget.json (safe to commit — slug is public).

[NLO Widget] Use it in your code:

    import NLOWidget from "@nlofinance/widget";
    NLOWidget.mount("#nlo-widget", {
      partner: "your-company",
      title: "Your Company",
    });

What gets written: .nlowidget.json

.nlowidget.json
{
  "slug": "your-company",
  "company_name": "Your Company",
  "website_url": "https://example.com",
  "payout_wallet": "0x0000000000000000000000000000000000000000",
  "contact_email": "you@example.com",
  "signed_up_at": "2026-05-28T10:14:31.000Z",
  "source": "self-service"
}

Commit this file to git. The slug is public — it's in your bundle anyway when you mount the widget. Committing it means every developer / CI run uses the same slug without having to re-register.

Safe to re-run

  • Existing config. If .nlowidget.json already exists and has a slug, the wizard exits with "Already configured" — no prompts, no API call.
  • Same domain, different machine. If your domain is already registered (e.g. a coworker ran the wizard first), the backend returns the existing slug and the wizard saves it automatically. Same slug for every dev on the team — no duplicate rows.
  • Non-interactive context. CI / Docker / piped stdin / npm ci all skip the wizard silently. Run npx nlo-init manually in a real terminal once (commit the result), then CI will use the committed config.

3. Render the widget

Drop one of these components into your project. The slug comes from .nlowidget.json (written by npx nlo-init) so partners never hard-code it.

The widget auto-sizes its own iframe (SDK v1.0.0+) — you don't need to wire onResize or track height in component state. Just mount it.

Functional component with useEffect for mount / unmount cleanup. Works in CRA, Vite + React, Next.js app router, and Next.js pages router.

1. The component

components/NLODeposit.jsx
import { useEffect, useRef } from "react";
import NLOWidget from "@nlofinance/widget";
import config from "../.nlowidget.json";   // adjust path to your project root

export default function NLODeposit() {
  const hostRef = useRef(null);

  useEffect(() => {
    const handle = NLOWidget.mount(hostRef.current, {
      partner: config.slug,
      title: config.company_name,
    });
    return () => handle.unmount();
  }, []);

  return <div ref={hostRef} style={{ width: 480, maxWidth: "100%" }} />;
}

2. Use it on a page

App.jsx
import NLODeposit from "./components/NLODeposit";

export default function App() {
  return (
    <main>
      <h1>Deposit into NLO Ultra-Safe</h1>
      <NLODeposit />
    </main>
  );
}
Next.js app router: add "use client" at the top of NLODeposit.jsx — the widget uses window and must run client-side.

Single-file component using the Composition API (<script setup>). Works in Vite + Vue, Nuxt 3 (inside <ClientOnly>), and any standard Vue 3 project.

1. The component

components/NLODeposit.vue
<script setup>
import { onBeforeUnmount, onMounted, ref } from "vue";
import NLOWidget from "@nlofinance/widget";
import config from "../../.nlowidget.json";   // adjust path to your project root

const hostEl = ref(null);
let handle = null;

onMounted(() => {
  handle = NLOWidget.mount(hostEl.value, {
    partner: config.slug,
    title: config.company_name,
  });
});

onBeforeUnmount(() => {
  if (handle) handle.unmount();
});
</script>

<template>
  <div ref="hostEl" style="width: 480px; max-width: 100%"></div>
</template>

2. Use it on a page

App.vue
<script setup>
import NLODeposit from "./components/NLODeposit.vue";
</script>

<template>
  <main>
    <h1>Deposit into NLO Ultra-Safe</h1>
    <NLODeposit />
  </main>
</template>
Nuxt 3: wrap in <ClientOnly> in your page template so SSR doesn't try to render the widget (it needs window).

No framework? For a quick prototype, mount it from an inline <script type="module">. For production apps, prefer the npm package so your build can pin versions and bundle the SDK normally.

index.html
<div id="nlo-widget" style="width: 480px; max-width: 100%"></div>

<script type="module">
  import NLOWidget from "https://cdn.jsdelivr.net/npm/@nlofinance/widget@1/dist/index.esm.js";

  // Inline the slug from .nlowidget.json — for plain HTML you can't
  // import JSON natively, so paste the value:
  const SLUG = "your-slug-from-nlowidget-json";

  NLOWidget.mount("#nlo-widget", {
    partner: SLUG,
    title: "Your App",
  });
</script>

Tip: if you're bundling with Webpack / Rollup / esbuild, change the import to import NLOWidget from "@nlofinance/widget" and let your bundler resolve it.

Mount options

NLOWidget.mount(target, options) mounts the hosted iframe inside the target element and returns a handle. Call handle.unmount() to clean up when your framework component unmounts.

Options

OptionTypeDescription
partnerstring or object requiredYour partner slug (e.g. "demo") or a signup object. Most projects read config.slug from .nlowidget.json.
embedOriginstringOverride the iframe origin. Default: "https://nlo.finance". Useful only for staging or local backend testing.
widthstringIframe width. Default: "100%".
heightstringInitial iframe height before the first resize event. Bare numbers become px. Default: "620".
autoResizebooleanAuto-size the iframe to its content. Default: true. Set false for fixed-height iframes; onResize still fires.
titlestringOptional display title shown above VIA NLO in the widget header. Display-only; does not affect attribution, routing, fees, or deposits.
onReadyfunctionFires after the iframe loads /widget/config and is ready for input. Payload: { partner, supported_chains }.
onWalletConnectfunctionFires when user connects. Payload: { wallet }.
onWalletDisconnectfunctionFires when user disconnects. No payload.
onAssetSelectfunctionUser picked an asset. Payload: { chain, token_symbol }.
onDepositSubmittedfunctionQuote returned before signing. Payload: { chain, token_symbol, amount_display, amount_raw, wallet }.
onDepositCompletefunctionDeposit is recorded on NLO's backend. Payload: { chain, token_symbol, amount_display, amount_raw, tx_hash, deposit_uuid, wallet }.
onDepositFailedfunctionAny failure. Payload: { step, message, chain?, wallet? }.
onResizefunctionIframe height changed. Payload: height_px number.

Connect Wallet & Deposit Flow

Here's what the user sees end-to-end. The widget handles wallet connection (Reown AppKit — MetaMask, WalletConnect, Coinbase Wallet), chain switching, ERC-20 approval, and the deposit transaction. Your page does not need to listen for events unless you want custom analytics, quest logic, or post-deposit UI.

Step 1 Connect wallet

User taps Connect — Reown AppKit opens (MetaMask, WalletConnect, Coinbase). Attribution binds to your partner slug automatically.

Step 2 Pick asset & amount

Live balances per (token, chain). The user picks an asset and enters the amount they want to deposit.

Step 3 Approve & deposit

One tap handles chain switch, ERC-20 approval, and the deposit. The widget shows the on-chain tx_hash on success.

Step-by-step

  1. Connect wallet. Tap Connect wallet → Reown AppKit modal opens with MetaMask, WalletConnect QR, Coinbase Wallet, etc. The widget binds the connected wallet to your partner account (Tier 2 attribution) automatically.
  2. Pick an asset. The widget shows live balances for every supported (token, chain) combo. User picks an asset and types an amount.
  3. Approve + Deposit. One tap. The widget handles chain switching, the ERC-20 approve transaction (if not already approved), and the deposit transaction in sequence. The user signs in their wallet; the widget polls for confirmations and records the deposit on NLO's backend.
  4. Success. The widget shows a success screen with the on-chain tx hash. If you wired optional events, onDepositComplete also gives you { tx_hash, chain, token_symbol, amount_display, amount_raw, deposit_uuid, wallet }.

Optional events

You do not need events for a basic embed. Use them only if your app wants to update UI, send analytics, advance a quest, or run custom post-deposit logic. For a TaskOn-style placement, you can skip this section entirely.

Events are optional and best-effort. SDK events fire in the user's browser. If the tab closes or the network drops before onDepositComplete arrives, your page can miss it. Do not use callbacks as your only source of truth for quests, rewards, or commission accounting — reconcile important actions against the on-chain tx_hash.
optional-events.js
NLOWidget.mount("#nlo-widget", {
  partner: "your-slug",
  title: "Your App",

  onDepositComplete: (e) => {
    // Optional: use the real on-chain tx hash for your own UX or analytics.
    console.log("deposit complete:", e.tx_hash);
  },

  onDepositFailed: (e) => {
    // Optional: capture this for debugging or support.
    console.warn("deposit failed at", e.step, "-", e.message);
  },
});

Set the display title

The V1 widget keeps customization intentionally small. Partners can set a display title for the header above VIA NLO. Deeper visual customization is handled by NLO during partner onboarding so the deposit flow stays readable, compliant, and easy to support.

title.js
NLOWidget.mount("#nlo-widget", {
  partner: "your-slug",
  title: "Your App",
});