diff --git a/backend/integration-tests/presale.live.test.js b/backend/integration-tests/presale.live.test.js index a7df920e..780aa26e 100644 --- a/backend/integration-tests/presale.live.test.js +++ b/backend/integration-tests/presale.live.test.js @@ -30,7 +30,7 @@ let sudo, alice, bob; // Asset IDs (assumed from mock.rs) const PEZ_ASSET_ID = 1; -const WUSDT_ASSET_ID = 2; // Assuming wUSDT has 6 decimals +const WUSDT_ASSET_ID = 1000; // wUSDT has 6 decimals (matches runtime WUSDT_ASSET_ID) // Helper to wait for N finalized blocks const waitForBlocks = async (count) => { diff --git a/shared/constants/index.ts b/shared/constants/index.ts index da8d687a..23943c48 100644 --- a/shared/constants/index.ts +++ b/shared/constants/index.ts @@ -55,21 +55,21 @@ export const KNOWN_TOKENS: Record = { symbol: 'wHEZ', name: 'Wrapped HEZ', decimals: 12, - logo: '🟡', + logo: '/shared/images/hez_logo.png', }, 1: { id: 1, symbol: 'PEZ', name: 'Pezkuwi Token', decimals: 12, - logo: '🟣', + logo: '/shared/images/pez_logo.jpg', }, - 2: { - id: 2, + 1000: { + id: 1000, symbol: 'wUSDT', name: 'Wrapped USDT', decimals: 6, - logo: '💵', + logo: '/shared/images/USDT(hez)logo.png', }, }; diff --git a/shared/images/ADAlogo.png b/shared/images/ADAlogo.png new file mode 100644 index 00000000..b6374207 Binary files /dev/null and b/shared/images/ADAlogo.png differ diff --git a/shared/images/BNB_logo.png b/shared/images/BNB_logo.png new file mode 100644 index 00000000..d10ef0a8 Binary files /dev/null and b/shared/images/BNB_logo.png differ diff --git a/shared/images/USDT(hez)logo.png b/shared/images/USDT(hez)logo.png new file mode 100644 index 00000000..ad5aa405 Binary files /dev/null and b/shared/images/USDT(hez)logo.png differ diff --git a/shared/images/bitcoin.png b/shared/images/bitcoin.png new file mode 100644 index 00000000..51cd6040 Binary files /dev/null and b/shared/images/bitcoin.png differ diff --git a/shared/images/dot.png b/shared/images/dot.png new file mode 100644 index 00000000..1049bb0d Binary files /dev/null and b/shared/images/dot.png differ diff --git a/shared/images/etherium.png b/shared/images/etherium.png new file mode 100644 index 00000000..cc0a7d39 Binary files /dev/null and b/shared/images/etherium.png differ diff --git a/shared/images/hez_logo.png b/shared/images/hez_logo.png new file mode 100644 index 00000000..43aa15a7 Binary files /dev/null and b/shared/images/hez_logo.png differ diff --git a/shared/images/pez_logo.jpg b/shared/images/pez_logo.jpg new file mode 100644 index 00000000..30788d28 Binary files /dev/null and b/shared/images/pez_logo.jpg differ diff --git a/shared/utils/auth.ts b/shared/utils/auth.ts index ea8b133b..f59792df 100644 --- a/shared/utils/auth.ts +++ b/shared/utils/auth.ts @@ -3,18 +3,29 @@ * Security-critical: Founder wallet detection and permissions */ -// SECURITY: Founder wallet address for beta testnet -// This address has sudo rights and can perform privileged operations -export const FOUNDER_ADDRESS = '5GgTgG9sRmPQAYU1RsTejZYnZRjwzKZKWD3awtuqjHioki45'; +// DEPRECATED: Hardcoded founder address (kept for fallback only) +// Modern approach: Fetch sudo key dynamically from blockchain +export const FOUNDER_ADDRESS_FALLBACK = '5GgTgG9sRmPQAYU1RsTejZYnZRjwzKZKWD3awtuqjHioki45'; /** - * Check if given address is the founder wallet + * Check if given address is the sudo account (admin/founder) * @param address - Substrate address to check - * @returns true if address matches founder, false otherwise + * @param sudoKey - Sudo key fetched from blockchain (if available) + * @returns true if address matches sudo key or fallback founder address */ -export const isFounderWallet = (address: string | null | undefined): boolean => { +export const isFounderWallet = ( + address: string | null | undefined, + sudoKey?: string | null +): boolean => { if (!address) return false; - return address === FOUNDER_ADDRESS; + + // Priority 1: Use dynamic sudo key from blockchain if available + if (sudoKey && sudoKey !== '') { + return address === sudoKey; + } + + // Priority 2: Fallback to hardcoded founder address (for compatibility) + return address === FOUNDER_ADDRESS_FALLBACK; }; /** @@ -48,11 +59,13 @@ export enum DexPermission { * Check if user has permission for a specific DEX operation * @param address - User's wallet address * @param permission - Required permission level + * @param sudoKey - Sudo key fetched from blockchain (if available) * @returns true if user has permission */ export const hasPermission = ( address: string | null | undefined, - permission: DexPermission + permission: DexPermission, + sudoKey?: string | null ): boolean => { if (!address || !isValidSubstrateAddress(address)) { return false; @@ -67,7 +80,7 @@ export const hasPermission = ( // Founder-only operations if (founderOnly.includes(permission)) { - return isFounderWallet(address); + return isFounderWallet(address, sudoKey); } // Everyone can view and trade @@ -77,10 +90,14 @@ export const hasPermission = ( /** * Get user role string for display * @param address - User's wallet address + * @param sudoKey - Sudo key fetched from blockchain (if available) * @returns Human-readable role */ -export const getUserRole = (address: string | null | undefined): string => { +export const getUserRole = ( + address: string | null | undefined, + sudoKey?: string | null +): string => { if (!address) return 'Guest'; - if (isFounderWallet(address)) return 'Founder (Admin)'; + if (isFounderWallet(address, sudoKey)) return 'Sudo (Admin)'; return 'User'; }; diff --git a/web/src/components/AddLiquidityModal.tsx b/web/src/components/AddLiquidityModal.tsx index f621e018..6f9db54c 100644 --- a/web/src/components/AddLiquidityModal.tsx +++ b/web/src/components/AddLiquidityModal.tsx @@ -30,7 +30,7 @@ interface Balances { const getDisplayName = (assetId: number): string => { if (assetId === ASSET_IDS.WHEZ || assetId === 0) return 'HEZ'; if (assetId === ASSET_IDS.PEZ || assetId === 1) return 'PEZ'; - if (assetId === ASSET_IDS.WUSDT || assetId === 2) return 'USDT'; + if (assetId === ASSET_IDS.WUSDT || assetId === 1000) return 'USDT'; return getAssetSymbol(assetId); }; @@ -38,13 +38,13 @@ const getDisplayName = (assetId: number): string => { const getBalanceKey = (assetId: number): string => { if (assetId === ASSET_IDS.WHEZ || assetId === 0) return 'HEZ'; if (assetId === ASSET_IDS.PEZ || assetId === 1) return 'PEZ'; - if (assetId === ASSET_IDS.WUSDT || assetId === 2) return 'USDT'; + if (assetId === ASSET_IDS.WUSDT || assetId === 1000) return 'USDT'; return getAssetSymbol(assetId); }; // Helper to get decimals for asset const getAssetDecimals = (assetId: number): number => { - if (assetId === ASSET_IDS.WUSDT || assetId === 2) return 6; // wUSDT has 6 decimals + if (assetId === ASSET_IDS.WUSDT || assetId === 1000) return 6; // wUSDT has 6 decimals return 12; // wHEZ, PEZ have 12 decimals }; diff --git a/web/src/components/PoolDashboard.tsx b/web/src/components/PoolDashboard.tsx index 6f38f2ff..75ee0be1 100644 --- a/web/src/components/PoolDashboard.tsx +++ b/web/src/components/PoolDashboard.tsx @@ -16,7 +16,7 @@ import { RemoveLiquidityModal } from '@/components/RemoveLiquidityModal'; const getDisplayTokenName = (assetId: number): string => { if (assetId === ASSET_IDS.WHEZ || assetId === 0) return 'HEZ'; if (assetId === ASSET_IDS.PEZ || assetId === 1) return 'PEZ'; - if (assetId === ASSET_IDS.WUSDT || assetId === 2) return 'USDT'; + if (assetId === ASSET_IDS.WUSDT || assetId === 1000) return 'USDT'; return getAssetSymbol(assetId); // Fallback for other assets }; diff --git a/web/src/components/RemoveLiquidityModal.tsx b/web/src/components/RemoveLiquidityModal.tsx index 9fdffb44..d7833f8b 100644 --- a/web/src/components/RemoveLiquidityModal.tsx +++ b/web/src/components/RemoveLiquidityModal.tsx @@ -11,7 +11,7 @@ import { ASSET_IDS, getAssetSymbol } from '@pezkuwi/lib/wallet'; const getDisplayTokenName = (assetId: number): string => { if (assetId === ASSET_IDS.WHEZ || assetId === 0) return 'HEZ'; if (assetId === ASSET_IDS.PEZ || assetId === 1) return 'PEZ'; - if (assetId === ASSET_IDS.WUSDT || assetId === 2) return 'USDT'; + if (assetId === ASSET_IDS.WUSDT || assetId === 1000) return 'USDT'; return getAssetSymbol(assetId); }; diff --git a/web/src/components/TokenSwap.tsx b/web/src/components/TokenSwap.tsx index 66079433..ff9c735c 100644 --- a/web/src/components/TokenSwap.tsx +++ b/web/src/components/TokenSwap.tsx @@ -18,7 +18,7 @@ import { PriceChart } from './trading/PriceChart'; const AVAILABLE_TOKENS = [ { symbol: 'HEZ', emoji: '🟡', assetId: 0, name: 'HEZ', badge: true, displaySymbol: 'HEZ' }, { symbol: 'PEZ', emoji: '🟣', assetId: 1, name: 'PEZ', badge: true, displaySymbol: 'PEZ' }, - { symbol: 'USDT', emoji: '💵', assetId: 2, name: 'USDT', badge: true, displaySymbol: 'USDT' }, + { symbol: 'USDT', emoji: '💵', assetId: 1000, name: 'USDT', badge: true, displaySymbol: 'USDT' }, ] as const; const TokenSwap = () => { @@ -182,7 +182,7 @@ const TokenSwap = () => { const getPoolAssetId = (token: string) => { if (token === 'HEZ') return 0; // wHEZ if (token === 'PEZ') return 1; - if (token === 'USDT') return 2; + if (token === 'USDT') return 1000; return ASSET_IDS[token as keyof typeof ASSET_IDS]; }; @@ -271,9 +271,9 @@ const TokenSwap = () => { // Use correct decimals for each asset // asset1=0 (wHEZ): 12 decimals // asset1=1 (PEZ): 12 decimals - // asset2=2 (wUSDT): 6 decimals - const decimals0 = asset1 === 2 ? 6 : 12; // asset1 is the smaller ID - const decimals1 = asset2 === 2 ? 6 : 12; // asset2 is the larger ID + // asset2=1000 (wUSDT): 6 decimals + const decimals0 = asset1 === 1000 ? 6 : 12; // asset1 is the smaller ID + const decimals1 = asset2 === 1000 ? 6 : 12; // asset2 is the larger ID const reserve0 = Number(BigInt(balance0Hex)) / (10 ** decimals0); const reserve1 = Number(BigInt(balance1Hex)) / (10 ** decimals1); @@ -581,7 +581,7 @@ const TokenSwap = () => { // HEZ → Any Asset: wrap(HEZ→wHEZ) then swap(wHEZ→Asset) const wrapTx = api.tx.tokenWrapper.wrap(amountIn.toString()); // Map token symbol to asset ID - const toAssetId = toToken === 'PEZ' ? 1 : toToken === 'USDT' ? 2 : ASSET_IDS[toToken as keyof typeof ASSET_IDS]; + const toAssetId = toToken === 'PEZ' ? 1 : toToken === 'USDT' ? 1000 : ASSET_IDS[toToken as keyof typeof ASSET_IDS]; const swapPath = [0, toAssetId]; // wHEZ → target asset const swapTx = api.tx.assetConversion.swapExactTokensForTokens( swapPath, @@ -595,7 +595,7 @@ const TokenSwap = () => { } else if (toToken === 'HEZ') { // Any Asset → HEZ: swap(Asset→wHEZ) then unwrap(wHEZ→HEZ) // Map token symbol to asset ID - const fromAssetId = fromToken === 'PEZ' ? 1 : fromToken === 'USDT' ? 2 : ASSET_IDS[fromToken as keyof typeof ASSET_IDS]; + const fromAssetId = fromToken === 'PEZ' ? 1 : fromToken === 'USDT' ? 1000 : ASSET_IDS[fromToken as keyof typeof ASSET_IDS]; const swapPath = [fromAssetId, 0]; // source asset → wHEZ const swapTx = api.tx.assetConversion.swapExactTokensForTokens( swapPath, @@ -610,8 +610,8 @@ const TokenSwap = () => { } else { // Direct swap between assets (PEZ ↔ USDT, etc.) // Map token symbols to asset IDs - const fromAssetId = fromToken === 'PEZ' ? 1 : fromToken === 'USDT' ? 2 : ASSET_IDS[fromToken as keyof typeof ASSET_IDS]; - const toAssetId = toToken === 'PEZ' ? 1 : toToken === 'USDT' ? 2 : ASSET_IDS[toToken as keyof typeof ASSET_IDS]; + const fromAssetId = fromToken === 'PEZ' ? 1 : fromToken === 'USDT' ? 1000 : ASSET_IDS[fromToken as keyof typeof ASSET_IDS]; + const toAssetId = toToken === 'PEZ' ? 1 : toToken === 'USDT' ? 1000 : ASSET_IDS[toToken as keyof typeof ASSET_IDS]; const swapPath = [fromAssetId, toAssetId]; tx = api.tx.assetConversion.swapExactTokensForTokens( diff --git a/web/src/components/TransferModal.tsx b/web/src/components/TransferModal.tsx index bd3c369e..62054aeb 100644 --- a/web/src/components/TransferModal.tsx +++ b/web/src/components/TransferModal.tsx @@ -48,7 +48,7 @@ interface Token { const TOKENS: Token[] = [ { symbol: 'HEZ', name: 'Hez Token', decimals: 12, color: 'from-green-600 to-yellow-400' }, { symbol: 'PEZ', name: 'Pez Token', assetId: 1, decimals: 12, color: 'from-blue-600 to-purple-400' }, - { symbol: 'USDT', name: 'Tether USD', assetId: 2, decimals: 6, color: 'from-green-500 to-green-600' }, + { symbol: 'USDT', name: 'Tether USD', assetId: 1000, decimals: 6, color: 'from-green-500 to-green-600' }, { symbol: 'BTC', name: 'Bitcoin', assetId: 3, decimals: 8, color: 'from-orange-500 to-yellow-500' }, { symbol: 'ETH', name: 'Ethereum', assetId: 4, decimals: 18, color: 'from-purple-500 to-blue-500' }, { symbol: 'DOT', name: 'Polkadot', assetId: 5, decimals: 10, color: 'from-pink-500 to-red-500' }, @@ -72,7 +72,7 @@ export const TransferModal: React.FC = ({ isOpen, onClose, s assetId: selectedAsset.assetId, decimals: selectedAsset.decimals, color: selectedAsset.assetId === 0 ? 'from-green-600 to-yellow-400' : - selectedAsset.assetId === 2 ? 'from-emerald-500 to-teal-500' : + selectedAsset.assetId === 1000 ? 'from-emerald-500 to-teal-500' : 'from-cyan-500 to-blue-500', } : TOKENS.find(t => t.symbol === selectedToken) || TOKENS[0]; diff --git a/web/src/components/dex/DEXDashboard.tsx b/web/src/components/dex/DEXDashboard.tsx index 4331e1fa..7c52d778 100644 --- a/web/src/components/dex/DEXDashboard.tsx +++ b/web/src/components/dex/DEXDashboard.tsx @@ -1,23 +1,27 @@ import React, { useState } from 'react'; // import { useNavigate } from 'react-router-dom'; import { useWallet } from '@/contexts/WalletContext'; +import { usePolkadot } from '@/contexts/PolkadotContext'; import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs'; import TokenSwap from '@/components/TokenSwap'; import PoolDashboard from '@/components/PoolDashboard'; import { CreatePoolModal } from './CreatePoolModal'; import { InitializeHezPoolModal } from './InitializeHezPoolModal'; +import { InitializeUsdtModal } from './InitializeUsdtModal'; import { ArrowRightLeft, Droplet, Settings } from 'lucide-react'; import { isFounderWallet } from '@pezkuwi/utils/auth'; export const DEXDashboard: React.FC = () => { const { account } = useWallet(); + const { sudoKey } = usePolkadot(); const [activeTab, setActiveTab] = useState('swap'); // Admin modal states const [showCreatePoolModal, setShowCreatePoolModal] = useState(false); const [showInitializeHezPoolModal, setShowInitializeHezPoolModal] = useState(false); + const [showInitializeUsdtModal, setShowInitializeUsdtModal] = useState(false); - const isFounder = account ? isFounderWallet(account) : false; + const isFounder = account ? isFounderWallet(account, sudoKey) : false; const handleCreatePool = () => { setShowCreatePoolModal(true); @@ -26,6 +30,7 @@ export const DEXDashboard: React.FC = () => { const handleModalClose = () => { setShowCreatePoolModal(false); setShowInitializeHezPoolModal(false); + setShowInitializeUsdtModal(false); }; const handleSuccess = async () => { @@ -116,6 +121,19 @@ export const DEXDashboard: React.FC = () => { +
+

USDT Token Minting

+

+ Mint wUSDT tokens for testing and liquidity provision +

+ +
+

Pool Management

@@ -154,6 +172,12 @@ export const DEXDashboard: React.FC = () => { onClose={handleModalClose} onSuccess={handleSuccess} /> + +

); }; diff --git a/web/src/components/dex/InitializeUsdtModal.tsx b/web/src/components/dex/InitializeUsdtModal.tsx new file mode 100644 index 00000000..953addb9 --- /dev/null +++ b/web/src/components/dex/InitializeUsdtModal.tsx @@ -0,0 +1,300 @@ +import React, { useState, useEffect } from 'react'; +import { usePolkadot } from '@/contexts/PolkadotContext'; +import { useWallet } from '@/contexts/WalletContext'; +import { X, AlertCircle, Loader2, CheckCircle, Info } from 'lucide-react'; +import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'; +import { Badge } from '@/components/ui/badge'; +import { Button } from '@/components/ui/button'; +import { Input } from '@/components/ui/input'; +import { Alert, AlertDescription } from '@/components/ui/alert'; +import { useToast } from '@/hooks/use-toast'; +import { ASSET_IDS, ASSET_CONFIGS } from '../../../shared/lib/wallet'; + +interface InitializeUsdtModalProps { + isOpen: boolean; + onClose: () => void; + onSuccess?: () => void; +} + +type TransactionStatus = 'idle' | 'signing' | 'submitting' | 'success' | 'error'; + +const USDT_ASSET_ID = 1000; // wUSDT asset ID (matches runtime WUSDT_ASSET_ID) +const USDT_DECIMALS = 6; // USDT standard decimals + +export const InitializeUsdtModal: React.FC = ({ + isOpen, + onClose, + onSuccess, +}) => { + const { api, isApiReady } = usePolkadot(); + const { account, signer } = useWallet(); + const { toast } = useToast(); + + const [usdtAmount, setUsdtAmount] = useState('10000'); + + const [wusdtBalance, setWusdtBalance] = useState('0'); + + const [txStatus, setTxStatus] = useState('idle'); + const [errorMessage, setErrorMessage] = useState(''); + + // Reset form when modal closes + useEffect(() => { + if (!isOpen) { + setUsdtAmount('10000'); + setTxStatus('idle'); + setErrorMessage(''); + } + }, [isOpen]); + + // Fetch wUSDT balance + useEffect(() => { + const fetchBalance = async () => { + if (!api || !isApiReady || !account) return; + + try { + // wUSDT balance (asset 2) + const wusdtData = await api.query.assets.account(USDT_ASSET_ID, account); + setWusdtBalance(wusdtData.isSome ? wusdtData.unwrap().balance.toString() : '0'); + } catch (error) { + if (import.meta.env.DEV) console.error('Failed to fetch wUSDT balance:', error); + } + }; + + fetchBalance(); + }, [api, isApiReady, account]); + + const handleMint = async () => { + if (!api || !isApiReady || !signer || !account) { + toast({ + title: 'Error', + description: 'Please connect your wallet', + variant: 'destructive', + }); + return; + } + + const usdtAmountRaw = BigInt(parseFloat(usdtAmount) * 10 ** USDT_DECIMALS); + + if (usdtAmountRaw <= BigInt(0)) { + setErrorMessage('Amount must be greater than zero'); + return; + } + + setTxStatus('signing'); + setErrorMessage(''); + + try { + if (import.meta.env.DEV) console.log('💵 Minting wUSDT...', { + usdtAmount, + usdtAmountRaw: usdtAmountRaw.toString(), + assetId: USDT_ASSET_ID, + }); + + const mintTx = api.tx.assets.mint(USDT_ASSET_ID, account, usdtAmountRaw.toString()); + + setTxStatus('submitting'); + + await mintTx.signAndSend( + account, + { signer }, + ({ status, dispatchError, events }) => { + if (import.meta.env.DEV) console.log('📦 Transaction status:', status.type); + + if (status.isInBlock) { + if (import.meta.env.DEV) console.log('✅ In block:', status.asInBlock.toHex()); + + if (dispatchError) { + let errorMsg = ''; + + if (dispatchError.isModule) { + const decoded = api.registry.findMetaError(dispatchError.asModule); + errorMsg = `${decoded.section}.${decoded.name}: ${decoded.docs.join(' ')}`; + if (import.meta.env.DEV) console.error('❌ Module error:', errorMsg); + } else { + errorMsg = dispatchError.toString(); + if (import.meta.env.DEV) console.error('❌ Dispatch error:', errorMsg); + } + + setErrorMessage(errorMsg); + setTxStatus('error'); + toast({ + title: 'Transaction Failed', + description: errorMsg, + variant: 'destructive', + }); + } else { + if (import.meta.env.DEV) console.log('✅ Mint successful!'); + if (import.meta.env.DEV) console.log('📋 Events:', events.map(e => e.event.method).join(', ')); + setTxStatus('success'); + toast({ + title: 'Success!', + description: `Successfully minted ${usdtAmount} wUSDT`, + }); + setTimeout(() => { + onSuccess?.(); + onClose(); + }, 2000); + } + } + } + ); + } catch (error) { + if (import.meta.env.DEV) console.error('Mint failed:', error); + setErrorMessage(error instanceof Error ? error.message : 'Transaction failed'); + setTxStatus('error'); + toast({ + title: 'Error', + description: error instanceof Error ? error.message : 'Mint failed', + variant: 'destructive', + }); + } + }; + + if (!isOpen) return null; + + const wusdtBalanceDisplay = (parseFloat(wusdtBalance) / 10 ** USDT_DECIMALS).toFixed(2); + + return ( +
+ + +
+ + Mint wUSDT Tokens + + +
+ + Admin Only - Token Minting + +
+ + + {/* Info Banner */} + + + + Mint wUSDT (Wrapped USDT) tokens for testing and liquidity pool creation. + Use responsibly! + + + + {/* USDT Amount */} +
+
+ + + Current: {wusdtBalanceDisplay} wUSDT + +
+
+ setUsdtAmount(e.target.value)} + placeholder="10000" + className="w-full px-4 py-3 bg-gray-800 border border-gray-700 rounded-lg text-white text-lg" + disabled={txStatus === 'signing' || txStatus === 'submitting'} + /> +
+ + +
+
+

+ 💡 wUSDT has 6 decimals (same as real USDT) +

+
+ + {/* Current wUSDT Balance */} +
+
Current wUSDT Balance
+
+ {wusdtBalanceDisplay} wUSDT +
+
+ + {/* Error Message */} + {errorMessage && ( + + + + {errorMessage} + + + )} + + {/* Success Message */} + {txStatus === 'success' && ( + + + + Successfully minted {usdtAmount} wUSDT! + + + )} + + {/* Action Buttons */} +
+ + +
+
+
+
+ ); +}; diff --git a/web/src/components/dex/SwapInterface.tsx b/web/src/components/dex/SwapInterface.tsx index 59667054..284a5cba 100644 --- a/web/src/components/dex/SwapInterface.tsx +++ b/web/src/components/dex/SwapInterface.tsx @@ -28,7 +28,7 @@ type TransactionStatus = 'idle' | 'signing' | 'submitting' | 'success' | 'error' const USER_TOKENS = [ { symbol: 'HEZ', emoji: '🟡', assetId: 0, name: 'HEZ', decimals: 12, displaySymbol: 'HEZ' }, // actually wHEZ (asset 0) { symbol: 'PEZ', emoji: '🟣', assetId: 1, name: 'PEZ', decimals: 12, displaySymbol: 'PEZ' }, - { symbol: 'USDT', emoji: '💵', assetId: 2, name: 'USDT', decimals: 6, displaySymbol: 'USDT' }, + { symbol: 'USDT', emoji: '💵', assetId: 1000, name: 'USDT', decimals: 6, displaySymbol: 'USDT' }, ] as const; export const SwapInterface: React.FC = ({ pools }) => { diff --git a/web/src/contexts/PolkadotContext.tsx b/web/src/contexts/PolkadotContext.tsx index 12a09819..b4462ea6 100644 --- a/web/src/contexts/PolkadotContext.tsx +++ b/web/src/contexts/PolkadotContext.tsx @@ -14,6 +14,7 @@ interface PolkadotContextType { connectWallet: () => Promise; disconnectWallet: () => void; error: string | null; + sudoKey: string | null; } const PolkadotContext = createContext(undefined); @@ -32,6 +33,7 @@ export const PolkadotProvider: React.FC = ({ const [accounts, setAccounts] = useState([]); const [selectedAccount, setSelectedAccount] = useState(null); const [error, setError] = useState(null); + const [sudoKey, setSudoKey] = useState(null); // Wrapper to trigger events when wallet changes const handleSetSelectedAccount = (account: InjectedAccountWithMeta | null) => { @@ -88,6 +90,16 @@ export const PolkadotProvider: React.FC = ({ if (import.meta.env.DEV) console.log(`🖥️ Node: ${nodeName} v${nodeVersion}`); } + // Fetch sudo key from blockchain + try { + const sudoAccount = await apiInstance.query.sudo.key(); + const sudoAddress = sudoAccount.toString(); + setSudoKey(sudoAddress); + if (import.meta.env.DEV) console.log(`🔑 Sudo key: ${sudoAddress}`); + } catch (err) { + if (import.meta.env.DEV) console.warn('⚠️ Failed to fetch sudo key (sudo pallet may not be available):', err); + } + return; } catch (err) { lastError = err; @@ -216,6 +228,7 @@ export const PolkadotProvider: React.FC = ({ connectWallet, disconnectWallet, error, + sudoKey, }; return (