diff --git a/projects/TokenizeRWATemplate-frontend/index.html b/projects/TokenizeRWATemplate-frontend/index.html index 5a2ef4f..0f96514 100644 --- a/projects/TokenizeRWATemplate-frontend/index.html +++ b/projects/TokenizeRWATemplate-frontend/index.html @@ -1,9 +1,9 @@ - + - AlgoKit React Template + Tokenization Template
diff --git a/projects/TokenizeRWATemplate-frontend/package-lock.json b/projects/TokenizeRWATemplate-frontend/package-lock.json index 991bd0b..4794a5c 100644 --- a/projects/TokenizeRWATemplate-frontend/package-lock.json +++ b/projects/TokenizeRWATemplate-frontend/package-lock.json @@ -15,6 +15,7 @@ "@txnlab/use-wallet-react": "^4.0.0", "algosdk": "^3.0.0", "daisyui": "^4.0.0", + "lute-connect": "^1.6.3", "notistack": "^3.0.1", "react": "^18.2.0", "react-dom": "^18.2.0", @@ -8785,6 +8786,12 @@ "yallist": "^3.0.2" } }, + "node_modules/lute-connect": { + "version": "1.6.3", + "resolved": "https://registry.npmjs.org/lute-connect/-/lute-connect-1.6.3.tgz", + "integrity": "sha512-QSBHj1fG9QhkgzezcRrG1piYCxTt0Tlf13ZOTLuXsJfagEC+IERKBKORo0CgzBMOlo47nr4w8n19vcFpfCvJNQ==", + "license": "ISC" + }, "node_modules/magic-string": { "version": "0.30.21", "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", diff --git a/projects/TokenizeRWATemplate-frontend/package.json b/projects/TokenizeRWATemplate-frontend/package.json index 589f39b..bebc9f2 100644 --- a/projects/TokenizeRWATemplate-frontend/package.json +++ b/projects/TokenizeRWATemplate-frontend/package.json @@ -42,6 +42,7 @@ "@txnlab/use-wallet-react": "^4.0.0", "algosdk": "^3.0.0", "daisyui": "^4.0.0", + "lute-connect": "^1.6.3", "notistack": "^3.0.1", "react": "^18.2.0", "react-dom": "^18.2.0", diff --git a/projects/TokenizeRWATemplate-frontend/src/App.tsx b/projects/TokenizeRWATemplate-frontend/src/App.tsx index 83c2153..bc0db70 100644 --- a/projects/TokenizeRWATemplate-frontend/src/App.tsx +++ b/projects/TokenizeRWATemplate-frontend/src/App.tsx @@ -1,13 +1,15 @@ import { SupportedWallet, WalletId, WalletManager, WalletProvider } from '@txnlab/use-wallet-react' import { SnackbarProvider } from 'notistack' -import { BrowserRouter, Routes, Route } from 'react-router-dom' +import { BrowserRouter, Route, Routes } from 'react-router-dom' import Home from './Home' import Layout from './Layout' import TokenizePage from './TokenizePage' import { getAlgodConfigFromViteEnvironment, getKmdConfigFromViteEnvironment } from './utils/network/getAlgoClientConfigs' +// Configure supported wallets based on network environment let supportedWallets: SupportedWallet[] if (import.meta.env.VITE_ALGOD_NETWORK === 'localnet') { + // LocalNet: KMD wallet for local development const kmdConfig = getKmdConfigFromViteEnvironment() supportedWallets = [ { @@ -18,11 +20,17 @@ if (import.meta.env.VITE_ALGOD_NETWORK === 'localnet') { port: String(kmdConfig.port), }, }, + { id: WalletId.LUTE }, ] } else { - supportedWallets = [{ id: WalletId.DEFLY }, { id: WalletId.PERA }, { id: WalletId.EXODUS }] + // TestNet/MainNet: Browser extension wallets (Pera, Defly, Exodus, Lute) + supportedWallets = [{ id: WalletId.DEFLY }, { id: WalletId.PERA }, { id: WalletId.EXODUS }, { id: WalletId.LUTE }] } +/** + * Main App Component + * Sets up wallet provider and routing for the Tokenization dApp + */ export default function App() { const algodConfig = getAlgodConfigFromViteEnvironment() diff --git a/projects/TokenizeRWATemplate-frontend/src/Home.tsx b/projects/TokenizeRWATemplate-frontend/src/Home.tsx index 25629a8..d8d84c9 100644 --- a/projects/TokenizeRWATemplate-frontend/src/Home.tsx +++ b/projects/TokenizeRWATemplate-frontend/src/Home.tsx @@ -1,6 +1,11 @@ import { useWallet } from '@txnlab/use-wallet-react' import { Link } from 'react-router-dom' +/** + * Home Page + * Landing page showcasing the RWA tokenization platform + * Displays features, how it works, and CTAs to connect wallet and create assets + */ export default function Home() { const { activeAddress } = useWallet() diff --git a/projects/TokenizeRWATemplate-frontend/src/Layout.tsx b/projects/TokenizeRWATemplate-frontend/src/Layout.tsx index faecc7e..2f1b57f 100644 --- a/projects/TokenizeRWATemplate-frontend/src/Layout.tsx +++ b/projects/TokenizeRWATemplate-frontend/src/Layout.tsx @@ -4,6 +4,10 @@ import { NavLink, Outlet } from 'react-router-dom' import ConnectWallet from './components/ConnectWallet' import ThemeToggle from './components/ThemeToggle' +/** + * Main Layout Component + * Wraps the entire app with navigation, footer, and wallet connection modal + */ export default function Layout() { const [openWalletModal, setOpenWalletModal] = useState(false) const { activeAddress } = useWallet() diff --git a/projects/TokenizeRWATemplate-frontend/src/TokenizePage.tsx b/projects/TokenizeRWATemplate-frontend/src/TokenizePage.tsx index f6b4c80..ee6276c 100644 --- a/projects/TokenizeRWATemplate-frontend/src/TokenizePage.tsx +++ b/projects/TokenizeRWATemplate-frontend/src/TokenizePage.tsx @@ -1,5 +1,9 @@ import TokenizeAsset from './components/TokenizeAsset' +/** + * Tokenize Page + * Main page for creating new Algorand Standard Assets (ASAs) + */ export default function TokenizePage() { return (
diff --git a/projects/TokenizeRWATemplate-frontend/src/components/Account.tsx b/projects/TokenizeRWATemplate-frontend/src/components/Account.tsx index 081ce47..865de04 100644 --- a/projects/TokenizeRWATemplate-frontend/src/components/Account.tsx +++ b/projects/TokenizeRWATemplate-frontend/src/components/Account.tsx @@ -3,6 +3,11 @@ import { useMemo } from 'react' import { ellipseAddress } from '../utils/ellipseAddress' import { getAlgodConfigFromViteEnvironment } from '../utils/network/getAlgoClientConfigs' +/** + * Account Component + * Displays the connected wallet address (shortened) and current network + * Address links to Lora explorer for easy account tracking + */ const Account = () => { const { activeAddress } = useWallet() const algoConfig = getAlgodConfigFromViteEnvironment() @@ -13,10 +18,14 @@ const Account = () => { return (
- + Address: {ellipseAddress(activeAddress)} -
Network: {networkName}
+
Network: {networkName}
) } diff --git a/projects/TokenizeRWATemplate-frontend/src/components/ConnectWallet.tsx b/projects/TokenizeRWATemplate-frontend/src/components/ConnectWallet.tsx index 8ff01dc..4d55d40 100644 --- a/projects/TokenizeRWATemplate-frontend/src/components/ConnectWallet.tsx +++ b/projects/TokenizeRWATemplate-frontend/src/components/ConnectWallet.tsx @@ -1,4 +1,5 @@ import { useWallet, Wallet, WalletId } from '@txnlab/use-wallet-react' +import { useEffect, useRef } from 'react' import Account from './Account' interface ConnectWalletInterface { @@ -6,21 +7,87 @@ interface ConnectWalletInterface { closeModal: () => void } +/** + * ConnectWallet Modal Component + * Displays wallet connection options (Pera, Defly, Lute, KMD for LocalNet) + * Also shows connected wallet details and network information when logged in + */ const ConnectWallet = ({ openModal, closeModal }: ConnectWalletInterface) => { const { wallets, activeAddress } = useWallet() + const dialogRef = useRef(null) + + // Manage native dialog element's open/close state + useEffect(() => { + const dialog = dialogRef.current + if (!dialog) return + + if (openModal) { + dialog.showModal() + } else { + dialog.close() + } + }, [openModal]) + + const getActiveWallet = () => { + if (!wallets) return null + return wallets.find((w) => w.isActive) + } + + const getWalletDisplayName = (wallet: Wallet) => { + if (wallet.id === WalletId.KMD) return 'LocalNet Wallet' + return wallet.metadata.name + } const isKmd = (wallet: Wallet) => wallet.id === WalletId.KMD - return ( - -
-

Select wallet provider

+ const activeWallet = getActiveWallet() -
+ return ( + { + // Close when clicking the backdrop + if (e.target === dialogRef.current) { + closeModal() + } + }} + > +
+
+

Select wallet provider

+ +
+ +

+ Choose the wallet you want to connect. Supported: Pera, Defly, LocalNet (KMD), and others. +

+ +
{activeAddress && ( <> - -
+
+ +
+ + {/* Wallet Info */} +
+ {activeWallet && ( +
+

Connected Wallet

+

{getWalletDisplayName(activeWallet)}

+
+ )} +
+ +
)} @@ -28,47 +95,54 @@ const ConnectWallet = ({ openModal, closeModal }: ConnectWalletInterface) => { wallets?.map((wallet) => ( ))}
-
+
+ {activeAddress && ( )}
- +
) } diff --git a/projects/TokenizeRWATemplate-frontend/src/components/ThemeToggle.tsx b/projects/TokenizeRWATemplate-frontend/src/components/ThemeToggle.tsx index 6644433..439c8bc 100644 --- a/projects/TokenizeRWATemplate-frontend/src/components/ThemeToggle.tsx +++ b/projects/TokenizeRWATemplate-frontend/src/components/ThemeToggle.tsx @@ -3,6 +3,9 @@ import { useEffect, useState } from 'react' const THEME_KEY = 'tokenize_theme' type Theme = 'light' | 'dark' +/** + * Get initial theme preference from localStorage or system preference + */ function getInitialTheme(): Theme { const saved = localStorage.getItem(THEME_KEY) if (saved === 'light' || saved === 'dark') return saved @@ -10,6 +13,11 @@ function getInitialTheme(): Theme { return prefersDark ? 'dark' : 'light' } +/** + * ThemeToggle Component + * Allows users to toggle between light and dark modes + * Persists theme preference to localStorage and applies Tailwind's dark class + */ export default function ThemeToggle() { const [theme, setTheme] = useState(() => getInitialTheme()) const [mounted, setMounted] = useState(false) diff --git a/projects/TokenizeRWATemplate-frontend/src/components/TokenizeAsset.tsx b/projects/TokenizeRWATemplate-frontend/src/components/TokenizeAsset.tsx index d1d3852..21a41dd 100644 --- a/projects/TokenizeRWATemplate-frontend/src/components/TokenizeAsset.tsx +++ b/projects/TokenizeRWATemplate-frontend/src/components/TokenizeAsset.tsx @@ -6,6 +6,10 @@ import { AiOutlineInfoCircle, AiOutlineLoading3Quarters } from 'react-icons/ai' import { BsCoin } from 'react-icons/bs' import { getAlgodConfigFromViteEnvironment } from '../utils/network/getAlgoClientConfigs' +/** + * Type for created assets stored in browser localStorage + * Captures all ASA configuration including compliance fields + */ type CreatedAsset = { assetId: number assetName: string @@ -23,6 +27,9 @@ type CreatedAsset = { const STORAGE_KEY = 'tokenize_assets' const LORA_BASE = 'https://lora.algokit.io/testnet' +/** + * Load created assets from browser localStorage + */ function loadAssets(): CreatedAsset[] { try { const raw = localStorage.getItem(STORAGE_KEY) @@ -32,6 +39,10 @@ function loadAssets(): CreatedAsset[] { } } +/** + * Save a newly created asset to localStorage + * Returns updated asset list with new asset at the top + */ function persistAsset(asset: CreatedAsset): CreatedAsset[] { const existing = loadAssets() const next = [asset, ...existing] @@ -39,6 +50,12 @@ function persistAsset(asset: CreatedAsset): CreatedAsset[] { return next } +/** + * TokenizeAsset Component + * Main form for creating Algorand Standard Assets (ASAs) + * Collects basic and advanced compliance metadata + * Persists created assets to localStorage for tracking + */ export default function TokenizeAsset() { const [assetName, setAssetName] = useState('Tokenized Coffee Membership') const [unitName, setUnitName] = useState('COFFEE') @@ -84,6 +101,10 @@ export default function TokenizeAsset() { const isWholeNumber = (v: string) => /^\d+$/.test(v) + /** + * Handle ASA creation with validation and on-chain transaction + * Adjusts total supply by decimals and saves asset to localStorage + */ const handleTokenize = async () => { if (!transactionSigner || !activeAddress) { enqueueSnackbar('Please connect your wallet first.', { variant: 'warning' })