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
112 changes: 112 additions & 0 deletions apps/web/app/assets/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
"use client";

import { useState } from "react";
import { PageShell } from "@/components/PageShell";
import { Alert, Button, Card, Input, Label, Select } from "@/components/ui";
import {
DEFAULT_TESTNET_REGISTRY,
checkAssetOnNetwork,
parseAssetString,
getNativeAsset,
} from "@anchorkit/stellar-kit";
import type { StellarAsset, StellarNetwork } from "@anchorkit/types";

const NETWORKS: StellarNetwork[] = ["testnet", "mainnet", "futurenet"];

export default function AssetsPage() {
const [input, setInput] = useState("USDC:GC5HTWCIAUD72MGI7AHMJEF5ZJRKXS7II2PYVYOJEYKN4UYH6QTPCPZV");
const [network, setNetwork] = useState<StellarNetwork>("testnet");
const [result, setResult] = useState<ReturnType<typeof checkAssetOnNetwork> | null>(null);

const native = getNativeAsset();
const registryAssets: StellarAsset[] = [
native,
...DEFAULT_TESTNET_REGISTRY.entries.map((e) => e.asset),
];

function handleCheck() {
const parsed = parseAssetString(input);
if (!parsed.success) {
setResult({
ok: false,
code: "ASSET_INVALID",
error: parsed.error.issues[0]?.message ?? "Invalid asset string",
});
return;
}
setResult(checkAssetOnNetwork(parsed.data, network));
}

return (
<PageShell
title="Asset registry"
subtitle="Network-aware Stellar asset registry and validation. Native XLM is always supported; issued assets are checked against the per-network registry."
>
<div className="grid gap-4">
<Card>
<h2 className="text-base font-semibold tracking-tight">Check an asset</h2>
<div className="mt-3 space-y-3">
<div>
<Label htmlFor="asset-input">Asset (XLM, native, or CODE:ISSUER)</Label>
<Input
id="asset-input"
value={input}
onChange={(e: { target: { value: string } }) => setInput(e.target.value)}
placeholder="USDC:GC5H..."
/>
</div>
<div>
<Label htmlFor="network-select">Network</Label>
<Select
id="network-select"
value={network}
onChange={(e: { target: { value: string } }) => setNetwork(e.target.value as StellarNetwork)}
>
{NETWORKS.map((n) => (
<option key={n} value={n}>
{n}
</option>
))}
</Select>
</div>
<Button onClick={handleCheck}>Check support</Button>

{result && !result.ok && (
<Alert tone="error" title={`${result.code}`}>
{result.error}
</Alert>
)}
{result && result.ok && (
<Alert tone="success" title="Supported">
Supported on {network}.
</Alert>
)}
</div>
</Card>

<Card>
<h2 className="text-base font-semibold tracking-tight">Registered assets (testnet MVP)</h2>
<ul className="mt-3 divide-y divide-ink-200 dark:divide-ink-800">
{registryAssets.map((a) => {
const key = a.type === "native" ? "XLM" : `${a.code}:${a.issuer}`;
const entry = DEFAULT_TESTNET_REGISTRY.byKey.get(key) ?? null;
return (
<li key={key} className="py-2 text-sm">
<div className="font-mono">{key}</div>
<div className="text-ink-500 dark:text-ink-300">
{a.type === "native"
? "Native — supported on all networks."
: entry?.testnetOnly
? "Testnet-only issued asset."
: "Issued asset."}
{entry?.note ? ` ${entry.note}` : ""}
</div>
</li>
);
})}
</ul>
</Card>
</div>
</PageShell>
);
}
81 changes: 81 additions & 0 deletions docs/asset-registry.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
# Network-aware asset registry (issue #23)

AnchorKit provides a typed, network-aware asset registry on top of the shared
`StellarAsset` primitives. It distinguishes native XLM, issued assets,
testnet-only assets, and unsupported assets, and returns a typed
`ASSET_UNSUPPORTED` error for assets that are structurally valid but not
permitted on the target network.

## Core types

```ts
type AssetSupport = "supported" | "testnetOnly" | "unsupported";

interface RegistryEntry {
asset: StellarAsset;
networks: StellarNetwork[]; // networks where the asset is allowed
testnetOnly?: boolean; // demo/testnet issued asset
note?: string;
}

interface AssetLookupResult {
asset: StellarAsset;
network: StellarNetwork;
support: AssetSupport;
entry: RegistryEntry | null;
error: { code: "ASSET_UNSUPPORTED"; message: string } | null;
}
```

## API

- `createAssetRegistry(entries)` — build a registry (native XLM is always
supported implicitly; no need to list it).
- `lookupAsset(asset, network, registry?)` — returns the support state without
throwing.
- `validateAssetOnNetwork(asset, network, registry?)` — validates structure AND
network support; throws `ASSET_INVALID` or `ASSET_UNSUPPORTED`.
- `checkAssetOnNetwork(asset, network, registry?)` — safe variant returning
`{ ok: true, value } | { ok: false, code, error }`.
- `DEFAULT_TESTNET_REGISTRY` — MVP registry defaulting to safe testnet examples
(native XLM + a demo testnet USDC).

## Behaviour

| Asset | testnet | mainnet | futurenet |
| --- | --- | --- | --- |
| Native XLM | supported | supported | supported |
| Registered testnet USDC | supported | testnetOnly (error) | testnetOnly (error) |
| Unregistered issued asset | unsupported (error) | unsupported (error) | unsupported (error) |

## Configuration

The default MVP registry is testnet-first. For production, build your own
registry and pass it explicitly:

```ts
import { createAssetRegistry, validateAssetOnNetwork } from "@anchorkit/stellar-kit";

const registry = createAssetRegistry([
{
asset: { type: "issued", code: "USDC", issuer: "GA5ZSEJ..." },
networks: ["mainnet", "testnet"],
},
]);

const asset = validateAssetOnNetwork(input, "mainnet", registry);
```

Example fixtures live in `examples/assets-registry.testnet.json`.

## UI

`apps/web/app/assets/page.tsx` lets you paste an asset string, pick a network,
and see the support state (including the `ASSET_UNSUPPORTED` message for
disallowed assets), plus the list of registered assets.

## Notes

- No secrets are involved — issuers are public Stellar accounts.
- The MVP ships only testnet demo assets by default; consumers supply
mainnet production lists via a custom registry.
24 changes: 24 additions & 0 deletions examples/assets-registry.testnet.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
{
"network": "testnet",
"entries": [
{
"asset": {
"type": "native",
"code": "XLM",
"issuer": null
},
"networks": ["testnet", "mainnet", "futurenet"],
"note": "Native lumens — supported on every network."
},
{
"asset": {
"type": "issued",
"code": "USDC",
"issuer": "GC5HTWCIAUD72MGI7AHMJEF5ZJRKXS7II2PYVYOJEYKN4UYH6QTPCPZV"
},
"networks": ["testnet"],
"testnetOnly": true,
"note": "Demo testnet USDC (issuer is a generated testnet account)."
}
]
}
Loading