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
10 changes: 5 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,10 +60,9 @@ If either party isn't KYC-approved the transaction reverts. The transfer form in
`/asset/[id]` reads this status up front and disables the action with a clear
reason when the connected wallet can't transfer.

### Network & deployed contracts (Testnet)
### Networks & deployed contracts

Network passphrase: `Test SDF Network ; September 2015` · RPC:
`https://soroban-testnet.stellar.org`
**Testnet** · Network passphrase: `Test SDF Network ; September 2015` · Default RPC: `https://soroban-testnet.stellar.org`

| Contract | Contract ID | Explorer |
|-------------|-------------|----------|
Expand All @@ -72,8 +71,9 @@ Network passphrase: `Test SDF Network ; September 2015` · RPC:
| dividend | `CAR4XY3CEBQWFOL27JEWFW34KXSIZA7RFKDQMEIV7ZU723RWY37I2SYX` | [view](https://stellar.expert/explorer/testnet/contract/CAR4XY3CEBQWFOL27JEWFW34KXSIZA7RFKDQMEIV7ZU723RWY37I2SYX) |
| asset-token (sample) | `CBMCWLSQSWUTLUJFCNBHNBSXMUM3XU7NAQ5TSNERW4HA4ZZBYHLG4ECZ` | [view](https://stellar.expert/explorer/testnet/contract/CBMCWLSQSWUTLUJFCNBHNBSXMUM3XU7NAQ5TSNERW4HA4ZZBYHLG4ECZ) |

Registry, compliance and dividend ids are configured via `NEXT_PUBLIC_*` env vars;
per-asset token ids are discovered at runtime from the registry.
Registry, compliance and dividend contract IDs are configured via environment variables (see [Environment Variables](docs/environment-variables.md#contract-ids)). Per-asset token IDs are discovered at runtime from the registry.

To point the app at different deployments (e.g., local, alternative RPC, or Mainnet), see [Environment Variables](docs/environment-variables.md) and [Custom RPC Setup](docs/custom-rpc.md).

## Getting started

Expand Down
103 changes: 103 additions & 0 deletions docs/custom-rpc.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
# Custom and Local RPC Setup

By default, the app connects to the public Stellar Soroban RPC endpoints. For development or testing against a local or custom RPC instance, you can override these endpoints via environment variables.

## Quick Start: Local Soroban Network

To run the app against a **local Soroban quickstart** (standalone network):

1. **Start your local Soroban network:**
```bash
docker run --rm -it \
-p 8000:8000 \
stellar/quickstart:latest \
--standalone
```
This exposes a local Soroban RPC at `http://localhost:8000`.

2. **Set the environment variable in `.env.local`:**
```
NEXT_PUBLIC_TESTNET_RPC_URL=http://localhost:8000
NEXT_PUBLIC_DEFAULT_NETWORK=testnet
```

3. **Start the app:**
```bash
npm run dev
```

The app now talks to your local network. Since the local network has no deployed contracts, you'll need to deploy the registry, compliance, and dividend contracts there first, then set their IDs:

```
NEXT_PUBLIC_TESTNET_REGISTRY_ID=<local-registry-id>
NEXT_PUBLIC_TESTNET_COMPLIANCE_ID=<local-compliance-id>
NEXT_PUBLIC_TESTNET_DIVIDEND_ID=<local-dividend-id>
```

## Custom Remote RPC Provider

To point the app at a different RPC provider (e.g., a private or alternative public endpoint):

```
NEXT_PUBLIC_TESTNET_RPC_URL=https://your-custom-rpc.example.com
```

## Failover / Multiple RPC URLs

The app supports RPC failover: if the primary RPC URL fails **three consecutive times**, the app automatically switches to the next URL in the fallback list.

To configure multiple RPC URLs for redundancy:

```
NEXT_PUBLIC_TESTNET_RPC_URL=https://primary-rpc.example.com
NEXT_PUBLIC_TESTNET_RPC_URLS_FALLBACK=https://fallback-1.example.com,https://fallback-2.example.com
```

The app tries URLs in order:
1. **Primary:** `https://primary-rpc.example.com`
2. **Fallback 1:** `https://fallback-1.example.com` (after 3 failures against primary)
3. **Fallback 2:** `https://fallback-2.example.com` (after 3 failures against fallback 1)
4. **Public default:** `https://soroban-testnet.stellar.org` (always available as a last resort)

### How Failover Works

- Each RPC error increments a failure counter for the current URL.
- After **3 consecutive failures**, the cached RPC client is invalidated and rebuilt against the next URL in the list, with the failure counter reset.
- If only one URL is configured (or all URLs are exhausted), the failure counter still increments but no failover occurs—errors bubble up to the UI.
- The counter is **per network** (Testnet and Mainnet track failures independently) and **per browser session** (resets on page reload).

### Example: High-Availability Setup

```
NEXT_PUBLIC_TESTNET_RPC_URL=https://rpc-1.myinfra.com
NEXT_PUBLIC_TESTNET_RPC_URLS_FALLBACK=https://rpc-2.myinfra.com,https://rpc-3.myinfra.com,https://soroban-testnet.stellar.org
NEXT_PUBLIC_MAINNET_RPC_URL=https://mainnet-rpc-1.myinfra.com
NEXT_PUBLIC_MAINNET_RPC_URLS_FALLBACK=https://mainnet-rpc-2.myinfra.com,https://mainnet.sorobanrpc.com
```

## Mixed HTTP/HTTPS

The app allows unencrypted HTTP URLs **only** for localhost or testing (useful for local Docker networks). Public URLs must be HTTPS.

```
# Allowed (local testing)
NEXT_PUBLIC_TESTNET_RPC_URL=http://localhost:8000

# Rejected (insecure public URL)
# NEXT_PUBLIC_TESTNET_RPC_URL=http://example.com <- This will fail
```

## Troubleshooting

### "ECONNREFUSED" or "No route to host"

- Verify the RPC URL is reachable: `curl -s https://your-rpc.example.com/health`
- Check firewall rules and DNS resolution.

### High latency on list views

- Check if `NEXT_PUBLIC_API_URL` is set and responsive. If missing or slow, list operations fall back to simulating every read against RPC, which is much slower.

### "The contract call could not be completed"

- This usually means the RPC is reachable but the contract ID doesn't exist there. Verify you've set the correct contract IDs for this network and that they're actually deployed.
176 changes: 176 additions & 0 deletions docs/environment-variables.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,176 @@
# Environment Variables

All configuration is via `NEXT_PUBLIC_*` environment variables, making them available to the browser. These control the network, RPC endpoints, deployed contract IDs, and an optional read aggregation API.

Copy [.env.example](.env.example) to `.env.local` and edit as needed.

## Network & RPC Configuration

### `NEXT_PUBLIC_DEFAULT_NETWORK`

- **Purpose:** The Stellar network the app connects to by default when no wallet is connected (read-only browsing mode) or after a fresh page load.
- **Required:** No (defaults to `"testnet"`)
- **Format:** String: `"testnet"` or `"mainnet"`
- **Example:**
```
NEXT_PUBLIC_DEFAULT_NETWORK=testnet
```

### `NEXT_PUBLIC_TESTNET_RPC_URL`

- **Purpose:** Primary Soroban RPC endpoint for Testnet reads and writes. This is tried first for all RPC operations.
- **Required:** No (defaults to `https://soroban-testnet.stellar.org`)
- **Format:** HTTPS URL
- **Example:**
```
NEXT_PUBLIC_TESTNET_RPC_URL=https://soroban-testnet.stellar.org
```

### `NEXT_PUBLIC_TESTNET_RPC_URLS_FALLBACK`

- **Purpose:** Comma-separated list of fallback Soroban RPC endpoints for Testnet. These are tried in order after the primary URL fails consecutively (see [custom RPC setup](./custom-rpc.md)).
- **Required:** No (defaults to empty; only the primary URL is used if not set)
- **Format:** Comma-separated HTTPS URLs
- **Example:**
```
NEXT_PUBLIC_TESTNET_RPC_URLS_FALLBACK=https://alternative-rpc-1.example.com,https://alternative-rpc-2.example.com
```

### `NEXT_PUBLIC_MAINNET_RPC_URL`

- **Purpose:** Primary Soroban RPC endpoint for Mainnet reads and writes.
- **Required:** No (defaults to `https://mainnet.sorobanrpc.com`)
- **Format:** HTTPS URL
- **Example:**
```
NEXT_PUBLIC_MAINNET_RPC_URL=https://mainnet.sorobanrpc.com
```

### `NEXT_PUBLIC_MAINNET_RPC_URLS_FALLBACK`

- **Purpose:** Comma-separated list of fallback Soroban RPC endpoints for Mainnet, tried in order after the primary URL fails consecutively.
- **Required:** No (defaults to empty)
- **Format:** Comma-separated HTTPS URLs
- **Example:**
```
NEXT_PUBLIC_MAINNET_RPC_URLS_FALLBACK=https://mainnet-alt-1.example.com,https://mainnet-alt-2.example.com
```

## Contract IDs

The app invokes four RWA contracts: **registry** (asset index), **compliance** (KYC gate), **dividend** (yield distribution), and individual **asset tokens** (discovered at runtime from the registry).

The first three are configured via environment variables and are network-specific; asset token IDs are not configurable—they are discovered at runtime.

### Testnet Contract IDs

All three Testnet contract IDs have hardcoded defaults that ship with the app, matching the official deployments. These can be overridden via environment variables.

#### `NEXT_PUBLIC_TESTNET_REGISTRY_ID`

- **Purpose:** Testnet registry contract ID — the authoritative index of tokenized assets and their total value locked.
- **Required:** No (defaults to the official Testnet deployment: `CBX5SMLTXX6JP4HA5GQIO2V6QM7WCUGL2GZ6D4U773HMRI6RXISKPUR3`)
- **Format:** Stellar contract ID (starts with 'C', 56 characters)
- **Example:**
```
NEXT_PUBLIC_TESTNET_REGISTRY_ID=CBX5SMLTXX6JP4HA5GQIO2V6QM7WCUGL2GZ6D4U773HMRI6RXISKPUR3
```

#### `NEXT_PUBLIC_TESTNET_COMPLIANCE_ID`

- **Purpose:** Testnet compliance contract ID — enforces KYC allowlists and jurisdiction rules, checked on every token transfer.
- **Required:** No (defaults to the official Testnet deployment: `CBUERYDM7DXTZLLKDBRJKUBPFJ7M4OSUN4T7XKUARU345RLXNAIQD2IU`)
- **Format:** Stellar contract ID (starts with 'C', 56 characters)
- **Example:**
```
NEXT_PUBLIC_TESTNET_COMPLIANCE_ID=CBUERYDM7DXTZLLKDBRJKUBPFJ7M4OSUN4T7XKUARU345RLXNAIQD2IU
```

#### `NEXT_PUBLIC_TESTNET_DIVIDEND_ID`

- **Purpose:** Testnet dividend contract ID — creates and manages proportional dividend/yield distributions to token holders.
- **Required:** No (defaults to the official Testnet deployment: `CAR4XY3CEBQWFOL27JEWFW34KXSIZA7RFKDQMEIV7ZU723RWY37I2SYX`)
- **Format:** Stellar contract ID (starts with 'C', 56 characters)
- **Example:**
```
NEXT_PUBLIC_TESTNET_DIVIDEND_ID=CAR4XY3CEBQWFOL27JEWFW34KXSIZA7RFKDQMEIV7ZU723RWY37I2SYX
```

### Mainnet Contract IDs

All three Mainnet contract IDs default to empty strings, meaning the app **will not function** on Mainnet until these are explicitly set. This is intentional: it prevents accidentally pointing at undeployed contracts.

#### `NEXT_PUBLIC_MAINNET_REGISTRY_ID`

- **Purpose:** Mainnet registry contract ID.
- **Required:** No, but the app will not work on Mainnet without it (defaults to empty string)
- **Format:** Stellar contract ID (starts with 'C', 56 characters)
- **Example:**
```
NEXT_PUBLIC_MAINNET_REGISTRY_ID=CBXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
```

#### `NEXT_PUBLIC_MAINNET_COMPLIANCE_ID`

- **Purpose:** Mainnet compliance contract ID.
- **Required:** No, but the app will not work on Mainnet without it (defaults to empty string)
- **Format:** Stellar contract ID (starts with 'C', 56 characters)
- **Example:**
```
NEXT_PUBLIC_MAINNET_COMPLIANCE_ID=CBXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
```

#### `NEXT_PUBLIC_MAINNET_DIVIDEND_ID`

- **Purpose:** Mainnet dividend contract ID.
- **Required:** No, but the app will not work on Mainnet without it (defaults to empty string)
- **Format:** Stellar contract ID (starts with 'C', 56 characters)
- **Example:**
```
NEXT_PUBLIC_MAINNET_DIVIDEND_ID=CBXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
```

## Read Aggregation API (Optional)

### `NEXT_PUBLIC_API_URL`

- **Purpose:** Optional URL of a Stellar RWA API server that provides faster read aggregations (list views, statistics, holder counts). When set, the app reads these aggregations from this endpoint instead of simulating every read directly against Soroban RPC, which is slower and more expensive. **Writes (signing transactions) always go through RPC regardless of this setting.**
- **Required:** No (defaults to empty; all reads fall back to direct RPC simulations)
- **Format:** HTTPS URL (base URL; the app appends paths like `/assets`, `/stats`, `/holders`)
- **Example:**
```
NEXT_PUBLIC_API_URL=https://rwa-api.example.com
```
- **Fallback behavior:** When `NEXT_PUBLIC_API_URL` is not set or the API is unreachable, the app automatically falls back to reading directly from Soroban RPC. No UI change is visible to the user—reads simply take longer.

## App Metadata (Optional)

These are typically set automatically by the build system and are used only in the footer for version tracking and debugging.

### `NEXT_PUBLIC_APP_VERSION`

- **Purpose:** The semantic version of the app displayed in the footer.
- **Required:** No (defaults to the `version` field in `package.json` at build time)
- **Format:** Semantic version (e.g., `1.0.0`, `1.2.3-alpha`)
- **Example:**
```
NEXT_PUBLIC_APP_VERSION=1.0.0
```
- **Note:** If not set, `next.config.mjs` reads the version from `package.json` during the build.

### `NEXT_PUBLIC_APP_COMMIT`

- **Purpose:** The git commit hash of the deployed build, displayed in the footer for correlating user-reported bugs with a specific code version.
- **Required:** No (defaults to resolution from environment, then `git rev-parse --short HEAD`)
- **Format:** Git commit hash (full or short; only the first 7 characters are displayed)
- **Example:**
```
NEXT_PUBLIC_APP_COMMIT=abc1234
```
- **Resolution order at build time:**
1. `NEXT_PUBLIC_APP_COMMIT` (explicit override)
2. `VERCEL_GIT_COMMIT_SHA` (set by Vercel on deploy)
3. `GIT_COMMIT_SHA` (set by some CI systems)
4. `COMMIT_REF` (set by other CI systems)
5. `git rev-parse --short HEAD` (local git history, if available)
6. Undefined (if none of the above are available)
Loading