Skip to content
Draft
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
9 changes: 6 additions & 3 deletions .env.example
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
# Miden Contract Addresses
VITE_MIDEN_ID_CONTRACT_ADDRESS=0x88751c1fec53b640361a89e3570acb
VITE_MIDEN_FAUCET_CONTRACT_ADDRESS=0x54bf4e12ef20082070758b022456c7
VITE_MIDEN_FAUCET_ID_BECH32=mtst1ap2t7nsjausqsgrswk9syfzkcu328yna_qruqqypuyph
VITE_MIDEN_ID_CONTRACT_ADDRESS=0x61b6080c4843bc507be706f6b7c050
VITE_MIDEN_FAUCET_CONTRACT_ADDRESS=0x37d5977a8e16d8205a360820f0230f
VITE_MIDEN_FAUCET_ID_BECH32=mtst1aqmat9m63ctdsgz6xcyzpuprpulwk9vg_qruqqypuyph

# API Configuration
VITE_API_BASE=https://your-backend-api.com

# Dashboard API Configuration (admin dashboard)
VITE_DASHBOARD_API_BASE=http://localhost:3081

# File Upload Configuration (in bytes)
VITE_MAX_FILE_SIZE=5242880
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -337,4 +337,4 @@ For issues and questions:

---

Built with ❤️ for the Miden ecosystem
Built with ❤️ for the Miden ecosystem.
1,741 changes: 879 additions & 862 deletions package-lock.json

Large diffs are not rendered by default.

7 changes: 5 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,12 @@
"prepare": "husky"
},
"dependencies": {
"@demox-labs/miden-sdk": "^0.12.5",
"@demox-labs/miden-wallet-adapter": "^0.10.0",
"@hookform/resolvers": "^5.2.2",
"@miden-sdk/miden-sdk": "^0.13.1",
"@miden-sdk/miden-wallet-adapter": "^0.13.2",
"@miden-sdk/react": "^0.13.2",
"@radix-ui/react-accordion": "^1.2.12",
"@radix-ui/react-alert-dialog": "^1.1.15",
"@radix-ui/react-checkbox": "^1.3.3",
"@radix-ui/react-dialog": "^1.1.15",
"@radix-ui/react-dropdown-menu": "^2.1.16",
Expand All @@ -28,6 +30,7 @@
"@radix-ui/react-separator": "^1.1.7",
"@radix-ui/react-slider": "^1.3.6",
"@radix-ui/react-slot": "^1.2.3",
"@radix-ui/react-switch": "^1.2.6",
"@radix-ui/react-tooltip": "^1.2.8",
"framer-motion": "^12.23.24",
"motion": "^12.23.24",
Expand Down
230 changes: 230 additions & 0 deletions src/api/dashboard.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,230 @@
/**
* Dashboard API service
* Handles session-based authentication and dashboard data fetching
*/

import type {
DashboardLoginRequest,
DashboardLoginResponse,
DashboardLogoutResponse,
DashboardData,
DashboardLimit,
ResetStoreResponse,
TelegramStatusResponse,
TelegramToggleResponse,
ApiResponse,
} from '@/types/api';
import { DASHBOARD_API_BASE, API_BASE } from '@/shared';

/**
* Login to the dashboard
* @param username - Admin username
* @param password - Admin password
* @returns Login response with success status
*/
export async function dashboardLogin(
username: string,
password: string
): Promise<ApiResponse<DashboardLoginResponse>> {
if (!username || !password) {
return {
success: false,
error: 'Username and password are required',
};
}

try {
const response = await fetch(`${DASHBOARD_API_BASE}/api/login`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
credentials: 'include',
body: JSON.stringify({ username, password } as DashboardLoginRequest),
});

const data = await response.json();

if (!response.ok) {
return {
success: false,
error: data.message || `HTTP ${response.status}`,
};
}

return {
success: true,
data: data as DashboardLoginResponse,
};
} catch (error) {
console.error('Dashboard login failed:', error);
return {
success: false,
error: error instanceof Error ? error.message : 'Unknown error',
};
}
}

/**
* Logout from the dashboard
* @returns Logout response
*/
export async function dashboardLogout(): Promise<ApiResponse<DashboardLogoutResponse>> {
try {
const response = await fetch(`${DASHBOARD_API_BASE}/api/logout`, {
method: 'POST',
credentials: 'include',
});

const data = await response.json();

return {
success: response.ok,
data: data as DashboardLogoutResponse,
};
} catch (error) {
console.error('Dashboard logout failed:', error);
return {
success: false,
error: error instanceof Error ? error.message : 'Unknown error',
};
}
}

/**
* Get dashboard data
* @param limit - Number of recent domains to return (10, 25, 50, or 100)
* @returns Dashboard data including blockchain status, stats, and recent domains
*/
export async function getDashboardData(
limit: DashboardLimit = 10
): Promise<ApiResponse<DashboardData>> {
try {
const response = await fetch(
`${DASHBOARD_API_BASE}/api/dashboard-data?limit=${limit}`,
{
credentials: 'include',
}
);

if (response.status === 401) {
const errorData = await response.json();
return {
success: false,
error: errorData.error || 'Not authenticated',
};
}

if (!response.ok) {
const errorText = await response.text();
return {
success: false,
error: `HTTP ${response.status}: ${errorText}`,
};
}

const data: DashboardData = await response.json();
return {
success: true,
data,
};
} catch (error) {
console.error('Failed to get dashboard data:', error);
return {
success: false,
error: error instanceof Error ? error.message : 'Unknown error',
};
}
}

/**
* Reset the Miden store
* @returns Reset store response
*/
export async function resetStore(): Promise<ApiResponse<ResetStoreResponse>> {
try {
const response = await fetch(`${API_BASE}/admin/reset-store`, {
method: 'POST',
});

const data: ResetStoreResponse = await response.json();

if (!response.ok) {
return {
success: false,
error: data.message || `HTTP ${response.status}`,
};
}

return {
success: true,
data,
};
} catch (error) {
console.error('Failed to reset store:', error);
return {
success: false,
error: error instanceof Error ? error.message : 'Unknown error',
};
}
}

/**
* Get Telegram notifications status
* @returns Telegram status response
*/
export async function getTelegramStatus(): Promise<ApiResponse<TelegramStatusResponse>> {
try {
const response = await fetch(`${API_BASE}/admin/telegram-notifications/status`);

const data: TelegramStatusResponse = await response.json();

return {
success: response.ok,
data,
};
} catch (error) {
console.error('Failed to get Telegram status:', error);
return {
success: false,
error: error instanceof Error ? error.message : 'Unknown error',
};
}
}

/**
* Toggle Telegram notifications
* @param enable - Whether to enable or disable notifications
* @returns Toggle response
*/
export async function toggleTelegramNotifications(
enable: boolean
): Promise<ApiResponse<TelegramToggleResponse>> {
try {
const endpoint = enable ? 'enable' : 'disable';
const response = await fetch(
`${API_BASE}/admin/telegram-notifications/${endpoint}`,
{ method: 'POST' }
);

const data: TelegramToggleResponse = await response.json();

if (!response.ok) {
return {
success: false,
error: data.message || `HTTP ${response.status}`,
};
}

return {
success: true,
data,
};
} catch (error) {
console.error('Failed to toggle Telegram notifications:', error);
return {
success: false,
error: error instanceof Error ? error.message : 'Unknown error',
};
}
}
2 changes: 1 addition & 1 deletion src/components/MobileSidebar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import {
SheetContent,
SheetTrigger,
} from '@/components/ui/sheet'
import { WalletMultiButton } from '@demox-labs/miden-wallet-adapter-reactui'
import { WalletMultiButton } from '@miden-sdk/miden-wallet-adapter'
import { Separator } from '@/components/ui/separator'
import { useTheme } from './ThemeProvider'
import ThemeToggle from './ThemeToggle'
Expand Down
22 changes: 17 additions & 5 deletions src/components/RegisterModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,12 @@ import {
} from "@/components/ui/shadcn-io/animated-modal";
import { type ReactNode, useState, useEffect } from "react";
import { cloneElement, isValidElement } from "react";
import { useWallet } from "@demox-labs/miden-wallet-adapter";
import { useWallet } from "@miden-sdk/miden-wallet-adapter";
import {
MIDEN_FAUCET_CONTRACT_ADDRESS,
MIDEN_FAUCET_ID_BECH32,
MIDEN_ID_CONTRACT_ADDRESS,
} from "@/shared/constants";
import { AccountId, Felt } from "@demox-labs/miden-sdk";
import { AccountId, Felt } from "@miden-sdk/miden-sdk";
import { useToast } from "@/hooks/useToast";
import { ToastCause } from "@/types/toast";
import { TermsModal } from "@/components/TermsModal";
Expand All @@ -22,7 +22,7 @@ import { ConfirmedStep } from "./register-modal/ConfirmedStep";
import { transactionCreator } from "@/lib/transactionCreator";
import { REGISTER_NOTE_SCRIPT, MIDEN_NAME_CONTRACT_CODE } from "@/shared";
import { encodeDomain } from "@/utils/encode";
import { NoteInputs, MidenArrays } from "@demox-labs/miden-sdk";
import { NoteInputs, MidenArrays } from "@miden-sdk/miden-sdk";
import { getDomainPrice } from "@/shared/pricing";
import { bech32ToAccountId, instantiateClient } from "@/lib/midenClient";
import { executeStep } from "@/utils/errorHandler";
Expand Down Expand Up @@ -55,18 +55,26 @@ function RegisterModalContent({
}) {
const domainPrice = getDomainPrice(domain.length);
const { connected, requestTransaction, address } = useWallet();
const { open } = useModal();
const showToast = useToast();
const [currentStep, setCurrentStep] = useState<ModalStep>("registration");
const [isPurchasing, setIsPurchasing] = useState(false);
const [noteId, setNoteId] = useState<string | null>(null);
const [termsOpen, setTermsOpen] = useState(false);

const faucetId = AccountId.fromHex(MIDEN_FAUCET_CONTRACT_ADDRESS as string)
const faucetId = AccountId.fromBech32(MIDEN_FAUCET_ID_BECH32 as string);

const accountId = address ? bech32ToAccountId(address) : null

const destinationAccountId = AccountId.fromHex(MIDEN_ID_CONTRACT_ADDRESS as string)

// Reset when modal closes
useEffect(() => {
if (!open) {
setCurrentStep("registration");
}
}, [open]);

// Reset when domain changes
useEffect(() => {
setCurrentStep("registration");
Expand Down Expand Up @@ -97,6 +105,10 @@ function RegisterModalContent({
() => encodeDomain(domain)
);

console.log("buyAmount:", buyAmount.toString());

console.log("faucetId:", faucetId.toString());

const noteInputs = await executeStep(
ErrorCodes.NOTE_INPUTS_CREATION_FAILED,
'Note inputs creation',
Expand Down
2 changes: 1 addition & 1 deletion src/components/SiteHeader.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { Menubar } from '@/components/ui/menubar'
import { Separator } from '@/components/ui/separator'
import { Link } from 'react-router'
import { MobileSidebar } from './MobileSidebar'
import { WalletMultiButton } from '@demox-labs/miden-wallet-adapter-reactui'
import { WalletMultiButton } from '@miden-sdk/miden-wallet-adapter'
import {
DropdownMenu,
DropdownMenuContent,
Expand Down
3 changes: 3 additions & 0 deletions src/components/TestnetWarningModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,9 @@ export function TestnetWarningModal() {
<div className="font-semibold">
Important: Names registered on the testnet may be wiped out during maintenance or new deployments. Registration on testnet does not guarantee name availability or ownership on mainnet.
</div>
<div className="font-semibold text-red-600">
Miden name is transitioning from testnet version v0.12 to v0.13. During this transition application can behave unpredictably.
</div>
</div>
</DialogDescription>
</DialogHeader>
Expand Down
4 changes: 2 additions & 2 deletions src/components/register-modal/RegistrationStep.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { useState } from "react";
import { Button } from "@/components/ui/button";
import { useWallet, WalletMultiButton } from "@demox-labs/miden-wallet-adapter";
import { useWallet, WalletMultiButton } from "@miden-sdk/miden-wallet-adapter";
import { TOKEN_SYMBOL, getDomainPrice } from "@/shared/pricing";
import type { AccountId } from "@demox-labs/miden-sdk";
import type { AccountId } from "@miden-sdk/miden-sdk";
import { MIDEN_FAUCET_ID_BECH32 } from "@/shared";
import { ErrorCodes } from "@/types/errors";
import { executeStep } from "@/utils/errorHandler";
Expand Down
Loading