Skip to content
Merged
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
15 changes: 14 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,29 @@ on:
jobs:
test:
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [20, 22]
steps:
- uses: actions/checkout@v4
- uses: pnpm/action-setup@v4
with:
version: 10
- uses: actions/setup-node@v4
with:
node-version: 22
node-version: ${{ matrix.node-version }}
cache: pnpm
- run: pnpm install --frozen-lockfile
- run: pnpm run format:check
- run: pnpm build
- run: pnpm test
bun:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: oven-sh/setup-bun@v2
with:
bun-version: latest
- run: bun install --frozen-lockfile
- run: bun test
- run: bun run build
23 changes: 23 additions & 0 deletions COMPAT.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# Compatibility Matrix

This file tracks the SDK's runtime support across the environments called out in issue #23.

| Runtime | Status | Notes |
| ---------------------------- | ------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Node 20 | Working | Verified with the full `vitest` suite and build output in CI. |
| Node 22 | Working | Verified locally and in CI. This is the current CI baseline. |
| Bun latest | Working | Verified locally with `bun test` and `bun run build`. |
| Deno latest | Partial | Use the npm specifier form, for example `import { deriveStealthKeys } from "npm:@wraith-protocol/sdk@1.x/chains/stellar";`. The pure crypto modules are ESM-friendly; the Stellar announcement parser now lazy-loads `@stellar/stellar-sdk` instead of using `require()`. |
| Cloudflare Workers / workerd | Partial | The SDK itself is fetch-first and Web Crypto-friendly. The same Stellar lazy-import fix applies here. If you use the optional Stellar or Solana peer dependencies, bundle them explicitly. |
| Vercel Edge | Partial | Same story as Workers: the core SDK works with standard Web APIs, while optional peer deps need to be bundled or avoided. |

## Verified Fixes

- Stellar announcement parsing no longer uses `require()` inside an ESM module.
- The Stellar announcement parser now loads `@stellar/stellar-sdk` lazily with `import()`.
- The test suite now includes a regression test for that code path.

## Remaining Caveats

- Deno and Workers support is documented from static review plus the Bun/Node verification we can run in this environment.
- If you need `fetchAnnouncements()` for Stellar on an edge runtime, make sure `@stellar/stellar-sdk` is available to the bundler or runtime.
56 changes: 56 additions & 0 deletions docs/running-on-the-edge.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
# Running Wraith On The Edge

Wraith is designed around ESM, `fetch`, `TextEncoder`, `TextDecoder`, and Web Crypto. That makes the core SDK a good fit for edge runtimes such as Bun, Deno, Cloudflare Workers, and Vercel Edge.

## Bun

Install and run the repository tests with Bun:

```bash
bun install
bun test
bun run build
```

If you only need the package in an app, import the same entry points you would use in Node:

```ts
import { deriveStealthKeys } from '@wraith-protocol/sdk/chains/stellar';
```

## Deno

Use npm specifiers when importing the published package:

```ts
import { deriveStealthKeys } from 'npm:@wraith-protocol/sdk@1.x/chains/stellar';
```

If you need the Stellar announcement parser, make sure the optional `@stellar/stellar-sdk` dependency is available to the runtime or bundler.

## Cloudflare Workers

Workers can consume the ESM entry points directly. The SDK does not require `Buffer` or `fs`, and the Stellar parser now uses dynamic `import()` instead of `require()`.

Minimal worker example:

```ts
import { deriveStealthKeys } from '@wraith-protocol/sdk/chains/stellar';

export default {
async fetch() {
const keys = deriveStealthKeys(new Uint8Array(64).fill(0xaa));
return Response.json({ spendingKeyLength: keys.spendingKey.length });
},
};
```

## Vercel Edge

Use the same import style as Workers. If you are only scanning/generating addresses, the pure crypto exports are the safest path. Optional peer dependencies such as `@stellar/stellar-sdk` and `@solana/web3.js` should be bundled explicitly if you use those chain modules.

## What Changed For Edge Support

- The Stellar announcement parser now lazy-loads `@stellar/stellar-sdk`.
- The pure chain modules are ESM-only and avoid Node-specific APIs.
- Bun is covered in CI so regressions show up before release.
25 changes: 17 additions & 8 deletions src/chains/stellar/announcements.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,13 @@ import type { Announcement } from './types';
import { bytesToHex } from './utils';
import { getDeployment } from './deployments';

let stellarSdkPromise: Promise<typeof import('@stellar/stellar-sdk')> | undefined;

function loadStellarSdk(): Promise<typeof import('@stellar/stellar-sdk')> {
stellarSdkPromise ??= import('@stellar/stellar-sdk');
return stellarSdkPromise;
}

export interface FetchAnnouncementsOptions {
/** Earliest ledger to include, inclusive. Ignored when cursor is provided. */
fromLedger?: number;
Expand Down Expand Up @@ -154,7 +161,7 @@ export async function fetchAnnouncements(
hasMore = false;
continue;
}
const ann = parseAnnouncementEvent(event);
const ann = await parseAnnouncementEvent(event);
if (ann) all.push(ann);
}

Expand Down Expand Up @@ -253,14 +260,11 @@ function parseLedgerRange(message: string): { oldest: number; latest: number } |
};
}

function eventLedger(event: Record<string, unknown>): number | undefined {
const ledger = event.ledger;
return typeof ledger === 'number' ? ledger : undefined;
}

function parseAnnouncementEvent(event: Record<string, unknown>): Announcement | null {
async function parseAnnouncementEvent(
event: Record<string, unknown>,
): Promise<Announcement | null> {
try {
const { xdr, Address } = require('@stellar/stellar-sdk');
const { xdr, Address } = await loadStellarSdk();

const topics = event.topic as string[];
if (!topics || topics.length < 3) return null;
Expand Down Expand Up @@ -289,3 +293,8 @@ function parseAnnouncementEvent(event: Record<string, unknown>): Announcement |
return null;
}
}

function eventLedger(event: Record<string, unknown>): number | undefined {
const ledger = event.ledger;
return typeof ledger === 'number' ? ledger : undefined;
}