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
218 changes: 149 additions & 69 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,85 +1,165 @@
# Relaycode: Rethinking Extrinsics in Polkadot
# Relaycode

[![CI](https://github.com/itsyogesh/relaycode/actions/workflows/ci.yml/badge.svg)](https://github.com/itsyogesh/relaycode/actions/workflows/ci.yml)
[![codecov](https://codecov.io/gh/itsyogesh/relaycode/branch/master/graph/badge.svg)](https://codecov.io/gh/itsyogesh/relaycode)
[![License: Apache-2.0](https://img.shields.io/badge/License-Apache_2.0-blue.svg)](https://opensource.org/licenses/Apache-2.0)
[![TypeScript](https://img.shields.io/badge/TypeScript-strict-blue.svg)](https://www.typescriptlang.org/)
[![W3F Grant](https://img.shields.io/badge/Web3_Foundation-Grant-green.svg)](https://grants.web3.foundation/)

## Project Overview
Relaycode is an intuitive extrinsics builder designed to transform the way developers and users interact with extrinsics in the Polkadot ecosystem. By bridging the gap between complex pallet operations and user-friendly interfaces, Relaycode gives both regular users and developers the ability to harness the full potential of extrinsics the Polkadot ecosystem.
The developer toolkit for Polkadot. Build extrinsics, write smart contracts, and interact with Substrate chains — all from your browser.

### Key Features:
<p align="center">
<img src="public/og/home.png" alt="Relaycode — The Developer Toolkit for Polkadot" width="800"/>
</p>

1. **A New Extrinsic Builder**: Our state-of-the-art builder allows users to construct extrinsics with ease, providing real-time encoding and decoding for immediate visual feedback.
## Tools

2. **Dual-Pane Interface**: Relaycode lets you see the best of both worlds with our split-view design. Build extrinsics using human-readable inputs on one side, while simultaneously viewing the corresponding encoded data on the other.
### Contract Studio

3. **Bi-Directional Editing**: Seamlessly switch between editing human-readable values and raw encoded data. Changes in one pane are instantly reflected in the other, offering unparalleled flexibility.
<img src="public/og/studio.png" alt="Relaycode Studio" width="600"/>

4. **Wallet Integration**: Connect your Polkadot wallet directly within Relaycode to sign and submit extrinsics, eliminating the need for external tools or interfaces.
Browser-based smart contract IDE for Polkadot Hub. Write Solidity, compile to EVM or PVM (PolkaVM), and deploy with native Polkadot wallets. No CLI, no MetaMask, no fragmented toolchain.

5. **Customizable Snippets**: Create, save, and share reusable extrinsic templates. Streamline complex processes by chaining multiple calls into a single, user-friendly form.
**[relaycode.org/studio](https://relaycode.org/studio)**

6. **Educational Tools**: Built-in guides and tooltips help users understand the intricacies of extrinsics, making Relaycode an excellent learning platform for Polkadot users.
### Extrinsic Builder

<p align="center">
<img src="docs/demo.gif" alt="Relaycode Demo" width="800"/>
</p>
<img src="public/og/builder.png" alt="Relaycode Builder" width="600"/>

Visual extrinsic builder for the Polkadot ecosystem. Build, encode, decode, and submit any Substrate extrinsic with a dual-pane interface. Supports all pallets across all chains.

**[relaycode.org/builder](https://relaycode.org/builder)**

### Component Docs

Documentation for input components, encoding/decoding APIs, and usage guides. Built with Fumadocs.

**[relaycode.org/docs](https://relaycode.org/docs)**

### Substrate Utilities *(planned)*

SS58, EVM, and hex address converter plus other Substrate utilities.

## Tech Stack

| Layer | Technology |
|-------|-----------|
| **Framework** | Next.js 15 (App Router) |
| **Language** | TypeScript (strict mode) |
| **Styling** | Tailwind CSS + shadcn/ui |
| **Polkadot Client** | [Dedot](https://github.com/dedotdev/dedot) |
| **Wallet** | [LunoKit](https://github.com/nickytonline/luno-kit) (Polkadot.js, Talisman, SubWallet) |
| **Smart Contracts** | Solidity → EVM / PVM via PolkaVM |
| **Docs** | [Fumadocs](https://fumadocs.vercel.app/) |
| **Testing** | Jest + React Testing Library |
| **Deployment** | Vercel (Edge + Serverless) |
| **Package Manager** | Yarn 1.x |

### Supported Chains

Polkadot, Kusama, Westend, Paseo, Asset Hubs, People chains, Coretime chains.

## Getting Started

### Prerequisites

- Node.js 18+
- Yarn 1.x (`npm install -g yarn`)

### Setup

```bash
# Clone the repository
git clone https://github.com/itsyogesh/relaycode.git
cd relaycode

# Install dependencies
yarn install

# Start the development server
yarn dev
```

Open [http://localhost:3000](http://localhost:3000) in your browser.

### Environment Variables

Copy the example and configure:

```bash
cp .env.example .env
```

| Variable | Description | Default |
|----------|------------|---------|
| `NEXT_PUBLIC_APP_URL` | Application URL | `http://localhost:3000` |
| `NEXT_PUBLIC_DEFAULT_CHAIN` | Default chain identifier | `pop-network-testnet` |
| `NEXT_PUBLIC_SUPPORTED_CHAINS` | JSON array of supported chains | See `.env` |

### Available Commands

| Command | Description |
|---------|------------|
| `yarn dev` | Start development server |
| `yarn build` | Production build |
| `yarn start` | Start production server |
| `yarn lint` | Run ESLint |
| `yarn test` | Run Jest tests |
| `yarn test:watch` | Run tests in watch mode |

## Project Structure

```
app/
(marketing)/ # Landing pages
builder/ # Extrinsic builder
studio/ # Contract Studio IDE
docs/ # Documentation (Fumadocs)
api/
og/ # Dynamic OG image generation
compile/ # Solidity compilation endpoint
components/
builder/ # Extrinsic builder components
studio/ # Contract Studio components
params/inputs/ # Substrate type input components
ui/ # shadcn/ui base components
context/ # React context providers
hooks/ # Custom React hooks
lib/ # Utility libraries
types/ # TypeScript type definitions
```

### Input Components

The extrinsic builder includes type-aware input components for every Substrate type:

Account, Amount, Balance, Bool, BTreeMap, BTreeSet, Bytes, Call, Enum, Hash (H160/H256/H512), KeyValue, Moment, Option, Struct, Text, Tuple, Vector, VectorFixed, Vote, VoteThreshold.

Each component maps directly to SCALE-encoded Substrate types and supports validation, encoding, and real-time feedback.

## Deployment

Relaycode is deployed on Vercel. Pushes to `master` trigger automatic production deployments.

OG images are generated dynamically at the edge via `next/og` ImageResponse routes:

- `/api/og/home` — Homepage
- `/api/og/studio` — Contract Studio
- `/api/og/builder` — Extrinsic Builder
- `/api/og/docs` — Documentation

## Contributing

1. Fork the repository
2. Create a feature branch (`git checkout -b feat/my-feature`)
3. Commit your changes
4. Push to the branch (`git push origin feat/my-feature`)
5. Open a Pull Request

## License

[Apache 2.0](LICENSE)

## Acknowledgments

## Technical Architecture
- Frontend: Next.js 15 with App Router, React, TypeScript
- Styling: Tailwind CSS, shadcn/ui components
- State Management: React Hooks, Context API
- Polkadot Integration: [Dedot](https://github.com/dedotdev/dedot)
- Wallet: [LunoKit](https://github.com/nickytonline/luno-kit) (Polkadot.js, Talisman, SubWallet)
- Chains: Polkadot, Kusama, Westend (testnet)
- Theming: next-themes for dark/light mode support

## Documentation

- [API Reference](docs/api/README.md) - Core encoding, decoding, and validation APIs
- [Component Reference](docs/components/README.md) - Input component documentation
- [Getting Started Tutorial](docs/tutorial/getting-started.md) - Build your first extrinsic
- [Advanced Usage](docs/tutorial/advanced.md) - Bi-directional editing, batch calls, complex types
- [Testing Guide](docs/testing-guide.md) - How to run and write tests

## Milestones

For detailed project milestones and deliverables, see our [Milestones](docs/relaycode.md) documentation.

### Implemented Input Components

The following input components have been implemented for the extrinsic builder:

- [x] **Account** - Handles `AccountId`, `Address`, `LookupSource`, `MultiAddress`
- [x] **Amount** - Handles `i8`, `i16`, `i32`, `i64`, `i128`, `u8`, `u16`, `u32`, `u64`, `u128`, `Compact<uN>`
- [x] **Balance** - Handles `Balance`, `BalanceOf`, `Compact<Balance>`, `Compact<BalanceOf>`
- [x] **Bool** - Handles `bool`
- [x] **BTreeMap** - Handles `BTreeMap<K, V>` with typed key-value pair inputs
- [x] **BTreeSet** - Handles `BTreeSet<T>` with duplicate detection
- [x] **Bytes** - Handles `Bytes`, `Vec<u8>`
- [x] **Call** - Handles `Call`, `RuntimeCall`
- [x] **Enum** - Handles enum types from metadata
- [x] **Hash160** - Handles `H160` (20-byte hash)
- [x] **Hash256** - Handles `Hash`, `H256` (32-byte hash)
- [x] **Hash512** - Handles `H512` (64-byte hash)
- [x] **KeyValue** - Handles `KeyValue`
- [x] **Moment** - Handles `Moment`, `MomentOf`
- [x] **Option** - Handles `Option<T>`
- [x] **Text** - Handles `String`, `Text` (and fallback for unknown types)
- [x] **Struct** - Handles composite/struct types
- [x] **Tuple** - Handles tuple types `(T1, T2, ...)`
- [x] **Vector** - Handles `Vec<T>`, `BoundedVec<T, S>`
- [x] **VectorFixed** - Handles fixed-length arrays `[T; N]`
- [x] **Vote** - Handles `Vote`
- [x] **VoteThreshold** - Handles `VoteThreshold`

### M2 Features

- [x] **Chain Selector** - Switch between Polkadot, Kusama, and Westend (testnet) from the navbar
- [x] **Type Badges** - Builder displays Substrate type names alongside field labels
- [x] **Wallet Integration** - LunoKit-based wallet connect (Polkadot.js, Talisman, SubWallet)
- [x] **Bi-Directional Editing** - Edit via form or raw hex, with real-time sync
- [x] **Transaction Submission** - Sign and submit extrinsics directly from the builder
- [x] **Input Validation** - Pre-submission validation with descriptive error messages
Funded by a [Web3 Foundation](https://web3.foundation/) grant. Built with [Dedot](https://github.com/dedotdev/dedot) and [LunoKit](https://github.com/nickytonline/luno-kit).
11 changes: 7 additions & 4 deletions __tests__/lib/utils.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,16 +58,19 @@ describe("absoluteUrl", () => {
describe("constructMetadata", () => {
it("returns defaults when called with no args", () => {
const meta = constructMetadata();
expect(meta.title).toBe("Relaycode");
expect(meta.description).toBe("Test description");
expect(meta.title).toEqual({
default: expect.any(String),
template: "%s | Relaycode",
});
expect(meta.description).toBeDefined();
});

it("uses custom values", () => {
const meta = constructMetadata({
title: "Custom Title",
description: "Custom description",
});
expect(meta.title).toBe("Custom Title");
expect((meta.title as any).default).toBe("Custom Title");
expect(meta.description).toBe("Custom description");
});

Expand All @@ -78,6 +81,6 @@ describe("constructMetadata", () => {

it("does not include robots when noIndex is false", () => {
const meta = constructMetadata({ noIndex: false });
expect(meta.robots).toBeUndefined();
expect(meta.robots).toEqual({ index: true, follow: true });
});
});
7 changes: 5 additions & 2 deletions app/builder/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,19 @@ import { SiteFooter } from "@/components/layout/site-footer";
import { Blocks } from "lucide-react";

export const metadata: Metadata = {
title: "Relaycode Builder — Polkadot Extrinsic Builder",
title: "Extrinsic Builder — Build and Submit Substrate Extrinsics",
description:
"Build, encode, decode, and submit any Substrate extrinsic visually. Supports all pallets across the Polkadot ecosystem.",
"Visual extrinsic builder for Polkadot. Build, encode, decode, and submit any pallet call across all Substrate chains.",
openGraph: {
images: [{ url: "/api/og/builder", width: 1200, height: 630 }],
},
twitter: {
card: "summary_large_image",
images: ["/api/og/builder"],
},
alternates: {
canonical: "/builder",
},
};

interface BuilderLayoutProps {
Expand Down
7 changes: 5 additions & 2 deletions app/docs/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,19 @@ import { DocsLayout } from "fumadocs-ui/layouts/docs";
import { source } from "@/lib/source";

export const metadata: Metadata = {
title: "Relaycode Docs — Developer Documentation",
title: "Docs — Guides, References, and Examples",
description:
"Developer documentation for the Relaycode toolkit. Guides, references, and examples for building on Polkadot.",
"Developer documentation for Relaycode. Guides, API references, and examples for building on Polkadot with Substrate.",
openGraph: {
images: [{ url: "/api/og/docs", width: 1200, height: 630 }],
},
twitter: {
card: "summary_large_image",
images: ["/api/og/docs"],
},
alternates: {
canonical: "/docs",
},
};

export default function Layout({ children }: { children: ReactNode }) {
Expand Down
44 changes: 43 additions & 1 deletion app/layout.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import "@/styles/globals.css";

import type { Viewport } from "next";
import { Nunito } from "next/font/google";
import { GeistMono } from "geist/font/mono";
import { GeistSans } from "geist/font/sans";
Expand All @@ -23,12 +24,53 @@ const nunito = Nunito({
variable: "--font-nunito",
});

export const viewport: Viewport = {
width: "device-width",
initialScale: 1,
maximumScale: 5,
themeColor: [
{ media: "(prefers-color-scheme: light)", color: "#ffffff" },
{ media: "(prefers-color-scheme: dark)", color: "#0a0a0a" },
],
};

export const metadata = constructMetadata();

export default function RootLayout({ children }: RootLayoutProps) {
return (
<html lang="en" suppressHydrationWarning>
<head />
<head>
<script
type="application/ld+json"
dangerouslySetInnerHTML={{
__html: JSON.stringify({
"@context": "https://schema.org",
"@type": "SoftwareApplication",
name: "Relaycode",
url: "https://relaycode.org",
applicationCategory: "DeveloperApplication",
operatingSystem: "Web",
description:
"Build extrinsics, write smart contracts, and interact with Substrate chains. Browser-based tools for the Polkadot ecosystem.",
offers: {
"@type": "Offer",
price: "0",
priceCurrency: "USD",
},
author: {
"@type": "Person",
name: "itsyogesh",
url: "https://github.com/itsyogesh",
},
funder: {
"@type": "Organization",
name: "Web3 Foundation",
url: "https://web3.foundation",
},
}),
}}
/>
</head>
<body
className={cn(
"min-h-screen bg-background font-sans antialiased",
Expand Down
15 changes: 15 additions & 0 deletions app/robots.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import type { MetadataRoute } from "next";

export default function robots(): MetadataRoute.Robots {
return {
rules: [
{
userAgent: "*",
allow: "/",
disallow: ["/api/", "/_next/"],
},
],
sitemap: "https://relaycode.org/sitemap.xml",
host: "https://relaycode.org",
};
}
Loading
Loading