Skip to content
Open
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
431 changes: 431 additions & 0 deletions docs/bug-bounty-program.md

Large diffs are not rendered by default.

3 changes: 3 additions & 0 deletions frontend/.eslintrc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"extends": "next/core-web-vitals"
}
156 changes: 156 additions & 0 deletions frontend/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
# PrivacyLayer Frontend

Next.js dApp for PrivacyLayer - the first ZK-proof shielded pool on Stellar Soroban.

## Features

- 🔐 Freighter wallet integration
- 🎨 Tailwind CSS styling
- 📦 Zustand state management
- 🔧 TypeScript support

## Getting Started

### Prerequisites

- Node.js 18+
- npm or yarn or pnpm
- [Freighter Wallet](https://www.freighter.app/) browser extension

### Installation

```bash
# Install dependencies
npm install

# Run development server
npm run dev
```

Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.

## Project Structure

```
frontend/
├── app/ # Next.js App Router pages
│ ├── layout.tsx # Root layout
│ ├── page.tsx # Home page
│ └── globals.css # Global styles
├── components/ # React components
│ └── wallet/ # Wallet-related components
│ ├── ConnectButton.tsx # Connect/disconnect button
│ ├── WalletInfo.tsx # Display connected wallet info
│ ├── NetworkSelector.tsx # Network switcher
│ └── InstallPrompt.tsx # Prompt to install Freighter
├── lib/ # Utility libraries
│ ├── wallet.ts # Freighter wallet functions
│ └── store.ts # Zustand state management
├── package.json
├── tsconfig.json
├── tailwind.config.ts
└── next.config.js
```

## Wallet Integration

### Using Wallet Functions

```typescript
import {
connectWallet,
disconnectWallet,
signTransactionWithWallet
} from '@/lib/wallet';

// Connect wallet
const result = await connectWallet();
if (result.error) {
console.error(result.error);
} else {
console.log('Connected:', result.publicKey);
}

// Sign a transaction
const { signedTxXdr, error } = await signTransactionWithWallet(transactionXdr);
```

### Using Wallet Store

```typescript
import { useWalletStore, useWalletPublicKey } from '@/lib/store';

function MyComponent() {
const publicKey = useWalletPublicKey();
const connect = useWalletStore((state) => state.connect);
const disconnect = useWalletStore((state) => state.disconnect);

return (
<div>
{publicKey ? (
<button onClick={disconnect}>Disconnect</button>
) : (
<button onClick={connect}>Connect</button>
)}
</div>
);
}
```

## Components

### ConnectButton

A button that handles wallet connection/disconnection.

```tsx
import ConnectButton from '@/components/wallet/ConnectButton';

<ConnectButton />
```

### WalletInfo

Displays connected wallet information.

```tsx
import WalletInfo from '@/components/wallet/WalletInfo';

<WalletInfo />
```

### NetworkSelector

Network selection dropdown (info only, actual network change must be done in Freighter).

```tsx
import NetworkSelector from '@/components/wallet/NetworkSelector';

<NetworkSelector />
```

### InstallPrompt

Prompts users to install Freighter if not detected.

```tsx
import InstallPrompt from '@/components/wallet/InstallPrompt';

<InstallPrompt />
```

## Environment Variables

Create a `.env.local` file for environment-specific configuration:

```env
NEXT_PUBLIC_NETWORK=TESTNET
NEXT_PUBLIC_CONTRACT_ID=your_contract_id
```

## Learn More

- [Next.js Documentation](https://nextjs.org/docs)
- [Freighter API Documentation](https://github.com/stellar/freighter)
- [Stellar SDK Documentation](https://developers.stellar.org/docs)
- [Zustand Documentation](https://github.com/pmndrs/zustand)
27 changes: 27 additions & 0 deletions frontend/app/globals.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
@tailwind base;
@tailwind components;
@tailwind utilities;

:root {
--foreground-rgb: 0, 0, 0;
--background-start-rgb: 214, 219, 220;
--background-end-rgb: 255, 255, 255;
}

@media (prefers-color-scheme: dark) {
:root {
--foreground-rgb: 255, 255, 255;
--background-start-rgb: 0, 0, 0;
--background-end-rgb: 0, 0, 0;
}
}

body {
color: rgb(var(--foreground-rgb));
background: linear-gradient(
to bottom,
transparent,
rgb(var(--background-end-rgb))
)
rgb(var(--background-start-rgb));
}
22 changes: 22 additions & 0 deletions frontend/app/layout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import type { Metadata } from 'next'
import { Inter } from 'next/font/google'
import './globals.css'

const inter = Inter({ subsets: ['latin'] })

export const metadata: Metadata = {
title: 'PrivacyLayer - ZK Shielded Pool on Stellar',
description: 'The first ZK-proof shielded pool on Stellar Soroban',
}

export default function RootLayout({
children,
}: {
children: React.ReactNode
}) {
return (
<html lang="en">
<body className={inter.className}>{children}</body>
</html>
)
}
22 changes: 22 additions & 0 deletions frontend/app/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import ConnectButton from '@/components/wallet/ConnectButton'
import WalletInfo from '@/components/wallet/WalletInfo'

export default function Home() {
return (
<main className="flex min-h-screen flex-col items-center justify-center p-24">
<div className="z-10 max-w-5xl w-full items-center justify-center font-mono text-sm">
<h1 className="text-4xl font-bold text-center mb-8">
🔐 PrivacyLayer
</h1>
<p className="text-center text-gray-500 mb-8">
The first ZK-proof shielded pool on Stellar Soroban
</p>

<div className="flex flex-col items-center gap-4">
<ConnectButton />
<WalletInfo />
</div>
</div>
</main>
)
}
67 changes: 67 additions & 0 deletions frontend/components/wallet/ConnectButton.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
'use client';

import { useWalletStore, useWalletLoading, useWalletConnected, useWalletError } from '@/lib/store';

export default function ConnectButton() {
const connect = useWalletStore((state) => state.connect);
const disconnect = useWalletStore((state) => state.disconnect);
const isLoading = useWalletLoading();
const isConnected = useWalletConnected();
const error = useWalletError();

const handleClick = async () => {
if (isConnected) {
disconnect();
} else {
await connect();
}
};

return (
<div className="flex flex-col items-center gap-2">
<button
onClick={handleClick}
disabled={isLoading}
className={`
px-6 py-3 rounded-lg font-medium text-white
transition-all duration-200 ease-in-out
${isConnected
? 'bg-red-500 hover:bg-red-600'
: 'bg-primary-600 hover:bg-primary-700'
}
${isLoading ? 'opacity-50 cursor-not-allowed' : 'cursor-pointer'}
`}
>
{isLoading ? (
<span className="flex items-center gap-2">
<svg className="animate-spin h-5 w-5" viewBox="0 0 24 24">
<circle
className="opacity-25"
cx="12" cy="12" r="10"
stroke="currentColor"
strokeWidth="4"
fill="none"
/>
<path
className="opacity-75"
fill="currentColor"
d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4z"
/>
</svg>
Connecting...
</span>
) : isConnected ? (
'Disconnect Wallet'
) : (
'Connect Freighter Wallet'
)}
</button>

{error && (
<p className="text-red-500 text-sm mt-2 max-w-md text-center">
{error}
</p>
)}
</div>
);
}
23 changes: 23 additions & 0 deletions frontend/components/wallet/InstallPrompt.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
'use client';

export default function InstallPrompt() {
return (
<div className="bg-yellow-900/50 border border-yellow-600 rounded-lg p-6 max-w-md text-center">
<h3 className="text-lg font-semibold text-yellow-400 mb-2">
⚠️ Freighter Not Detected
</h3>
<p className="text-gray-300 mb-4">
Freighter wallet is required to interact with PrivacyLayer.
Please install it to continue.
</p>
<a
href="https://www.freighter.app/"
target="_blank"
rel="noopener noreferrer"
className="inline-block bg-yellow-600 hover:bg-yellow-700 text-white font-medium px-6 py-2 rounded-lg transition-colors"
>
Install Freighter
</a>
</div>
);
}
36 changes: 36 additions & 0 deletions frontend/components/wallet/NetworkSelector.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
'use client';

import { useWalletNetwork } from '@/lib/store';

interface NetworkSelectorProps {
onNetworkChange?: (network: string) => void;
}

const NETWORKS = [
{ id: 'TESTNET', name: 'Testnet', passphrase: 'Test SDF Network ; September 2015' },
{ id: 'PUBLIC', name: 'Mainnet', passphrase: 'Public Global Stellar Network ; September 2015' },
];

export default function NetworkSelector({ onNetworkChange }: NetworkSelectorProps) {
const currentNetwork = useWalletNetwork();

return (
<div className="flex items-center gap-2">
<span className="text-gray-400 text-sm">Network:</span>
<select
value={currentNetwork || 'TESTNET'}
onChange={(e) => onNetworkChange?.(e.target.value)}
className="bg-gray-800 text-white text-sm rounded px-3 py-1.5 border border-gray-700 focus:border-primary-500 focus:outline-none"
>
{NETWORKS.map((network) => (
<option key={network.id} value={network.id}>
{network.name}
</option>
))}
</select>
<span className="text-xs text-gray-500">
(Change in Freighter)
</span>
</div>
);
}
Loading