Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions examples/stellar-nuxt-receive/.env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# No environment variables required for the basic receive flow.
# If you want to auto-fetch announcements from a custom RPC, set:
# NUXT_PUBLIC_STELLAR_RPC=https://soroban-testnet.stellar.org
42 changes: 42 additions & 0 deletions examples/stellar-nuxt-receive/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
# stellar-nuxt-receive

Minimal Nuxt 3 app demonstrating `@wraith-protocol/sdk-nuxt`.

Composables (`useStellarStealthKeys`, `useStealthMetaAddress`, etc.) are
**auto-imported** — no explicit import statements needed in your components.

## Setup

```bash
pnpm install
pnpm --filter stellar-nuxt-receive dev
```

## SSR notes

The module registers all composables as Nuxt auto-imports and adds the
packages to `build.transpile` so Nitro can tree-shake them on the server.
Crypto operations are synchronous and environment-agnostic; network calls
only run when explicitly invoked, so server rendering is safe by default.

## Adding the module to your own Nuxt app

```bash
npx nuxi module add @wraith-protocol/sdk-nuxt
```

Or manually in `nuxt.config.ts`:

```ts
export default defineNuxtConfig({
modules: ['@wraith-protocol/sdk-nuxt'],
})
```

Composables available without any import:

- `useStellarStealthKeys()`
- `useEvmStealthKeys()`
- `useSolanaStealthKeys()`
- `useStealthMetaAddress()`
- `useWraith(config?)`
101 changes: 101 additions & 0 deletions examples/stellar-nuxt-receive/app.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
<script setup lang="ts">
// Composables are auto-imported by @wraith-protocol/sdk-nuxt — no import needed.

const signatureInput = ref('');

// useStellarStealthKeys is auto-imported from @wraith-protocol/sdk-nuxt
const {
keys,
stealthAddress,
metaAddress,
loading,
error,
deriveKeys,
generateAddress,
encodeMetaAddress,
} = useStellarStealthKeys();

const signatureBytes = computed(() => {
const hex = signatureInput.value.replace(/^0x/i, '');
if (hex.length < 128) return null;
const bytes = new Uint8Array(hex.length / 2);
for (let i = 0; i < bytes.length; i++) {
bytes[i] = Number.parseInt(hex.slice(i * 2, i * 2 + 2), 16);
}
return bytes;
});

const canDerive = computed(() => signatureBytes.value !== null && !loading.value);

function toHex(bytes: Uint8Array): string {
return Array.from(bytes)
.map((b) => b.toString(16).padStart(2, '0'))
.join('');
}

function handleDerive() {
if (!signatureBytes.value) return;
const k = deriveKeys(signatureBytes.value);
generateAddress(k.spendingPubKey, k.viewingPubKey);
encodeMetaAddress(k.spendingPubKey, k.viewingPubKey);
}
</script>

<template>
<main style="padding: 2rem; font-family: system-ui, sans-serif; max-width: 640px; margin: 0 auto">
<h1>Wraith SDK — Nuxt Stellar Receive</h1>
<p>
Composables are auto-imported by <code>@wraith-protocol/sdk-nuxt</code>. This page works
server-side rendered and hydrates cleanly on the client.
</p>

<section>
<h2>1. Derive Stealth Keys</h2>
<p>Paste a 64-byte (128 hex chars) wallet signature:</p>
<textarea
v-model="signatureInput"
rows="3"
style="width: 100%; font-family: monospace; font-size: 0.85rem"
placeholder="0xaabbcc..."
aria-label="Wallet signature hex input"
/>
<button :disabled="!canDerive" style="margin-top: 0.5rem" @click="handleDerive">
{{ loading ? 'Deriving…' : 'Derive Keys' }}
</button>

<p v-if="error" style="color: red" role="alert">Error: {{ error }}</p>
</section>

<section v-if="keys" style="margin-top: 1.5rem">
<h2>2. Derived Keys</h2>
<dl>
<dt>Spending public key</dt>
<dd style="word-break: break-all; font-family: monospace; font-size: 0.8rem">
{{ toHex(keys.spendingPubKey) }}
</dd>
<dt style="margin-top: 0.5rem">Viewing public key</dt>
<dd style="word-break: break-all; font-family: monospace; font-size: 0.8rem">
{{ toHex(keys.viewingPubKey) }}
</dd>
</dl>
</section>

<section v-if="metaAddress" style="margin-top: 1.5rem">
<h2>3. Stealth Meta-Address</h2>
<p>Share this address to receive private payments:</p>
<code style="word-break: break-all; display: block; background: #f4f4f4; padding: 0.75rem">
{{ metaAddress }}
</code>
</section>

<section v-if="stealthAddress" style="margin-top: 1.5rem">
<h2>4. Generated Stealth Address</h2>
<dl>
<dt>Stealth address</dt>
<dd style="word-break: break-all; font-family: monospace; font-size: 0.8rem">
{{ stealthAddress.stealthAddress }}
</dd>
</dl>
</section>
</main>
</template>
13 changes: 13 additions & 0 deletions examples/stellar-nuxt-receive/nuxt.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
// https://nuxt.com/docs/api/configuration/nuxt-config
export default defineNuxtConfig({
modules: ['@wraith-protocol/sdk-nuxt'],

// sdk-nuxt module options (all optional)
wraithSdk: {
prefix: '',
},

typescript: {
strict: true,
},
});
19 changes: 19 additions & 0 deletions examples/stellar-nuxt-receive/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
{
"name": "stellar-nuxt-receive",
"private": true,
"type": "module",
"scripts": {
"dev": "nuxt dev",
"build": "nuxt build",
"generate": "nuxt generate",
"preview": "nuxt preview"
},
"dependencies": {
"@wraith-protocol/sdk": "workspace:*",
"@wraith-protocol/sdk-nuxt": "workspace:*",
"nuxt": "^3.13.0"
},
"devDependencies": {
"typescript": "^5.7.0"
}
}
3 changes: 3 additions & 0 deletions examples/stellar-nuxt-receive/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"extends": "./.nuxt/tsconfig.json"
}
5 changes: 5 additions & 0 deletions packages/sdk-nuxt/build.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { defineBuildConfig } from 'unbuild';

export default defineBuildConfig({
declaration: true,
});
38 changes: 38 additions & 0 deletions packages/sdk-nuxt/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
{
"name": "@wraith-protocol/sdk-nuxt",
"version": "0.1.0",
"private": false,
"type": "module",
"exports": {
".": {
"types": "./dist/types.d.ts",
"import": "./dist/module.mjs",
"require": "./dist/module.cjs"
}
},
"main": "./dist/module.cjs",
"module": "./dist/module.mjs",
"types": "./dist/types.d.ts",
"files": [
"dist"
],
"scripts": {
"build": "nuxt-module-build build",
"dev": "nuxt-module-build build --stub",
"typecheck": "tsc --noEmit",
"clean": "rm -rf dist"
},
"dependencies": {
"@nuxt/kit": "^3.13.0",
"@wraith-protocol/sdk": "workspace:*",
"@wraith-protocol/sdk-vue": "workspace:*"
},
"devDependencies": {
"@nuxt/module-builder": "^0.8.0",
"nuxt": "^3.13.0",
"typescript": "^5.7.0"
},
"peerDependencies": {
"nuxt": "^3.0.0"
}
}
2 changes: 2 additions & 0 deletions packages/sdk-nuxt/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export { default } from './module';
export type { ModuleOptions } from './module';
52 changes: 52 additions & 0 deletions packages/sdk-nuxt/src/module.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import { defineNuxtModule, addImports, createResolver } from '@nuxt/kit';

export interface ModuleOptions {
/**
* Prefix for auto-imported composables.
* Set to '' to disable prefix.
* @default ''
*/
prefix?: string;
}

export default defineNuxtModule<ModuleOptions>({
meta: {
name: '@wraith-protocol/sdk-nuxt',
configKey: 'wraithSdk',
compatibility: {
nuxt: '>=3.0.0',
},
},

defaults: {
prefix: '',
},

setup(options, nuxt) {
const { resolve } = createResolver(import.meta.url);
const prefix = options.prefix ?? '';

// Ensure sdk-vue composables (which wrap @wraith-protocol/sdk) are
// transpiled rather than bundled as-is by Nitro on the server.
nuxt.options.build.transpile ??= [];
nuxt.options.build.transpile.push('@wraith-protocol/sdk-nuxt');
nuxt.options.build.transpile.push('@wraith-protocol/sdk-vue');

const composables = [
'useWraith',
'useStellarStealthKeys',
'useEvmStealthKeys',
'useSolanaStealthKeys',
'useStealthMetaAddress',
'useScanner',
];

addImports(
composables.map((name) => ({
name,
as: `${prefix}${name}`,
from: resolve('./runtime/composables'),
})),
);
},
});
16 changes: 16 additions & 0 deletions packages/sdk-nuxt/src/runtime/composables/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
// Re-export all composables from sdk-vue with SSR safety wrappers.
// The underlying sdk-vue composables use Vue's ref/readonly which work fine
// on the server — they just hold no browser-specific state.
//
// Heavy crypto work (key derivation, scanning) is synchronous and pure, so
// it runs identically on server and client. Async network operations
// (fetchAnnouncements) are no-ops until called, so they don't execute
// during SSR unless the component explicitly calls them in a server context.

export { useWraith } from '@wraith-protocol/sdk-vue';
export { useStellarStealthKeys } from '@wraith-protocol/sdk-vue';
export { useEvmStealthKeys } from '@wraith-protocol/sdk-vue';
export { useSolanaStealthKeys } from '@wraith-protocol/sdk-vue';
export { useStealthMetaAddress } from '@wraith-protocol/sdk-vue';
export { useScanner } from '@wraith-protocol/sdk-vue';
export type { ChainType } from '@wraith-protocol/sdk-vue';
19 changes: 19 additions & 0 deletions packages/sdk-nuxt/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
{
"compilerOptions": {
"target": "ES2022",
"module": "ESNext",
"moduleResolution": "bundler",
"declaration": true,
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true,
"outDir": "dist",
"paths": {
"@wraith-protocol/sdk": ["../../src"],
"@wraith-protocol/sdk-vue": ["../sdk-vue/src/index.ts"]
}
},
"include": ["src"],
"exclude": ["node_modules", "dist"]
}
45 changes: 45 additions & 0 deletions packages/sdk-vue/src/composables/useScanner.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import { ref, readonly } from 'vue';
import { MultichainScannerPool } from '@wraith-protocol/sdk';
import type {
ScanInput,
ScanResults,
ProgressEvent,
MultichainScannerPoolOptions,
} from '@wraith-protocol/sdk';

export function useScanner(options?: MultichainScannerPoolOptions) {
const results = ref<ScanResults | null>(null);
const loading = ref(false);
const error = ref<string | null>(null);
const progress = ref<ProgressEvent | null>(null);

const pool = new MultichainScannerPool(options);

pool.on('progress', (e) => {
progress.value = e;
});

async function scan(input: ScanInput, signal?: AbortSignal): Promise<ScanResults> {
loading.value = true;
error.value = null;
try {
const r = await pool.scanAll(input, signal);
results.value = r;
return r;
} catch (e) {
const msg = e instanceof Error ? e.message : 'Scan failed';
error.value = msg;
throw e;
} finally {
loading.value = false;
}
}

return {
results: readonly(results),
loading: readonly(loading),
error: readonly(error),
progress: readonly(progress),
scan,
};
}
1 change: 1 addition & 0 deletions packages/sdk-vue/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,5 @@ export { useStellarStealthKeys } from './composables/useStellarStealthKeys';
export { useEvmStealthKeys } from './composables/useEvmStealthKeys';
export { useSolanaStealthKeys } from './composables/useSolanaStealthKeys';
export { useStealthMetaAddress } from './composables/useStealthMetaAddress';
export { useScanner } from './composables/useScanner';
export type { ChainType } from './composables/useStealthMetaAddress';
12 changes: 12 additions & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,18 @@
export { Wraith, WraithAgent } from './agent/client';
export { Chain } from './agent/types';
export { installReactNativePolyfills } from './compat';
export { MultichainScannerPool } from './scanner-pool';
export type {
ScanInput,
ScanResults,
ProgressEvent,
SupportedChain,
MultichainScannerPoolOptions,
EvmScanInput,
StellarScanInput,
SolanaScanInput,
CkbScanInput,
} from './scanner-pool';
export type {
WraithConfig,
AgentConfig,
Expand Down
Loading