From 4c93eb6a0ec907480f60a4ae857917f509889fd4 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Tue, 7 Apr 2026 11:56:10 +0000 Subject: [PATCH 1/7] feat(ecosystem-adapters): add comprehensive documentation for OpenZeppelin Ecosystem Adapters - Add 5 documentation pages: overview, architecture, getting started, supported ecosystems, building an adapter - Include Mermaid diagrams for package topology, capability tiers, profiles, runtime lifecycle, and execution strategies - Add navigation config (ecosystem-adapters.json) and wire into sidebar - Add Ecosystem Adapters section to ethereum-evm.json navigation under Open Source Tools - Register ecosystemAdaptersTree in navigation index and use-navigation-tree hook Co-authored-by: Aleksandr Pasevin --- content/ecosystem-adapters/architecture.mdx | 239 +++++++++++++ .../building-an-adapter.mdx | 333 ++++++++++++++++++ .../ecosystem-adapters/getting-started.mdx | 191 ++++++++++ content/ecosystem-adapters/index.mdx | 83 +++++ .../supported-ecosystems.mdx | 182 ++++++++++ src/hooks/use-navigation-tree.ts | 3 + src/navigation/ecosystem-adapters.json | 27 ++ src/navigation/ethereum-evm.json | 31 ++ src/navigation/index.ts | 7 + 9 files changed, 1096 insertions(+) create mode 100644 content/ecosystem-adapters/architecture.mdx create mode 100644 content/ecosystem-adapters/building-an-adapter.mdx create mode 100644 content/ecosystem-adapters/getting-started.mdx create mode 100644 content/ecosystem-adapters/index.mdx create mode 100644 content/ecosystem-adapters/supported-ecosystems.mdx create mode 100644 src/navigation/ecosystem-adapters.json diff --git a/content/ecosystem-adapters/architecture.mdx b/content/ecosystem-adapters/architecture.mdx new file mode 100644 index 00000000..224601be --- /dev/null +++ b/content/ecosystem-adapters/architecture.mdx @@ -0,0 +1,239 @@ +--- +title: Architecture +--- + +This page describes the capability-based architecture that underpins all OpenZeppelin Ecosystem Adapters. Understanding this architecture will help you choose the right profile for your application, consume capabilities efficiently, and — if needed — build your own adapter. + +## Package Topology + +The adapter system is split across several packages with clear dependency boundaries: + +```mermaid +flowchart TD + App["Consumer Application"] --> Runtime["EcosystemRuntime"] + Runtime --> Caps["Capability Interfaces\n(@openzeppelin/ui-types)"] + + Caps --> Evm["adapter-evm"] + Caps --> Polkadot["adapter-polkadot"] + Caps --> Stellar["adapter-stellar"] + Caps --> Midnight["adapter-midnight"] + Caps --> Solana["adapter-solana"] + + App --> Vite["adapters-vite"] + + Evm --> Core["adapter-evm-core"] + Polkadot --> Core + Core --> Utils["adapter-runtime-utils"] + Stellar --> Utils + + style Caps fill:#e8eaf6,stroke:#3f51b5,color:#000 + style Utils fill:#e0f2f1,stroke:#00897b,color:#000 + style Core fill:#fff3e0,stroke:#ef6c00,color:#000 +``` + +- **`@openzeppelin/ui-types`** defines all 13 capability interfaces. It is the single source of truth. +- **`adapter-runtime-utils`** provides profile composition, lazy capability instantiation, and staged disposal. +- **`adapter-evm-core`** centralizes reusable EVM implementations shared by `adapter-evm` and `adapter-polkadot`. +- Each public adapter exposes an `ecosystemDefinition` conforming to `EcosystemExport`. + +## Capability Tiers + +Adapter functionality is decomposed into **13 capability interfaces** organized across **3 tiers**. The tiers reflect increasing levels of runtime requirements: stateless metadata, network-aware schema operations, and stateful wallet-dependent interactions. + +```mermaid +block-beta + columns 3 + block:tier1:3 + columns 4 + t1h["Tier 1 — Lightweight"]:4 + Addressing Explorer NetworkCatalog UiLabels + end + block:tier2:3 + columns 4 + t2h["Tier 2 — Schema"]:4 + ContractLoading Schema TypeMapping Query + end + block:tier3:3 + columns 5 + t3h["Tier 3 — Runtime"]:5 + Execution Wallet UiKit Relayer AccessControl + end + + style tier1 fill:#e3f2fd,stroke:#1976d2,color:#000 + style tier2 fill:#fff3e0,stroke:#f57c00,color:#000 + style tier3 fill:#fce4ec,stroke:#c62828,color:#000 + style t1h fill:#bbdefb,stroke:#1976d2,color:#000 + style t2h fill:#ffe0b2,stroke:#f57c00,color:#000 + style t3h fill:#f8bbd0,stroke:#c62828,color:#000 +``` + +| Tier | Category | Network Required | Wallet Required | Capabilities | +| --- | --- | --- | --- | --- | +| **1** | Lightweight / Declarative | No | No | Addressing, Explorer, NetworkCatalog, UiLabels | +| **2** | Schema / Definition | Yes | No | ContractLoading, Schema, TypeMapping, Query | +| **3** | Runtime / Stateful | Yes | Yes | Execution, Wallet, UiKit, Relayer, AccessControl | + +### Tier Import Rules + +Tier isolation is enforced physically through sub-path exports, not tree-shaking: + +- **Tier 1** modules must not import from Tier 2 or Tier 3 modules +- **Tier 2** modules may import from Tier 1 +- **Tier 3** modules may import from Tier 1 and Tier 2 + +This means importing `@openzeppelin/adapter-evm/addressing` will never pull in wallet SDKs, RPC clients, or access control code — regardless of your bundler configuration. + +### Capability Reference + +| Capability | Interface | Tier | Key Methods | +| --- | --- | --- | --- | +| Addressing | `AddressingCapability` | 1 | `isValidAddress` | +| Explorer | `ExplorerCapability` | 1 | `getExplorerUrl`, `getExplorerTxUrl` | +| NetworkCatalog | `NetworkCatalogCapability` | 1 | `getNetworks` | +| UiLabels | `UiLabelsCapability` | 1 | `getUiLabels` | +| ContractLoading | `ContractLoadingCapability` | 2 | `loadContract`, `getContractDefinitionInputs` | +| Schema | `SchemaCapability` | 2 | `isViewFunction`, `getWritableFunctions` | +| TypeMapping | `TypeMappingCapability` | 2 | `mapParameterTypeToFieldType`, `getTypeMappingInfo` | +| Query | `QueryCapability` | 2 | `queryViewFunction`, `formatFunctionResult`, `getCurrentBlock` | +| Execution | `ExecutionCapability` | 3 | `signAndBroadcast`, `formatTransactionData`, `validateExecutionConfig` | +| Wallet | `WalletCapability` | 3 | `connectWallet`, `disconnectWallet`, `getWalletConnectionStatus` | +| UiKit | `UiKitCapability` | 3 | `getAvailableUiKits`, `configureUiKit` | +| Relayer | `RelayerCapability` | 3 | `getRelayers`, `getNetworkServiceForms` | +| AccessControl | `AccessControlCapability` | 3 | `registerContract`, `grantRole`, and 17 more | + +## Profiles + +Profiles are pre-composed bundles of capabilities that match common application archetypes. They exist for convenience — you can always consume individual capabilities directly via the `CapabilityFactoryMap`. + +```mermaid +flowchart LR + subgraph Profiles + D["Declarative"] + V["Viewer"] + T["Transactor"] + Co["Composer"] + O["Operator"] + end + + D -->|"Tier 1"| T1["Addressing\nExplorer\nNetworkCatalog\nUiLabels"] + V -->|"+ Tier 2"| T2["ContractLoading\nSchema\nTypeMapping\nQuery"] + T -->|"+ Partial Tier 2/3"| T2b["ContractLoading\nSchema\nTypeMapping\nExecution\nWallet"] + Co -->|"+ Tier 2 + Tier 3"| T3a["Query\nExecution\nWallet\nUiKit\nRelayer"] + O -->|"+ Tier 2 + Tier 3"| T3b["Query\nExecution\nWallet\nUiKit\nAccessControl"] + + V -.-> T1 + T -.-> T1 + Co -.-> T1 + O -.-> T1 + Co -.-> T2 + O -.-> T2 + + style D fill:#e3f2fd,stroke:#1976d2,color:#000 + style V fill:#e8f5e9,stroke:#388e3c,color:#000 + style T fill:#fff3e0,stroke:#f57c00,color:#000 + style Co fill:#f3e5f5,stroke:#7b1fa2,color:#000 + style O fill:#fce4ec,stroke:#c62828,color:#000 +``` + +### Profile–Capability Matrix + +| Profile | Use Case | Tier 1 (all 4) | Tier 2 | Tier 3 | +| --- | --- | --- | --- | --- | +| **Declarative** | Metadata-only (catalogs, validators) | All | — | — | +| **Viewer** | Read-only (dashboards, analytics) | All | All 4 | — | +| **Transactor** | Write-only (send, approve, mint) | All | ContractLoading, Schema, TypeMapping | Execution, Wallet | +| **Composer** | Full-featured UI Builder apps | All | All 4 | Execution, Wallet, UiKit, Relayer | +| **Operator** | Role/permission management | All | All 4 | Execution, Wallet, UiKit, AccessControl | + +### Profile Selection Guide + +| If your application needs to… | Choose | +| --- | --- | +| Validate addresses, list networks, link to explorers | **Declarative** | +| Read contract state without sending transactions | **Viewer** | +| Send transactions without reading contract state first | **Transactor** | +| Build full contract interaction UIs with relayer support | **Composer** | +| Manage contract roles and permissions | **Operator** | + +## Runtime Lifecycle + +Runtimes are **immutable** and **network-scoped**. When a user switches networks, the consuming application must dispose the current runtime and create a new one. + +```mermaid +sequenceDiagram + participant App as Application + participant ES as EcosystemExport + participant RT as EcosystemRuntime + participant Cap as Capabilities + + App->>ES: createRuntime('composer', networkA) + ES->>RT: Compose capabilities with shared state + RT-->>App: runtime (immutable) + + App->>RT: runtime.query.queryViewFunction(...) + RT->>Cap: Lazy-init Query capability + Cap-->>RT: result + + Note over App,RT: User switches to networkB + + App->>RT: runtime.dispose() + RT->>Cap: Staged cleanup (listeners → subscriptions → capabilities → RPC) + App->>ES: createRuntime('composer', networkB) + ES->>RT: New runtime with fresh state + RT-->>App: newRuntime +``` + +### Dispose Contract + +- `dispose()` is **idempotent** — calling it multiple times is a no-op +- After `dispose()`, any method or property access throws `RuntimeDisposedError` +- Pending async operations (e.g., in-flight `signAndBroadcast`) are rejected with `RuntimeDisposedError` +- Cleanup follows a staged order: mark disposed → reject pending operations → clean up listeners and subscriptions → dispose capabilities → release wallet and RPC resources +- Runtime disposal does **not** disconnect the wallet — disconnect is always an explicit user action + +## Execution Strategies + +The Execution capability uses a **strategy pattern** to support multiple transaction submission methods. Each adapter can provide its own set of strategies. + +```mermaid +flowchart TD + Exec["ExecutionCapability\nsignAndBroadcast()"] --> Config{"executionConfig.method"} + Config -->|"EOA"| EOA["EoaExecutionStrategy\nDirect wallet signing"] + Config -->|"Relayer"| Relay["RelayerExecutionStrategy\nOpenZeppelin Relayer"] + + EOA --> Wallet["Wallet signs tx"] + Relay --> RelayerSvc["Relayer service submits tx"] + + Wallet --> Confirm["waitForTransactionConfirmation()"] + RelayerSvc --> Confirm + + style Exec fill:#e8eaf6,stroke:#3f51b5,color:#000 + style EOA fill:#e8f5e9,stroke:#388e3c,color:#000 + style Relay fill:#fff3e0,stroke:#f57c00,color:#000 +``` + +The EVM and Stellar adapters ship with both EOA and Relayer strategies. Adapter authors can implement custom strategies by conforming to the `AdapterExecutionStrategy` interface. + +## Sub-Path Exports + +Each adapter publishes every implemented capability and profile as a dedicated sub-path export: + +```ts +// Tier 1 — no wallet, no RPC, no heavy dependencies +import { createAddressing } from '@openzeppelin/adapter-stellar/addressing'; +import { createExplorer } from '@openzeppelin/adapter-stellar/explorer'; + +// Tier 2 — network-aware +import { createQuery } from '@openzeppelin/adapter-stellar/query'; + +// Tier 3 — wallet-dependent +import { createExecution } from '@openzeppelin/adapter-stellar/execution'; + +// Profile runtimes +import { createRuntime } from '@openzeppelin/adapter-stellar/profiles/composer'; + +// Metadata and networks +import { networks } from '@openzeppelin/adapter-stellar/networks'; +``` + +This structure ensures that a Declarative-profile consumer never bundles wallet SDKs, and that individual capabilities can be tested in isolation. diff --git a/content/ecosystem-adapters/building-an-adapter.mdx b/content/ecosystem-adapters/building-an-adapter.mdx new file mode 100644 index 00000000..f97e8d90 --- /dev/null +++ b/content/ecosystem-adapters/building-an-adapter.mdx @@ -0,0 +1,333 @@ +--- +title: Building an Adapter +--- + +This guide walks through implementing a new ecosystem adapter from scratch. By the end, you'll have a working adapter that integrates with any application using the OpenZeppelin adapter architecture. + +## How Much Do You Need to Implement? + +Adapters are **incrementally adoptable**. You don't need to implement all 13 capabilities to ship a useful adapter. Start small and add capabilities as your ecosystem's support matures. + +```mermaid +flowchart TD + Start["Start Here"] --> T1["Implement Tier 1\n(4 capabilities)"] + T1 -->|"Unlocks"| Dec["Declarative Profile\nAddress validation, explorer links,\nnetwork catalogs"] + + T1 --> T2["Add Tier 2\n(4 capabilities)"] + T2 -->|"Unlocks"| View["Viewer Profile\nContract reading, schema parsing,\ntype mapping, queries"] + + T2 --> T3a["Add Execution + Wallet"] + T3a -->|"Unlocks"| Trans["Transactor Profile\nTransaction signing\nand broadcasting"] + + T3a --> T3b["Add UiKit + Relayer"] + T3b -->|"Unlocks"| Comp["Composer Profile\nFull UI Builder integration\nwith relayer support"] + + T3a --> T3c["Add UiKit + AccessControl"] + T3c -->|"Unlocks"| Op["Operator Profile\nRole and permission\nmanagement"] + + style Start fill:#e8eaf6,stroke:#3f51b5,color:#000 + style T1 fill:#e3f2fd,stroke:#1976d2,color:#000 + style T2 fill:#fff3e0,stroke:#f57c00,color:#000 + style T3a fill:#fce4ec,stroke:#c62828,color:#000 + style T3b fill:#f3e5f5,stroke:#7b1fa2,color:#000 + style T3c fill:#f3e5f5,stroke:#7b1fa2,color:#000 + style Dec fill:#e8f5e9,stroke:#388e3c,color:#000 + style View fill:#e8f5e9,stroke:#388e3c,color:#000 + style Trans fill:#e8f5e9,stroke:#388e3c,color:#000 + style Comp fill:#e8f5e9,stroke:#388e3c,color:#000 + style Op fill:#e8f5e9,stroke:#388e3c,color:#000 +``` + +## Package Structure + +Follow the standard adapter package layout: + +``` +packages/adapter-/ +├── src/ +│ ├── capabilities/ # Capability factory functions +│ │ ├── addressing.ts +│ │ ├── explorer.ts +│ │ ├── network-catalog.ts +│ │ ├── ui-labels.ts +│ │ ├── contract-loading.ts # Tier 2+ +│ │ ├── schema.ts +│ │ ├── type-mapping.ts +│ │ ├── query.ts +│ │ ├── execution.ts # Tier 3+ +│ │ ├── wallet.ts +│ │ ├── ui-kit.ts +│ │ ├── relayer.ts +│ │ ├── access-control.ts +│ │ └── index.ts +│ ├── profiles/ # Profile runtime factories +│ │ ├── shared-state.ts +│ │ └── index.ts +│ ├── networks/ # Network configurations +│ │ └── index.ts +│ ├── index.ts # ecosystemDefinition export +│ ├── metadata.ts +│ └── vite-config.ts +├── package.json +├── tsconfig.json +├── tsdown.config.ts +└── vitest.config.ts +``` + +Not every adapter needs every file. A Tier 1-only adapter may only have `addressing.ts`, `explorer.ts`, `network-catalog.ts`, and `ui-labels.ts` in `capabilities/`. + +## Implementation Guide + +### Step 1: Implement Tier 1 Capabilities + +Start with the four stateless capabilities that every adapter must provide: + +```ts +// capabilities/addressing.ts +import type { AddressingCapability } from '@openzeppelin/ui-types'; + +export function createAddressing(): AddressingCapability { + return { + isValidAddress(address: string): boolean { + // Your chain's address validation logic + return /^0x[0-9a-fA-F]{40}$/.test(address); + }, + }; +} +``` + +```ts +// capabilities/explorer.ts +import type { ExplorerCapability, NetworkConfig } from '@openzeppelin/ui-types'; + +export function createExplorer(config?: NetworkConfig): ExplorerCapability { + const baseUrl = config?.explorerUrl ?? 'https://explorer.example.com'; + return { + getExplorerUrl: (address) => `${baseUrl}/address/${address}`, + getExplorerTxUrl: (txHash) => `${baseUrl}/tx/${txHash}`, + }; +} +``` + +```ts +// capabilities/network-catalog.ts +import type { NetworkCatalogCapability } from '@openzeppelin/ui-types'; +import { networks } from '../networks'; + +export function createNetworkCatalog(): NetworkCatalogCapability { + return { getNetworks: () => networks }; +} +``` + +```ts +// capabilities/ui-labels.ts +import type { UiLabelsCapability } from '@openzeppelin/ui-types'; + +export function createUiLabels(): UiLabelsCapability { + return { + getUiLabels: () => ({ transactionLabel: 'Transaction' }), + }; +} +``` + +### Step 2: Define the CapabilityFactoryMap + +Wire all capability factories into the capability map: + +```ts +// capabilities/index.ts +import type { CapabilityFactoryMap } from '@openzeppelin/ui-types'; +import { createAddressing } from './addressing'; +import { createExplorer } from './explorer'; +import { createNetworkCatalog } from './network-catalog'; +import { createUiLabels } from './ui-labels'; + +export const capabilities: CapabilityFactoryMap = { + addressing: createAddressing, + explorer: createExplorer, + networkCatalog: createNetworkCatalog, + uiLabels: createUiLabels, +}; +``` + +### Step 3: Wire Profile Runtimes + +Use `adapter-runtime-utils` to compose capabilities into profile runtimes: + +```ts +// profiles/index.ts +import type { NetworkConfig, ProfileName } from '@openzeppelin/ui-types'; +import { createRuntimeFromFactories } from '@openzeppelin/adapter-runtime-utils'; +import { capabilities } from '../capabilities'; + +export function createRuntime( + profile: ProfileName, + config: NetworkConfig, + factoryMap = capabilities, + options?: { uiKit?: string } +) { + return createRuntimeFromFactories(profile, config, factoryMap, options); +} +``` + +`createRuntimeFromFactories` handles: +- Validating that all required capabilities for the profile are present +- Throwing `UnsupportedProfileError` with a list of missing capabilities if validation fails +- Lazy capability instantiation with caching +- Staged disposal + +### Step 4: Export the Ecosystem Definition + +The `ecosystemDefinition` is the public entry point that consuming applications use: + +```ts +// index.ts +import type { EcosystemExport } from '@openzeppelin/ui-types'; +import { capabilities } from './capabilities'; +import { metadata } from './metadata'; +import { networks } from './networks'; +import { createRuntime } from './profiles'; + +export const ecosystemDefinition: EcosystemExport = { + ...metadata, + networks, + capabilities, + createRuntime: (profile, config, options) => + createRuntime(profile, config, capabilities, options), +}; +``` + +### Step 5: Configure Sub-Path Exports + +Add sub-path exports to `package.json` for physical tier isolation: + +```json +{ + "exports": { + ".": { "import": "./dist/index.mjs" }, + "./addressing": { "import": "./dist/capabilities/addressing.mjs" }, + "./explorer": { "import": "./dist/capabilities/explorer.mjs" }, + "./network-catalog": { "import": "./dist/capabilities/network-catalog.mjs" }, + "./ui-labels": { "import": "./dist/capabilities/ui-labels.mjs" }, + "./metadata": { "import": "./dist/metadata.mjs" }, + "./networks": { "import": "./dist/networks.mjs" }, + "./vite-config": { "import": "./dist/vite-config.mjs" } + } +} +``` + +Add more entries as you implement higher-tier capabilities. + +### Step 6: Add a Vite Config Export + +Every adapter must publish a `./vite-config` entry that returns build-time requirements: + +```ts +// vite-config.ts +import type { UserConfig } from 'vite'; + +export function getViteConfig(): UserConfig { + return { + resolve: { + dedupe: ['@your-chain/sdk'], + }, + optimizeDeps: { + include: ['@your-chain/sdk'], + }, + }; +} +``` + +## Adding Higher-Tier Capabilities + +Once Tier 1 is working, add capabilities incrementally. Each Tier 2+ factory function must: + +1. Accept a `NetworkConfig` parameter +2. Return a capability object that includes a `dispose()` method +3. Follow the tier import rules (Tier 2 may import Tier 1, Tier 3 may import Tier 1 and 2) + +Here's an example Query capability: + +```ts +// capabilities/query.ts +import type { QueryCapability, NetworkConfig } from '@openzeppelin/ui-types'; + +export function createQuery(config: NetworkConfig): QueryCapability { + let client: YourRpcClient | null = null; + + function getClient() { + if (!client) client = new YourRpcClient(config.rpcUrl); + return client; + } + + return { + networkConfig: config, + + async queryViewFunction(address, functionId, args) { + return getClient().call(address, functionId, args); + }, + + formatFunctionResult(result, schema, functionId) { + return { raw: result, formatted: String(result) }; + }, + + async getCurrentBlock() { + return getClient().getBlockNumber(); + }, + + dispose() { + client?.close(); + client = null; + }, + }; +} +``` + +## Shared EVM Core + +If your chain is EVM-compatible, you don't need to reimplement everything. Depend on `@openzeppelin/adapter-evm-core` and reuse its capability implementations: + +```ts +import { createExecution } from '@openzeppelin/adapter-evm-core/execution'; +import { createQuery } from '@openzeppelin/adapter-evm-core/query'; +``` + +This is exactly what `adapter-polkadot` does — it delegates ABI loading, queries, transaction execution, and wallet infrastructure to the shared EVM core while adding Polkadot-specific metadata and network configurations. + +## Lazy Capability Factories + +For capabilities that depend on each other (e.g., Query needs ContractLoading for schema resolution), use `createLazyRuntimeCapabilityFactories` from `adapter-runtime-utils`: + +```ts +import { + createLazyRuntimeCapabilityFactories, +} from '@openzeppelin/adapter-runtime-utils'; + +const lazyFactories = createLazyRuntimeCapabilityFactories(config, { + contractLoading: () => createContractLoading(config), + query: (deps) => createQuery(config, { + getContractLoading: () => deps.contractLoading, + }), +}); +``` + +This avoids circular dependency issues while maintaining shared state within a profile runtime. + +## Contribution Checklist + +Before submitting your adapter: + +1. All Tier 1 capabilities are implemented and exported via sub-path exports +2. `ecosystemDefinition` conforms to `EcosystemExport` with `capabilities` and `createRuntime` +3. `metadata`, `networks`, and `vite-config` exports are present +4. Profile runtimes use `adapter-runtime-utils` for composition +5. Unsupported profiles throw `UnsupportedProfileError` with missing capabilities listed +6. Tier isolation is verified: Tier 1 sub-path imports don't pull Tier 2/3 dependencies +7. Tests cover each capability factory and profile runtime creation +8. `package.json` includes sub-path exports for every implemented capability and profile +9. `tsdown.config.ts` includes entry points for all sub-path exports +10. Build-time requirements are validated with `pnpm validate:vite-configs` + + +The `pnpm lint:adapters` CI check validates tier isolation and export structure automatically. Run it locally before submitting your PR. + diff --git a/content/ecosystem-adapters/getting-started.mdx b/content/ecosystem-adapters/getting-started.mdx new file mode 100644 index 00000000..98ce7277 --- /dev/null +++ b/content/ecosystem-adapters/getting-started.mdx @@ -0,0 +1,191 @@ +--- +title: Getting Started +--- + +This guide walks you through installing an adapter, creating a runtime, and performing your first on-chain interaction. + +## Prerequisites + +- Node.js >= 20.19.0 +- A package manager: pnpm (recommended), npm, or yarn + +## Installation + +Install the adapter for your target ecosystem along with the shared types package: + +**EVM (Ethereum, Polygon, etc.)** +```bash +pnpm add @openzeppelin/adapter-evm @openzeppelin/ui-types +``` + +**Stellar (Soroban)** +```bash +pnpm add @openzeppelin/adapter-stellar @openzeppelin/ui-types +``` + +**Polkadot** +```bash +pnpm add @openzeppelin/adapter-polkadot @openzeppelin/ui-types +``` + +If you're consuming multiple ecosystems, install all of them — each adapter is independent and tree-shakeable. + +## Quick Start + +### 1. Choose a Profile + +Pick the [profile](/ecosystem-adapters/architecture#profiles) that matches your application's needs: + +| Profile | What it gives you | +| --- | --- | +| `declarative` | Address validation, explorer links, network lists | +| `viewer` | + Contract reading, schema parsing, type mapping | +| `transactor` | + Transaction signing and broadcasting | +| `composer` | + Full UI integration with wallet and relayer | +| `operator` | + Role and access control management | + +### 2. Create a Runtime + +Every adapter exports an `ecosystemDefinition` object. Use its `createRuntime` method to compose a profile runtime: + +```ts +import { ecosystemDefinition } from '@openzeppelin/adapter-evm'; + +const network = ecosystemDefinition.networks[0]; // e.g., Ethereum Mainnet + +const runtime = ecosystemDefinition.createRuntime('viewer', network); +``` + +The `createRuntime` call is **synchronous** — it assembles capability instances immediately. Expensive initialization (RPC connections, wallet discovery) happens lazily on first use. + +If you request a profile the adapter doesn't fully support, `createRuntime` throws an `UnsupportedProfileError` listing the missing capabilities. + +### 3. Use Capabilities + +Access capabilities directly from the runtime object: + +```ts +// Validate an address (Tier 1 — instant, no network call) +const isValid = runtime.addressing.isValidAddress('0x1234...'); + +// Read contract state (Tier 2 — triggers RPC on first call) +const result = await runtime.query.queryViewFunction( + '0xContractAddress', + 'balanceOf', + ['0xOwnerAddress'] +); +const formatted = runtime.query.formatFunctionResult(result, schema, 'balanceOf'); +``` + +### 4. Dispose When Done + +Runtimes hold network connections and event listeners. Dispose them when switching networks or unmounting: + +```ts +runtime.dispose(); +// Any further access throws RuntimeDisposedError +``` + +## Consuming Individual Capabilities + +You don't have to use profiles. Import individual capability factories via sub-path exports for maximum control: + +```ts +import { createAddressing } from '@openzeppelin/adapter-evm/addressing'; +import { createExplorer } from '@openzeppelin/adapter-evm/explorer'; + +const addressing = createAddressing(); +const explorer = createExplorer(networkConfig); + +console.log(addressing.isValidAddress('0xABC...')); // true or false +console.log(explorer.getExplorerUrl('0xABC...')); // https://etherscan.io/address/0xABC... +``` + + +Standalone capability instances created from factory functions are fully isolated. They do not share state with profile runtimes or with each other. + + +## Vite / Vitest Integration + +Applications that consume multiple adapter packages should use `@openzeppelin/adapters-vite` to merge adapter-owned build configuration: + +```bash +pnpm add -D @openzeppelin/adapters-vite +``` + +### Vite Config + +```ts +import { defineOpenZeppelinAdapterViteConfig } from '@openzeppelin/adapters-vite'; +import react from '@vitejs/plugin-react'; + +export default defineOpenZeppelinAdapterViteConfig({ + ecosystems: ['evm', 'stellar', 'polkadot'], + config: { + plugins: [react()], + }, +}); +``` + +### Vitest Config + +```ts +import { defineOpenZeppelinAdapterVitestConfig } from '@openzeppelin/adapters-vite'; +import { mergeConfig } from 'vitest/config'; + +export default mergeConfig( + sharedVitestConfig, + await defineOpenZeppelinAdapterVitestConfig({ + ecosystems: ['evm', 'stellar'], + importMetaUrl: import.meta.url, + config: { + test: { environment: 'jsdom' }, + }, + }) +); +``` + +This centralizes `resolve.dedupe`, `optimizeDeps`, `ssr.noExternal`, and any adapter-specific Vite plugins — adapters remain the source of truth for their own build requirements. + +## Network Switching + +Runtimes are network-scoped and immutable. To switch networks, dispose and recreate: + +```ts +let runtime = ecosystemDefinition.createRuntime('composer', ethereumMainnet); + +// User switches to Sepolia +runtime.dispose(); +runtime = ecosystemDefinition.createRuntime('composer', sepoliaTestnet); +``` + + +Wallet sessions survive network changes. Runtime disposal cleans up runtime-owned resources (listeners, subscriptions, RPC connections) but does **not** disconnect the wallet. Disconnect is always an explicit user action. + + +## Error Handling + +The adapter system uses two primary error classes: + +| Error | When it's thrown | +| --- | --- | +| `UnsupportedProfileError` | `createRuntime` is called with a profile the adapter can't fully support. The error message lists the missing capabilities. | +| `RuntimeDisposedError` | Any method or property is accessed on a disposed runtime, or an in-flight async operation is interrupted by disposal. | + +```ts +import { UnsupportedProfileError, RuntimeDisposedError } from '@openzeppelin/ui-types'; + +try { + const runtime = ecosystemDefinition.createRuntime('operator', network); +} catch (error) { + if (error instanceof UnsupportedProfileError) { + console.log('Missing capabilities:', error.message); + } +} +``` + +## Next Steps + +- Read the [Architecture](/ecosystem-adapters/architecture) page for a deep dive into tiers, profiles, and lifecycle management +- Explore [Supported Ecosystems](/ecosystem-adapters/supported-ecosystems) to see what each adapter provides +- Follow the [Building an Adapter](/ecosystem-adapters/building-an-adapter) guide to add support for a new chain diff --git a/content/ecosystem-adapters/index.mdx b/content/ecosystem-adapters/index.mdx new file mode 100644 index 00000000..aaa890ac --- /dev/null +++ b/content/ecosystem-adapters/index.mdx @@ -0,0 +1,83 @@ +--- +title: Ecosystem Adapters +--- + +**OpenZeppelin Ecosystem Adapters** are a set of modular, chain-specific integration packages that let applications interact with any supported blockchain through a single, unified interface. Built on 13 composable capability interfaces organized in 3 tiers, each adapter encapsulates everything needed — contract loading, type mapping, transaction execution, wallet connection, and network configuration — while keeping consuming applications completely chain-agnostic. + + + + Understand the capability-based architecture: tiers, profiles, and the runtime lifecycle. + + + Install adapters, configure profiles, and run your first cross-chain interaction. + + + Explore the production-ready EVM, Stellar, Polkadot, and Midnight adapters. + + + Step-by-step guide to implementing a new adapter for your blockchain. + + + +## Why Adapters? + +Building cross-chain tooling traditionally forces developers into one of two traps: a monolithic abstraction that leaks chain details, or per-chain forks that drift apart over time. Adapters solve this with a **capability-based decomposition** — each chain implements only the interfaces it supports, and consuming applications pull in only what they need. + +```mermaid +flowchart LR + App["Your Application"] --> Runtime["EcosystemRuntime"] + Runtime --> T1["Tier 1\nLightweight"] + Runtime --> T2["Tier 2\nSchema"] + Runtime --> T3["Tier 3\nRuntime"] + + T1 --> A1["Addressing"] + T1 --> A2["Explorer"] + T1 --> A3["NetworkCatalog"] + T1 --> A4["UiLabels"] + + T2 --> B1["ContractLoading"] + T2 --> B2["Schema"] + T2 --> B3["TypeMapping"] + T2 --> B4["Query"] + + T3 --> C1["Execution"] + T3 --> C2["Wallet"] + T3 --> C3["UiKit"] + T3 --> C4["Relayer"] + T3 --> C5["AccessControl"] + + style T1 fill:#e3f2fd,stroke:#1976d2,color:#000 + style T2 fill:#fff3e0,stroke:#f57c00,color:#000 + style T3 fill:#fce4ec,stroke:#c62828,color:#000 +``` + +## Key Design Principles + +- **Pay for what you use.** Tier 1 capabilities are stateless and never pull in wallet SDKs or RPC clients. Import `addressing` and you get only address validation — nothing more. +- **Profiles simplify consumption.** Five pre-composed profiles (Declarative, Viewer, Transactor, Composer, Operator) match common application archetypes so you don't have to assemble capabilities manually. +- **Adapters own their chains.** All chain-specific logic — ABI parsing, Soroban type mapping, ZK proof orchestration — stays inside the adapter package. The consuming application never touches it. +- **Runtime lifecycle is explicit.** Runtimes are immutable and network-scoped. Switching networks means disposing the old runtime and creating a new one — no hidden state mutations. + +## Packages + +| Package | Description | Status | +| --- | --- | --- | +| `@openzeppelin/adapter-evm` | Ethereum, Polygon, and EVM-compatible chains | Production | +| `@openzeppelin/adapter-stellar` | Stellar / Soroban | Production | +| `@openzeppelin/adapter-polkadot` | Polkadot Hub, Moonbeam (EVM path) | Production | +| `@openzeppelin/adapter-midnight` | Midnight Network (ZK artifacts, Lace wallet) | Production | +| `@openzeppelin/adapter-solana` | Solana (scaffolding) | In Progress | +| `@openzeppelin/adapter-evm-core` | Shared EVM capability implementations (internal) | Internal | +| `@openzeppelin/adapter-runtime-utils` | Profile composition and lifecycle utilities (internal) | Internal | +| `@openzeppelin/adapters-vite` | Shared Vite/Vitest build integration | Utility | + +## Who Uses Adapters? + +Adapters are consumed by several OpenZeppelin products: + +- [**UI Builder**](https://github.com/OpenZeppelin/ui-builder) — full-featured smart contract interaction UI +- [**OpenZeppelin UI**](https://github.com/OpenZeppelin/openzeppelin-ui) — shared UI components and React integration +- [**Role Manager**](https://github.com/OpenZeppelin/role-manager) — role and permission management tool +- [**RWA Wizard**](https://github.com/OpenZeppelin/rwa-wizard) — real-world asset token generation + +Any TypeScript application that needs chain-agnostic blockchain interaction can use adapters directly. diff --git a/content/ecosystem-adapters/supported-ecosystems.mdx b/content/ecosystem-adapters/supported-ecosystems.mdx new file mode 100644 index 00000000..57ee13b1 --- /dev/null +++ b/content/ecosystem-adapters/supported-ecosystems.mdx @@ -0,0 +1,182 @@ +--- +title: Supported Ecosystems +--- + +Each adapter implements the subset of the [13 capability interfaces](/ecosystem-adapters/architecture#capability-tiers) that its blockchain supports. This page summarizes what each production adapter provides. + +## At a Glance + +```mermaid +flowchart LR + subgraph EVM["EVM Adapter"] + direction TB + E1["13/13 capabilities"] + E2["Ethereum, Polygon,\nArbitrum, Base, ..."] + end + + subgraph Stellar["Stellar Adapter"] + direction TB + S1["13/13 capabilities"] + S2["Stellar Public,\nStellar Testnet"] + end + + subgraph Polkadot["Polkadot Adapter"] + direction TB + P1["EVM-path capabilities"] + P2["Polkadot Hub,\nMoonbeam, Moonriver"] + end + + subgraph Midnight["Midnight Adapter"] + direction TB + M1["ZK artifact workflows"] + M2["Midnight Testnet"] + end + + subgraph Solana["Solana Adapter"] + direction TB + So1["Scaffolding only"] + So2["Devnet, Testnet"] + end + + style EVM fill:#e3f2fd,stroke:#1976d2,color:#000 + style Stellar fill:#fff3e0,stroke:#f57c00,color:#000 + style Polkadot fill:#f3e5f5,stroke:#7b1fa2,color:#000 + style Midnight fill:#e0f2f1,stroke:#00897b,color:#000 + style Solana fill:#fafafa,stroke:#9e9e9e,color:#000 +``` + +## EVM — `@openzeppelin/adapter-evm` + +The EVM adapter targets Ethereum and all EVM-compatible chains. It implements the full set of 13 capabilities and supports all 5 profiles. + +**Supported Networks**: Ethereum Mainnet, Sepolia, Polygon, Polygon Amoy, Arbitrum One, Arbitrum Sepolia, Base, Base Sepolia, Optimism, Optimism Sepolia, and more. + +**Profiles Supported**: Declarative, Viewer, Transactor, Composer, Operator + +### Highlights + +- **Contract Loading**: Fetches ABIs from Etherscan and Sourcify with automatic fallback ordering. Detects proxy contracts and resolves implementation ABIs. +- **Type Mapping**: Maps all Solidity types (`uint256`, `address`, `bytes32`, tuples, dynamic arrays) to UI-friendly form fields. +- **Execution Strategies**: Pluggable EOA (direct wallet signing via Wagmi/Viem) and OpenZeppelin Relayer strategies. +- **Wallet Integration**: Built on Wagmi and RainbowKit with React context providers and hooks. +- **Access Control**: Full role management including `grantRole`, `revokeRole`, `renounceRole`, ownership transfers, and role enumeration. + +### Configuration Resolution + +The EVM adapter resolves RPC URLs and explorer API keys through a layered priority system: + +1. **User-provided config** — settings from the end-user (stored in localStorage) +2. **Application config** — deployer settings via `app.config.json` or environment variables +3. **Default config** — values from the `NetworkConfig` the adapter was instantiated with + +### Internal Architecture + +`@openzeppelin/adapter-evm` is a thin public layer that re-exports capabilities from `@openzeppelin/adapter-evm-core`. The core package centralizes all reusable EVM implementations: + +| Core Module | Responsibility | +| --- | --- | +| ABI loading | Etherscan / Sourcify fetching, proxy detection | +| Schema transformation | ABI → `ContractSchema` conversion | +| Input/output conversion | Solidity type ↔ form field mapping | +| Query helpers | Public client creation, view function calls | +| Transaction execution | Calldata formatting, strategy dispatch | +| Wallet infrastructure | Wagmi config, RainbowKit setup, session management | +| Relayer | Relayer SDK integration, network service forms | +| Access Control | Role queries, grant/revoke operations | + +## Stellar — `@openzeppelin/adapter-stellar` + +The Stellar adapter provides a complete Soroban implementation with all 13 capabilities. + +**Supported Networks**: Stellar Public, Stellar Testnet + +**Profiles Supported**: Declarative, Viewer, Transactor, Composer, Operator + +### Highlights + +- **Contract Loading**: Loads Soroban contract specifications and transforms them into the shared `ContractSchema`. +- **Type Mapping**: Maps Soroban types (`Address`, `i128`, `Bytes`, `Vec`, `Map`) to form fields with input validation. +- **Execution**: Parses form values into Soroban `ScVal` arguments. Supports both EOA and Relayer execution strategies. +- **Wallet Integration**: Uses Stellar Wallets Kit with React UI providers and hooks. +- **Access Control**: First-class support for Ownable and AccessControl patterns, including role queries, ownership actions, and optional indexer-backed historical lookups. +- **SAC Support**: Detects Stellar Asset Contracts and dynamically loads their specifications. + +## Polkadot — `@openzeppelin/adapter-polkadot` + +The Polkadot adapter targets EVM-compatible parachains and relay chains, reusing the shared EVM core for most capabilities. + +**Supported Networks**: Polkadot Hub, Kusama Hub, Moonbeam, Moonriver + +**Profiles Supported**: Declarative, Viewer, Transactor, Composer (EVM execution path) + +### Highlights + +- **EVM Core Reuse**: Delegates ABI loading, queries, transaction execution, and wallet infrastructure to `adapter-evm-core`. +- **Polkadot Metadata**: Exposes relay chain associations and network category distinctions. +- **React Wallet Provider**: Ships wallet provider utilities for consumer applications. +- **Future Substrate Path**: Structured to support native Substrate/Wasm modules alongside the current EVM path. + + +Non-EVM execution paths (native Substrate) are not yet implemented. Requesting a profile that requires execution on a non-EVM network will throw `UnsupportedProfileError`. + + +## Midnight — `@openzeppelin/adapter-midnight` + +The Midnight adapter enables browser-based interaction with Midnight contracts using zero-knowledge proof workflows. + +**Supported Networks**: Midnight Testnet + +### Highlights + +- **Artifact Ingestion**: Supports ZIP-based artifact bundles for contract evaluation and ZK proof orchestration entirely in the browser. +- **Lace Wallet**: Integrates with the Lace wallet for signing and execution. +- **Runtime Secrets**: Handles organizer-only secrets as in-memory execution-time inputs, not persisted configuration. +- **Lazy Polyfills**: Midnight-specific browser polyfills are lazy-loaded so they don't affect other ecosystem bundles. +- **Export Bootstrap**: Supports exporting self-contained applications that bundle Midnight contract artifacts for standalone use. + +### Build Requirements + +Midnight requires host-provided plugin factories for WASM and top-level await: + +```ts +import { loadOpenZeppelinAdapterViteConfig } from '@openzeppelin/adapters-vite'; + +const adapterConfigs = await loadOpenZeppelinAdapterViteConfig({ + ecosystems: ['midnight'], + pluginFactories: { + midnight: { wasm, topLevelAwait }, + }, +}); +``` + +## Solana — `@openzeppelin/adapter-solana` + + +The Solana adapter is scaffolding only and is not yet production-ready. + + +The Solana package defines the package boundaries and network configurations for a future adapter. It currently provides: + +- Solana network configurations (Devnet, Testnet, Mainnet Beta) +- Package structure and sub-path export scaffolding +- Tier 1 capability implementations (Addressing, Explorer, NetworkCatalog, UiLabels) + +The Operator profile is explicitly unsupported — `createRuntime('operator', ...)` throws `UnsupportedProfileError` because `accessControl` factories are not yet implemented. + +## Capability Support Matrix + +| Capability | EVM | Stellar | Polkadot | Midnight | Solana | +| --- | --- | --- | --- | --- | --- | +| Addressing | ✅ | ✅ | ✅ | ✅ | ✅ | +| Explorer | ✅ | ✅ | ✅ | ✅ | ✅ | +| NetworkCatalog | ✅ | ✅ | ✅ | ✅ | ✅ | +| UiLabels | ✅ | ✅ | ✅ | ✅ | ✅ | +| ContractLoading | ✅ | ✅ | ✅ | ✅ | — | +| Schema | ✅ | ✅ | ✅ | ✅ | — | +| TypeMapping | ✅ | ✅ | ✅ | ✅ | — | +| Query | ✅ | ✅ | ✅ | ✅ | — | +| Execution | ✅ | ✅ | ✅ (EVM) | ✅ | — | +| Wallet | ✅ | ✅ | ✅ | ✅ | — | +| UiKit | ✅ | ✅ | ✅ | ✅ | — | +| Relayer | ✅ | ✅ | ✅ | — | — | +| AccessControl | ✅ | ✅ | ✅ | — | — | diff --git a/src/hooks/use-navigation-tree.ts b/src/hooks/use-navigation-tree.ts index 5c57bc00..53b96e2c 100644 --- a/src/hooks/use-navigation-tree.ts +++ b/src/hooks/use-navigation-tree.ts @@ -4,6 +4,7 @@ import { usePathname } from "next/navigation"; import { useEffect } from "react"; import { arbitrumStylusTree, + ecosystemAdaptersTree, ethereumEvmTree, impactTree, midnightTree, @@ -64,6 +65,8 @@ export function useNavigationTree() { return uniswapTree; } else if (pathname.startsWith("/substrate-runtimes")) { return polkadotTree; + } else if (pathname.startsWith("/ecosystem-adapters")) { + return ecosystemAdaptersTree; } else if (pathname.startsWith("/tools")) { return ethereumEvmTree; } diff --git a/src/navigation/ecosystem-adapters.json b/src/navigation/ecosystem-adapters.json new file mode 100644 index 00000000..0855ac83 --- /dev/null +++ b/src/navigation/ecosystem-adapters.json @@ -0,0 +1,27 @@ +[ + { + "type": "page", + "name": "Overview", + "url": "/ecosystem-adapters" + }, + { + "type": "page", + "name": "Architecture", + "url": "/ecosystem-adapters/architecture" + }, + { + "type": "page", + "name": "Getting Started", + "url": "/ecosystem-adapters/getting-started" + }, + { + "type": "page", + "name": "Supported Ecosystems", + "url": "/ecosystem-adapters/supported-ecosystems" + }, + { + "type": "page", + "name": "Building an Adapter", + "url": "/ecosystem-adapters/building-an-adapter" + } +] diff --git a/src/navigation/ethereum-evm.json b/src/navigation/ethereum-evm.json index 8ce1463b..09b78484 100644 --- a/src/navigation/ethereum-evm.json +++ b/src/navigation/ethereum-evm.json @@ -1246,6 +1246,37 @@ "name": "Role Manager", "url": "/role-manager" }, + { + "type": "folder", + "name": "Ecosystem Adapters", + "index": { + "type": "page", + "name": "Overview", + "url": "/ecosystem-adapters" + }, + "children": [ + { + "type": "page", + "name": "Architecture", + "url": "/ecosystem-adapters/architecture" + }, + { + "type": "page", + "name": "Getting Started", + "url": "/ecosystem-adapters/getting-started" + }, + { + "type": "page", + "name": "Supported Ecosystems", + "url": "/ecosystem-adapters/supported-ecosystems" + }, + { + "type": "page", + "name": "Building an Adapter", + "url": "/ecosystem-adapters/building-an-adapter" + } + ] + }, { "type": "folder", "name": "Defender", diff --git a/src/navigation/index.ts b/src/navigation/index.ts index eb8ac81b..35a30ea7 100644 --- a/src/navigation/index.ts +++ b/src/navigation/index.ts @@ -1,4 +1,5 @@ import arbitrumStylusData from "./arbitrum-stylus.json"; +import ecosystemAdaptersData from "./ecosystem-adapters.json"; import ethereumEvmData from "./ethereum-evm.json"; import impactData from "./impact.json"; import midnightData from "./midnight.json"; @@ -21,6 +22,7 @@ const zama = zamaData as NavigationNode[]; const uniswap = uniswapData as NavigationNode[]; const polkadot = polkadotData as NavigationNode[]; const impact = impactData as NavigationNode[]; +const ecosystemAdapters = ecosystemAdaptersData as NavigationNode[]; // Create separate navigation trees for each blockchain export const ethereumEvmTree: NavigationTree = { @@ -73,4 +75,9 @@ export const impactTree: NavigationTree = { children: impact, }; +export const ecosystemAdaptersTree: NavigationTree = { + name: "Ecosystem Adapters", + children: ecosystemAdapters, +}; + export * from "./types"; From 92be4d618182b6d5d335958d08480d9452fb2fb3 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Tue, 7 Apr 2026 12:39:09 +0000 Subject: [PATCH 2/7] fix(ecosystem-adapters): replace unsupported block-beta Mermaid syntax with flowchart subgraphs The block-beta diagram type causes a circular JSON serialization error in the Mermaid component. Replace with flowchart subgraphs which render correctly with colored tier groupings. Co-authored-by: Aleksandr Pasevin --- content/ecosystem-adapters/architecture.mdx | 45 ++++++++++++--------- 1 file changed, 25 insertions(+), 20 deletions(-) diff --git a/content/ecosystem-adapters/architecture.mdx b/content/ecosystem-adapters/architecture.mdx index 224601be..fb603593 100644 --- a/content/ecosystem-adapters/architecture.mdx +++ b/content/ecosystem-adapters/architecture.mdx @@ -41,30 +41,35 @@ flowchart TD Adapter functionality is decomposed into **13 capability interfaces** organized across **3 tiers**. The tiers reflect increasing levels of runtime requirements: stateless metadata, network-aware schema operations, and stateful wallet-dependent interactions. ```mermaid -block-beta - columns 3 - block:tier1:3 - columns 4 - t1h["Tier 1 — Lightweight"]:4 - Addressing Explorer NetworkCatalog UiLabels +flowchart TD + subgraph T1["Tier 1 — Lightweight"] + A1["Addressing"] + A2["Explorer"] + A3["NetworkCatalog"] + A4["UiLabels"] end - block:tier2:3 - columns 4 - t2h["Tier 2 — Schema"]:4 - ContractLoading Schema TypeMapping Query + + subgraph T2["Tier 2 — Schema"] + B1["ContractLoading"] + B2["Schema"] + B3["TypeMapping"] + B4["Query"] end - block:tier3:3 - columns 5 - t3h["Tier 3 — Runtime"]:5 - Execution Wallet UiKit Relayer AccessControl + + subgraph T3["Tier 3 — Runtime"] + C1["Execution"] + C2["Wallet"] + C3["UiKit"] + C4["Relayer"] + C5["AccessControl"] end - style tier1 fill:#e3f2fd,stroke:#1976d2,color:#000 - style tier2 fill:#fff3e0,stroke:#f57c00,color:#000 - style tier3 fill:#fce4ec,stroke:#c62828,color:#000 - style t1h fill:#bbdefb,stroke:#1976d2,color:#000 - style t2h fill:#ffe0b2,stroke:#f57c00,color:#000 - style t3h fill:#f8bbd0,stroke:#c62828,color:#000 + T1 ~~~ T2 + T2 ~~~ T3 + + style T1 fill:#e3f2fd,stroke:#1976d2,color:#000 + style T2 fill:#fff3e0,stroke:#f57c00,color:#000 + style T3 fill:#fce4ec,stroke:#c62828,color:#000 ``` | Tier | Category | Network Required | Wallet Required | Capabilities | From 657c053ce5869b51c567485ec04bd44eab012093 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Tue, 7 Apr 2026 13:59:28 +0000 Subject: [PATCH 3/7] fix(ecosystem-adapters): compact tier diagrams to reduce visual footprint - Overview: replace expanded 13-node tier tree with compact 3-node summary - Architecture: replace oversized Mermaid tier diagram with a single clean table - Removes redundant duplicate tier table Co-authored-by: Aleksandr Pasevin --- content/ecosystem-adapters/architecture.mdx | 40 +++------------------ content/ecosystem-adapters/index.mdx | 22 ++---------- 2 files changed, 7 insertions(+), 55 deletions(-) diff --git a/content/ecosystem-adapters/architecture.mdx b/content/ecosystem-adapters/architecture.mdx index fb603593..bbd50f9e 100644 --- a/content/ecosystem-adapters/architecture.mdx +++ b/content/ecosystem-adapters/architecture.mdx @@ -40,43 +40,11 @@ flowchart TD Adapter functionality is decomposed into **13 capability interfaces** organized across **3 tiers**. The tiers reflect increasing levels of runtime requirements: stateless metadata, network-aware schema operations, and stateful wallet-dependent interactions. -```mermaid -flowchart TD - subgraph T1["Tier 1 — Lightweight"] - A1["Addressing"] - A2["Explorer"] - A3["NetworkCatalog"] - A4["UiLabels"] - end - - subgraph T2["Tier 2 — Schema"] - B1["ContractLoading"] - B2["Schema"] - B3["TypeMapping"] - B4["Query"] - end - - subgraph T3["Tier 3 — Runtime"] - C1["Execution"] - C2["Wallet"] - C3["UiKit"] - C4["Relayer"] - C5["AccessControl"] - end - - T1 ~~~ T2 - T2 ~~~ T3 - - style T1 fill:#e3f2fd,stroke:#1976d2,color:#000 - style T2 fill:#fff3e0,stroke:#f57c00,color:#000 - style T3 fill:#fce4ec,stroke:#c62828,color:#000 -``` - -| Tier | Category | Network Required | Wallet Required | Capabilities | +| Tier | Category | Network | Wallet | Capabilities | | --- | --- | --- | --- | --- | -| **1** | Lightweight / Declarative | No | No | Addressing, Explorer, NetworkCatalog, UiLabels | -| **2** | Schema / Definition | Yes | No | ContractLoading, Schema, TypeMapping, Query | -| **3** | Runtime / Stateful | Yes | Yes | Execution, Wallet, UiKit, Relayer, AccessControl | +| **1** | Lightweight | No | No | `Addressing`, `Explorer`, `NetworkCatalog`, `UiLabels` | +| **2** | Schema | Yes | No | `ContractLoading`, `Schema`, `TypeMapping`, `Query` | +| **3** | Runtime | Yes | Yes | `Execution`, `Wallet`, `UiKit`, `Relayer`, `AccessControl` | ### Tier Import Rules diff --git a/content/ecosystem-adapters/index.mdx b/content/ecosystem-adapters/index.mdx index aaa890ac..23521988 100644 --- a/content/ecosystem-adapters/index.mdx +++ b/content/ecosystem-adapters/index.mdx @@ -26,25 +26,9 @@ Building cross-chain tooling traditionally forces developers into one of two tra ```mermaid flowchart LR App["Your Application"] --> Runtime["EcosystemRuntime"] - Runtime --> T1["Tier 1\nLightweight"] - Runtime --> T2["Tier 2\nSchema"] - Runtime --> T3["Tier 3\nRuntime"] - - T1 --> A1["Addressing"] - T1 --> A2["Explorer"] - T1 --> A3["NetworkCatalog"] - T1 --> A4["UiLabels"] - - T2 --> B1["ContractLoading"] - T2 --> B2["Schema"] - T2 --> B3["TypeMapping"] - T2 --> B4["Query"] - - T3 --> C1["Execution"] - T3 --> C2["Wallet"] - T3 --> C3["UiKit"] - T3 --> C4["Relayer"] - T3 --> C5["AccessControl"] + Runtime --> T1["Tier 1 — Lightweight\n4 capabilities\nAddressing, Explorer, ..."] + Runtime --> T2["Tier 2 — Schema\n4 capabilities\nContractLoading, Query, ..."] + Runtime --> T3["Tier 3 — Runtime\n5 capabilities\nExecution, Wallet, ..."] style T1 fill:#e3f2fd,stroke:#1976d2,color:#000 style T2 fill:#fff3e0,stroke:#f57c00,color:#000 From 9fa1590d38458981379ab3b6dff1900bef431055 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Tue, 7 Apr 2026 14:21:49 +0000 Subject: [PATCH 4/7] fix(ecosystem-adapters): replace messy profiles flowchart with clean capability matrix The Mermaid flowchart with cross-links between profiles and capabilities created a cluttered spider-web visual. Replaced with a single clear checkmark matrix table that shows exactly which capabilities each profile includes at a glance. Co-authored-by: Aleksandr Pasevin --- content/ecosystem-adapters/architecture.mdx | 52 +++++++-------------- 1 file changed, 16 insertions(+), 36 deletions(-) diff --git a/content/ecosystem-adapters/architecture.mdx b/content/ecosystem-adapters/architecture.mdx index bbd50f9e..a2ab6efd 100644 --- a/content/ecosystem-adapters/architecture.mdx +++ b/content/ecosystem-adapters/architecture.mdx @@ -78,45 +78,25 @@ This means importing `@openzeppelin/adapter-evm/addressing` will never pull in w Profiles are pre-composed bundles of capabilities that match common application archetypes. They exist for convenience — you can always consume individual capabilities directly via the `CapabilityFactoryMap`. -```mermaid -flowchart LR - subgraph Profiles - D["Declarative"] - V["Viewer"] - T["Transactor"] - Co["Composer"] - O["Operator"] - end - - D -->|"Tier 1"| T1["Addressing\nExplorer\nNetworkCatalog\nUiLabels"] - V -->|"+ Tier 2"| T2["ContractLoading\nSchema\nTypeMapping\nQuery"] - T -->|"+ Partial Tier 2/3"| T2b["ContractLoading\nSchema\nTypeMapping\nExecution\nWallet"] - Co -->|"+ Tier 2 + Tier 3"| T3a["Query\nExecution\nWallet\nUiKit\nRelayer"] - O -->|"+ Tier 2 + Tier 3"| T3b["Query\nExecution\nWallet\nUiKit\nAccessControl"] - - V -.-> T1 - T -.-> T1 - Co -.-> T1 - O -.-> T1 - Co -.-> T2 - O -.-> T2 - - style D fill:#e3f2fd,stroke:#1976d2,color:#000 - style V fill:#e8f5e9,stroke:#388e3c,color:#000 - style T fill:#fff3e0,stroke:#f57c00,color:#000 - style Co fill:#f3e5f5,stroke:#7b1fa2,color:#000 - style O fill:#fce4ec,stroke:#c62828,color:#000 -``` +Each profile is a strict superset of Declarative — higher profiles add capabilities incrementally: ### Profile–Capability Matrix -| Profile | Use Case | Tier 1 (all 4) | Tier 2 | Tier 3 | -| --- | --- | --- | --- | --- | -| **Declarative** | Metadata-only (catalogs, validators) | All | — | — | -| **Viewer** | Read-only (dashboards, analytics) | All | All 4 | — | -| **Transactor** | Write-only (send, approve, mint) | All | ContractLoading, Schema, TypeMapping | Execution, Wallet | -| **Composer** | Full-featured UI Builder apps | All | All 4 | Execution, Wallet, UiKit, Relayer | -| **Operator** | Role/permission management | All | All 4 | Execution, Wallet, UiKit, AccessControl | +| Capability | Declarative | Viewer | Transactor | Composer | Operator | +| --- | --- | --- | --- | --- | --- | +| `Addressing` | ✅ | ✅ | ✅ | ✅ | ✅ | +| `Explorer` | ✅ | ✅ | ✅ | ✅ | ✅ | +| `NetworkCatalog` | ✅ | ✅ | ✅ | ✅ | ✅ | +| `UiLabels` | ✅ | ✅ | ✅ | ✅ | ✅ | +| `ContractLoading` | | ✅ | ✅ | ✅ | ✅ | +| `Schema` | | ✅ | ✅ | ✅ | ✅ | +| `TypeMapping` | | ✅ | ✅ | ✅ | ✅ | +| `Query` | | ✅ | | ✅ | ✅ | +| `Execution` | | | ✅ | ✅ | ✅ | +| `Wallet` | | | ✅ | ✅ | ✅ | +| `UiKit` | | | | ✅ | ✅ | +| `Relayer` | | | | ✅ | | +| `AccessControl` | | | | | ✅ | ### Profile Selection Guide From ed372e52066b36bdf221322817f9dcdaa4efc187 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Tue, 7 Apr 2026 15:35:04 +0000 Subject: [PATCH 5/7] fix(ecosystem-adapters): replace At a Glance diagram with compact summary table The Mermaid subgraph cards took excessive vertical space for minimal info. Replaced with a single 5-row table showing adapter, networks, capability count, and status. Co-authored-by: Aleksandr Pasevin --- .../supported-ecosystems.mdx | 47 +++---------------- 1 file changed, 7 insertions(+), 40 deletions(-) diff --git a/content/ecosystem-adapters/supported-ecosystems.mdx b/content/ecosystem-adapters/supported-ecosystems.mdx index 57ee13b1..f97f116d 100644 --- a/content/ecosystem-adapters/supported-ecosystems.mdx +++ b/content/ecosystem-adapters/supported-ecosystems.mdx @@ -4,46 +4,13 @@ title: Supported Ecosystems Each adapter implements the subset of the [13 capability interfaces](/ecosystem-adapters/architecture#capability-tiers) that its blockchain supports. This page summarizes what each production adapter provides. -## At a Glance - -```mermaid -flowchart LR - subgraph EVM["EVM Adapter"] - direction TB - E1["13/13 capabilities"] - E2["Ethereum, Polygon,\nArbitrum, Base, ..."] - end - - subgraph Stellar["Stellar Adapter"] - direction TB - S1["13/13 capabilities"] - S2["Stellar Public,\nStellar Testnet"] - end - - subgraph Polkadot["Polkadot Adapter"] - direction TB - P1["EVM-path capabilities"] - P2["Polkadot Hub,\nMoonbeam, Moonriver"] - end - - subgraph Midnight["Midnight Adapter"] - direction TB - M1["ZK artifact workflows"] - M2["Midnight Testnet"] - end - - subgraph Solana["Solana Adapter"] - direction TB - So1["Scaffolding only"] - So2["Devnet, Testnet"] - end - - style EVM fill:#e3f2fd,stroke:#1976d2,color:#000 - style Stellar fill:#fff3e0,stroke:#f57c00,color:#000 - style Polkadot fill:#f3e5f5,stroke:#7b1fa2,color:#000 - style Midnight fill:#e0f2f1,stroke:#00897b,color:#000 - style Solana fill:#fafafa,stroke:#9e9e9e,color:#000 -``` +| Adapter | Networks | Capabilities | Status | +| --- | --- | --- | --- | +| **EVM** | Ethereum, Polygon, Arbitrum, Base, Optimism, ... | 13/13 | Production | +| **Stellar** | Stellar Public, Stellar Testnet | 13/13 | Production | +| **Polkadot** | Polkadot Hub, Moonbeam, Moonriver | 13/13 (EVM path) | Production | +| **Midnight** | Midnight Testnet | 11/13 | Production | +| **Solana** | Devnet, Testnet, Mainnet Beta | 4/13 (Tier 1 only) | Scaffolding | ## EVM — `@openzeppelin/adapter-evm` From 38ec4f5935295c13809433a6939674d903fac34b Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Tue, 7 Apr 2026 18:20:33 +0000 Subject: [PATCH 6/7] feat(ecosystem-adapters): add React integration and multi-ecosystem docs - Add 'Using with React' section covering wallet context providers, configureUiKit, connectWallet/disconnectWallet, and hook facade pattern (useAccount, useSwitchChain, etc.) - Add 'Multi-Ecosystem Apps' section showing how to instantiate runtimes from EVM + Stellar adapters in the same application - Add prominent link to the adapters GitHub repo on the overview page - All API details referenced from adapter source code Co-authored-by: Aleksandr Pasevin --- .../ecosystem-adapters/getting-started.mdx | 201 ++++++++++++++++++ content/ecosystem-adapters/index.mdx | 4 + 2 files changed, 205 insertions(+) diff --git a/content/ecosystem-adapters/getting-started.mdx b/content/ecosystem-adapters/getting-started.mdx index 98ce7277..915d3b35 100644 --- a/content/ecosystem-adapters/getting-started.mdx +++ b/content/ecosystem-adapters/getting-started.mdx @@ -184,8 +184,209 @@ try { } ``` +## Using with React + +Adapters integrate with React through three layers: a **wallet context provider** that manages wallet SDK state, **`configureUiKit`** that selects and configures the wallet UI kit, and a **hook facade** that exposes a uniform set of React hooks across every ecosystem. + +### Wallet Context Provider + +Each adapter exports a root React component via `getEcosystemReactUiContextProvider()`. This component wraps your application (or the relevant subtree) and bootstraps the wallet SDK context — for example, EVM adapters render `WagmiProvider` and `QueryClientProvider` internally, while the Stellar adapter renders its own `StellarWalletContext.Provider`. + +You don't render these providers yourself. Instead, pass the runtime to a shared `WalletStateProvider` from `@openzeppelin/ui-react`, which delegates to the correct ecosystem provider automatically: + +```tsx +import { WalletStateProvider } from '@openzeppelin/ui-react'; + +function App() { + return ( + + + + ); +} +``` + +The `loadConfigModule` callback lets the adapter lazily load user-authored wallet configuration files (e.g. `src/config/wallet/rainbowkit.config.ts` for a RainbowKit setup). If you don't use a native config file, pass a function that returns `null`. + +### Configuring the UI Kit + +Before rendering wallet UI components, configure the UI kit on the runtime's `uiKit` capability. This selects which wallet UI library to use (RainbowKit, a custom theme, or none) and passes kit-specific options: + +```ts +await runtime.uiKit?.configureUiKit( + { + kitName: 'rainbowkit', // or 'custom', 'none' + kitConfig: { + // kit-specific options, e.g., showInjectedConnector: true + }, + }, + { + loadUiKitNativeConfig: loadAppConfigModule, + } +); +``` + +After configuration, retrieve the wallet UI components from the runtime: + +```ts +const walletComponents = runtime.uiKit?.getEcosystemWalletComponents?.(); +const ConnectButton = walletComponents?.ConnectButton; +const AccountDisplay = walletComponents?.AccountDisplay; +const NetworkSwitcher = walletComponents?.NetworkSwitcher; +``` + +### Connecting and Disconnecting Wallets + +The `WalletCapability` on the runtime exposes imperative methods for wallet lifecycle management: + +```ts +// List available wallet connectors +const connectors = runtime.wallet.getAvailableConnectors(); + +// Connect using a specific connector +await runtime.wallet.connectWallet(connectors[0].id); + +// Check connection status +const status = runtime.wallet.getWalletConnectionStatus(); + +// Listen for connection changes +const unsubscribe = runtime.wallet.onWalletConnectionChange?.((newStatus) => { + console.log('Wallet status changed:', newStatus); +}); + +// Disconnect +await runtime.wallet.disconnectWallet(); +``` + + +`connectWallet` and `disconnectWallet` are imperative calls on the runtime — they are independent of the React rendering layer. You can call them from event handlers, effects, or outside React entirely. + + +### Hook Facade Pattern + +Every adapter exports a **facade hooks** object that conforms to the `EcosystemSpecificReactHooks` interface. This gives consuming applications a uniform set of React hooks regardless of the underlying wallet library. + +The EVM adapter maps its facade to [wagmi](https://wagmi.sh/) hooks, the Stellar adapter provides custom implementations, and other adapters follow the same pattern: + +```ts +// EVM facade (wraps wagmi) +import { evmFacadeHooks } from '@openzeppelin/adapter-evm'; + +// Stellar facade (custom hooks) +import { stellarFacadeHooks } from '@openzeppelin/adapter-stellar'; +``` + +Each facade object includes these hooks: + +| Hook | Purpose | +| --- | --- | +| `useAccount` | Returns the connected account address, connection status, and chain info | +| `useConnect` | Provides a `connect` function and available connectors | +| `useDisconnect` | Provides a `disconnect` function | +| `useSwitchChain` | Returns a `switchChain` function (EVM-only; stubs on other ecosystems) | +| `useChainId` | Returns the currently active chain ID | +| `useChains` | Returns the list of configured chains | +| `useBalance` | Returns the native token balance for the connected account | + +In practice, you access facade hooks through the runtime rather than importing them directly — the runtime's `uiKit.getEcosystemReactHooks()` method returns the correct facade for the active ecosystem: + +```tsx +import { useWalletState } from '@openzeppelin/ui-react'; + +function AccountInfo() { + const { walletFacadeHooks } = useWalletState(); + + const { address, isConnected } = walletFacadeHooks.useAccount(); + const { switchChain } = walletFacadeHooks.useSwitchChain(); + + if (!isConnected) return

Not connected

; + + return ( +
+

Connected: {address}

+ +
+ ); +} +``` + + +Hooks that don't apply to a given ecosystem return safe stubs. For example, the Stellar facade's `useSwitchChain` returns `{ switchChain: undefined }` — your components can feature-detect this without conditional imports. + + +## Multi-Ecosystem Apps + +Applications that interact with multiple blockchains create one runtime per ecosystem. Each runtime is independent — it manages its own wallet session, RPC connections, and lifecycle. + +### Setup + +Install the adapters you need and the shared Vite plugin: + +```bash +pnpm add @openzeppelin/adapter-evm @openzeppelin/adapter-stellar @openzeppelin/ui-types +pnpm add -D @openzeppelin/adapters-vite +``` + +Merge adapter build requirements with `defineOpenZeppelinAdapterViteConfig`: + +```ts +// vite.config.ts +import { defineOpenZeppelinAdapterViteConfig } from '@openzeppelin/adapters-vite'; +import react from '@vitejs/plugin-react'; + +export default defineOpenZeppelinAdapterViteConfig({ + ecosystems: ['evm', 'stellar'], + config: { + plugins: [react()], + }, +}); +``` + +### Creating Runtimes + +Instantiate a runtime from each adapter's `ecosystemDefinition`: + +```ts +import { ecosystemDefinition as evmDefinition } from '@openzeppelin/adapter-evm'; +import { ecosystemDefinition as stellarDefinition } from '@openzeppelin/adapter-stellar'; + +// EVM runtime — e.g., Ethereum Mainnet +const evmNetwork = evmDefinition.networks[0]; +const evmRuntime = evmDefinition.createRuntime('composer', evmNetwork); + +// Stellar runtime — e.g., Stellar Mainnet +const stellarNetwork = stellarDefinition.networks[0]; +const stellarRuntime = stellarDefinition.createRuntime('viewer', stellarNetwork); +``` + +Each runtime is fully isolated. The EVM runtime connects to Ethereum via wagmi, the Stellar runtime connects to Horizon/Soroban — they share no state and can be disposed independently: + +```ts +// Use both runtimes side by side +const evmValid = evmRuntime.addressing.isValidAddress('0x1234...'); +const stellarValid = stellarRuntime.addressing.isValidAddress('G...'); + +const evmBalance = await evmRuntime.query.queryViewFunction( + '0xToken', 'balanceOf', ['0xOwner'] +); + +// Dispose one without affecting the other +evmRuntime.dispose(); +// stellarRuntime continues working +``` + + +Wallet sessions are ecosystem-scoped, not runtime-scoped. Disposing an EVM runtime to switch from Mainnet to Sepolia does not disconnect the user's MetaMask — the wallet session persists across runtime recreation within the same ecosystem. + + ## Next Steps - Read the [Architecture](/ecosystem-adapters/architecture) page for a deep dive into tiers, profiles, and lifecycle management - Explore [Supported Ecosystems](/ecosystem-adapters/supported-ecosystems) to see what each adapter provides - Follow the [Building an Adapter](/ecosystem-adapters/building-an-adapter) guide to add support for a new chain +- Browse the [adapter source code on GitHub](https://github.com/OpenZeppelin/openzeppelin-adapters) for implementation details diff --git a/content/ecosystem-adapters/index.mdx b/content/ecosystem-adapters/index.mdx index 23521988..e6922464 100644 --- a/content/ecosystem-adapters/index.mdx +++ b/content/ecosystem-adapters/index.mdx @@ -4,6 +4,10 @@ title: Ecosystem Adapters **OpenZeppelin Ecosystem Adapters** are a set of modular, chain-specific integration packages that let applications interact with any supported blockchain through a single, unified interface. Built on 13 composable capability interfaces organized in 3 tiers, each adapter encapsulates everything needed — contract loading, type mapping, transaction execution, wallet connection, and network configuration — while keeping consuming applications completely chain-agnostic. + +**Source code**: The adapters are open-source. Browse the implementation, open issues, and contribute at [**github.com/OpenZeppelin/openzeppelin-adapters**](https://github.com/OpenZeppelin/openzeppelin-adapters). + + Understand the capability-based architecture: tiers, profiles, and the runtime lifecycle. From 588d11dc31ea887101ea5a01420f3aa710c4f2c7 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Tue, 7 Apr 2026 19:03:06 +0000 Subject: [PATCH 7/7] feat(navigation): add Ecosystem Adapters to all ecosystem sidebars Add the Ecosystem Adapters folder to Stellar, Polkadot, Midnight, and Arbitrum Stylus navigation trees so the section is discoverable from every supported ecosystem, not just EVM. Co-authored-by: Aleksandr Pasevin --- src/navigation/arbitrum-stylus.json | 31 +++++++++++++++++++++++++ src/navigation/midnight.json | 35 +++++++++++++++++++++++++++++ src/navigation/polkadot.json | 31 +++++++++++++++++++++++++ src/navigation/stellar.json | 31 +++++++++++++++++++++++++ 4 files changed, 128 insertions(+) diff --git a/src/navigation/arbitrum-stylus.json b/src/navigation/arbitrum-stylus.json index 0a8cd80d..0641c29d 100644 --- a/src/navigation/arbitrum-stylus.json +++ b/src/navigation/arbitrum-stylus.json @@ -612,5 +612,36 @@ "external": true } ] + }, + { + "type": "folder", + "name": "Ecosystem Adapters", + "index": { + "type": "page", + "name": "Overview", + "url": "/ecosystem-adapters" + }, + "children": [ + { + "type": "page", + "name": "Architecture", + "url": "/ecosystem-adapters/architecture" + }, + { + "type": "page", + "name": "Getting Started", + "url": "/ecosystem-adapters/getting-started" + }, + { + "type": "page", + "name": "Supported Ecosystems", + "url": "/ecosystem-adapters/supported-ecosystems" + }, + { + "type": "page", + "name": "Building an Adapter", + "url": "/ecosystem-adapters/building-an-adapter" + } + ] } ] diff --git a/src/navigation/midnight.json b/src/navigation/midnight.json index d1411012..f782f069 100644 --- a/src/navigation/midnight.json +++ b/src/navigation/midnight.json @@ -118,5 +118,40 @@ "url": "/contracts-compact/api/utils" } ] + }, + { + "type": "separator", + "name": "Open Source Tools" + }, + { + "type": "folder", + "name": "Ecosystem Adapters", + "index": { + "type": "page", + "name": "Overview", + "url": "/ecosystem-adapters" + }, + "children": [ + { + "type": "page", + "name": "Architecture", + "url": "/ecosystem-adapters/architecture" + }, + { + "type": "page", + "name": "Getting Started", + "url": "/ecosystem-adapters/getting-started" + }, + { + "type": "page", + "name": "Supported Ecosystems", + "url": "/ecosystem-adapters/supported-ecosystems" + }, + { + "type": "page", + "name": "Building an Adapter", + "url": "/ecosystem-adapters/building-an-adapter" + } + ] } ] diff --git a/src/navigation/polkadot.json b/src/navigation/polkadot.json index 66c48e4c..29140d70 100644 --- a/src/navigation/polkadot.json +++ b/src/navigation/polkadot.json @@ -745,5 +745,36 @@ "external": true } ] + }, + { + "type": "folder", + "name": "Ecosystem Adapters", + "index": { + "type": "page", + "name": "Overview", + "url": "/ecosystem-adapters" + }, + "children": [ + { + "type": "page", + "name": "Architecture", + "url": "/ecosystem-adapters/architecture" + }, + { + "type": "page", + "name": "Getting Started", + "url": "/ecosystem-adapters/getting-started" + }, + { + "type": "page", + "name": "Supported Ecosystems", + "url": "/ecosystem-adapters/supported-ecosystems" + }, + { + "type": "page", + "name": "Building an Adapter", + "url": "/ecosystem-adapters/building-an-adapter" + } + ] } ] diff --git a/src/navigation/stellar.json b/src/navigation/stellar.json index be1994a8..f813898f 100644 --- a/src/navigation/stellar.json +++ b/src/navigation/stellar.json @@ -644,5 +644,36 @@ "type": "page", "name": "Role Manager", "url": "/role-manager" + }, + { + "type": "folder", + "name": "Ecosystem Adapters", + "index": { + "type": "page", + "name": "Overview", + "url": "/ecosystem-adapters" + }, + "children": [ + { + "type": "page", + "name": "Architecture", + "url": "/ecosystem-adapters/architecture" + }, + { + "type": "page", + "name": "Getting Started", + "url": "/ecosystem-adapters/getting-started" + }, + { + "type": "page", + "name": "Supported Ecosystems", + "url": "/ecosystem-adapters/supported-ecosystems" + }, + { + "type": "page", + "name": "Building an Adapter", + "url": "/ecosystem-adapters/building-an-adapter" + } + ] } ]