mirror of
https://github.com/pezkuwichain/pwap.git
synced 2026-04-30 21:47:55 +00:00
4a3694c831
- Replaced shadowColor/shadowOffset/shadowOpacity/shadowRadius with boxShadow - Fixed 28 files (21 screens + 7 components) - Preserved elevation for Android compatibility - All React Native Web deprecation warnings resolved Files fixed: - All screen components - All reusable components - Navigation components - Modal components
871 lines
25 KiB
Plaintext
871 lines
25 KiB
Plaintext
import React, { useState, useEffect, useMemo } from 'react';
|
||
import {
|
||
View,
|
||
Text,
|
||
StyleSheet,
|
||
SafeAreaView,
|
||
ScrollView,
|
||
TouchableOpacity,
|
||
TextInput,
|
||
Modal,
|
||
ActivityIndicator,
|
||
Alert,
|
||
Platform,
|
||
Image,
|
||
} from 'react-native';
|
||
import { KurdistanColors } from '../theme/colors';
|
||
import { usePezkuwi } from '../contexts/PezkuwiContext';
|
||
|
||
// Token Images
|
||
const hezLogo = require('../../../shared/images/hez_logo.png');
|
||
const pezLogo = require('../../../shared/images/pez_logo.jpg');
|
||
const usdtLogo = require('../../../shared/images/USDT(hez)logo.png');
|
||
|
||
interface TokenInfo {
|
||
symbol: string;
|
||
name: string;
|
||
assetId: number;
|
||
decimals: number;
|
||
logo: any;
|
||
}
|
||
|
||
const TOKENS: TokenInfo[] = [
|
||
{ symbol: 'HEZ', name: 'Hemuwelet', assetId: 0, decimals: 12, logo: hezLogo },
|
||
{ symbol: 'PEZ', name: 'Pezkunel', assetId: 1, decimals: 12, logo: pezLogo },
|
||
{ symbol: 'USDT', name: 'Tether USD', assetId: 1000, decimals: 6, logo: usdtLogo },
|
||
];
|
||
|
||
type TransactionStatus = 'idle' | 'signing' | 'submitting' | 'success' | 'error';
|
||
|
||
const SwapScreen: React.FC = () => {
|
||
const { api, isApiReady, selectedAccount, getKeyPair } = usePezkuwi();
|
||
|
||
const [fromToken, setFromToken] = useState<TokenInfo>(TOKENS[0]);
|
||
const [toToken, setToToken] = useState<TokenInfo>(TOKENS[1]);
|
||
const [fromAmount, setFromAmount] = useState('');
|
||
const [toAmount, setToAmount] = useState('');
|
||
const [slippage, setSlippage] = useState(0.5); // 0.5% default
|
||
|
||
const [fromBalance, setFromBalance] = useState('0');
|
||
const [toBalance, setToBalance] = useState('0');
|
||
|
||
const [txStatus, setTxStatus] = useState<TransactionStatus>('idle');
|
||
const [errorMessage, setErrorMessage] = useState('');
|
||
|
||
const [showTokenSelector, setShowTokenSelector] = useState<'from' | 'to' | null>(null);
|
||
const [showSettings, setShowSettings] = useState(false);
|
||
const [showConfirm, setShowConfirm] = useState(false);
|
||
|
||
// Fetch balances
|
||
useEffect(() => {
|
||
const fetchBalances = async () => {
|
||
if (!api || !isApiReady || !selectedAccount) return;
|
||
|
||
// Fetch From Token Balance
|
||
try {
|
||
if (fromToken.symbol === 'HEZ') {
|
||
const accountInfo = await api.query.system.account(selectedAccount.address);
|
||
setFromBalance(accountInfo.data.free.toString());
|
||
} else {
|
||
const balanceData = await api.query.assets.account(fromToken.assetId, selectedAccount.address);
|
||
setFromBalance(balanceData.isSome ? balanceData.unwrap().balance.toString() : '0');
|
||
}
|
||
} catch (error) {
|
||
console.error('Failed to fetch from balance:', error);
|
||
setFromBalance('0');
|
||
}
|
||
|
||
// Fetch To Token Balance
|
||
try {
|
||
if (toToken.symbol === 'HEZ') {
|
||
const accountInfo = await api.query.system.account(selectedAccount.address);
|
||
setToBalance(accountInfo.data.free.toString());
|
||
} else {
|
||
const balanceData = await api.query.assets.account(toToken.assetId, selectedAccount.address);
|
||
setToBalance(balanceData.isSome ? balanceData.unwrap().balance.toString() : '0');
|
||
}
|
||
} catch (error) {
|
||
console.error('Failed to fetch to balance:', error);
|
||
setToBalance('0');
|
||
}
|
||
};
|
||
|
||
fetchBalances();
|
||
}, [api, isApiReady, selectedAccount, fromToken, toToken]);
|
||
|
||
// Calculate output amount (simple 1:1 for now - should use pool reserves)
|
||
useEffect(() => {
|
||
if (!fromAmount || parseFloat(fromAmount) <= 0) {
|
||
setToAmount('');
|
||
return;
|
||
}
|
||
|
||
// TODO: Implement proper AMM calculation using pool reserves
|
||
// For now, simple 1:1 conversion (placeholder)
|
||
const calculatedAmount = (parseFloat(fromAmount) * 0.97).toFixed(6); // 3% fee simulation
|
||
setToAmount(calculatedAmount);
|
||
}, [fromAmount, fromToken, toToken]);
|
||
|
||
// Calculate formatted balances
|
||
const fromBalanceFormatted = useMemo(() => {
|
||
return (Number(fromBalance) / Math.pow(10, fromToken.decimals)).toFixed(4);
|
||
}, [fromBalance, fromToken]);
|
||
|
||
const toBalanceFormatted = useMemo(() => {
|
||
return (Number(toBalance) / Math.pow(10, toToken.decimals)).toFixed(4);
|
||
}, [toBalance, toToken]);
|
||
|
||
const hasInsufficientBalance = useMemo(() => {
|
||
const amountNum = parseFloat(fromAmount || '0');
|
||
const balanceNum = parseFloat(fromBalanceFormatted);
|
||
return amountNum > 0 && amountNum > balanceNum;
|
||
}, [fromAmount, fromBalanceFormatted]);
|
||
|
||
const handleSwapDirection = () => {
|
||
const tempToken = fromToken;
|
||
const tempBalance = fromBalance;
|
||
const tempAmount = fromAmount;
|
||
|
||
setFromToken(toToken);
|
||
setToToken(tempToken);
|
||
setFromBalance(toBalance);
|
||
setToBalance(tempBalance);
|
||
setFromAmount(toAmount);
|
||
setToAmount(tempAmount);
|
||
};
|
||
|
||
const handleMaxClick = () => {
|
||
setFromAmount(fromBalanceFormatted);
|
||
};
|
||
|
||
const handleTokenSelect = (token: TokenInfo) => {
|
||
if (showTokenSelector === 'from') {
|
||
if (token.symbol === toToken.symbol) {
|
||
Alert.alert('Error', 'Cannot select the same token for both sides');
|
||
return;
|
||
}
|
||
setFromToken(token);
|
||
} else if (showTokenSelector === 'to') {
|
||
if (token.symbol === fromToken.symbol) {
|
||
Alert.alert('Error', 'Cannot select the same token for both sides');
|
||
return;
|
||
}
|
||
setToToken(token);
|
||
}
|
||
setShowTokenSelector(null);
|
||
};
|
||
|
||
const handleConfirmSwap = async () => {
|
||
if (!api || !selectedAccount) {
|
||
Alert.alert('Error', 'Please connect your wallet');
|
||
return;
|
||
}
|
||
|
||
setTxStatus('signing');
|
||
setShowConfirm(false);
|
||
setErrorMessage('');
|
||
|
||
try {
|
||
const keypair = await getKeyPair(selectedAccount.address);
|
||
if (!keypair) throw new Error('Failed to load keypair');
|
||
|
||
const amountIn = BigInt(Math.floor(parseFloat(fromAmount) * Math.pow(10, fromToken.decimals)));
|
||
const minAmountOut = BigInt(
|
||
Math.floor(parseFloat(toAmount) * (1 - slippage / 100) * Math.pow(10, toToken.decimals))
|
||
);
|
||
|
||
let tx;
|
||
|
||
if (fromToken.symbol === 'HEZ' && toToken.symbol === 'PEZ') {
|
||
// HEZ → PEZ: wrap(HEZ→wHEZ) then swap(wHEZ→PEZ)
|
||
const wrapTx = api.tx.tokenWrapper.wrap(amountIn.toString());
|
||
const swapTx = api.tx.assetConversion.swapExactTokensForTokens(
|
||
[0, 1], // wHEZ → PEZ
|
||
amountIn.toString(),
|
||
minAmountOut.toString(),
|
||
selectedAccount.address,
|
||
true
|
||
);
|
||
tx = api.tx.utility.batchAll([wrapTx, swapTx]);
|
||
|
||
} else if (fromToken.symbol === 'PEZ' && toToken.symbol === 'HEZ') {
|
||
// PEZ → HEZ: swap(PEZ→wHEZ) then unwrap(wHEZ→HEZ)
|
||
const swapTx = api.tx.assetConversion.swapExactTokensForTokens(
|
||
[1, 0], // PEZ → wHEZ
|
||
amountIn.toString(),
|
||
minAmountOut.toString(),
|
||
selectedAccount.address,
|
||
true
|
||
);
|
||
const unwrapTx = api.tx.tokenWrapper.unwrap(minAmountOut.toString());
|
||
tx = api.tx.utility.batchAll([swapTx, unwrapTx]);
|
||
|
||
} else if (fromToken.symbol === 'HEZ') {
|
||
// HEZ → Any Asset: wrap(HEZ→wHEZ) then swap(wHEZ→Asset)
|
||
const wrapTx = api.tx.tokenWrapper.wrap(amountIn.toString());
|
||
const swapTx = api.tx.assetConversion.swapExactTokensForTokens(
|
||
[0, toToken.assetId],
|
||
amountIn.toString(),
|
||
minAmountOut.toString(),
|
||
selectedAccount.address,
|
||
true
|
||
);
|
||
tx = api.tx.utility.batchAll([wrapTx, swapTx]);
|
||
|
||
} else if (toToken.symbol === 'HEZ') {
|
||
// Any Asset → HEZ: swap(Asset→wHEZ) then unwrap(wHEZ→HEZ)
|
||
const swapTx = api.tx.assetConversion.swapExactTokensForTokens(
|
||
[fromToken.assetId, 0],
|
||
amountIn.toString(),
|
||
minAmountOut.toString(),
|
||
selectedAccount.address,
|
||
true
|
||
);
|
||
const unwrapTx = api.tx.tokenWrapper.unwrap(minAmountOut.toString());
|
||
tx = api.tx.utility.batchAll([swapTx, unwrapTx]);
|
||
|
||
} else {
|
||
// Direct swap between assets (PEZ ↔ USDT, etc.)
|
||
tx = api.tx.assetConversion.swapExactTokensForTokens(
|
||
[fromToken.assetId, toToken.assetId],
|
||
amountIn.toString(),
|
||
minAmountOut.toString(),
|
||
selectedAccount.address,
|
||
true
|
||
);
|
||
}
|
||
|
||
setTxStatus('submitting');
|
||
|
||
await tx.signAndSend(keypair, ({ status, dispatchError }) => {
|
||
if (status.isInBlock) {
|
||
if (dispatchError) {
|
||
const errorMsg = dispatchError.toString();
|
||
setErrorMessage(errorMsg);
|
||
setTxStatus('error');
|
||
Alert.alert('Transaction Failed', errorMsg);
|
||
} else {
|
||
setTxStatus('success');
|
||
Alert.alert('Success!', `Swapped ${fromAmount} ${fromToken.symbol} for ~${toAmount} ${toToken.symbol}`);
|
||
setTimeout(() => {
|
||
setFromAmount('');
|
||
setToAmount('');
|
||
setTxStatus('idle');
|
||
}, 2000);
|
||
}
|
||
}
|
||
});
|
||
} catch (error: any) {
|
||
console.error('Swap failed:', error);
|
||
setErrorMessage(error.message || 'Transaction failed');
|
||
setTxStatus('error');
|
||
Alert.alert('Error', error.message || 'Swap transaction failed');
|
||
}
|
||
};
|
||
|
||
if (!selectedAccount) {
|
||
return (
|
||
<SafeAreaView style={styles.container}>
|
||
<View style={styles.centerContent}>
|
||
<Text style={styles.emptyIcon}>💱</Text>
|
||
<Text style={styles.emptyText}>Connect your wallet to swap tokens</Text>
|
||
</View>
|
||
</SafeAreaView>
|
||
);
|
||
}
|
||
|
||
return (
|
||
<SafeAreaView style={styles.container}>
|
||
{/* Transaction Loading Overlay */}
|
||
{(txStatus === 'signing' || txStatus === 'submitting') && (
|
||
<View style={styles.loadingOverlay}>
|
||
<View style={styles.loadingCard}>
|
||
<ActivityIndicator size="large" color={KurdistanColors.kesk} />
|
||
<Text style={styles.loadingText}>
|
||
{txStatus === 'signing' ? 'Waiting for signature...' : 'Processing swap...'}
|
||
</Text>
|
||
</View>
|
||
</View>
|
||
)}
|
||
|
||
<ScrollView style={styles.scrollContent} contentContainerStyle={styles.scrollContentContainer}>
|
||
{/* Header */}
|
||
<View style={styles.header}>
|
||
<Text style={styles.headerTitle}>Swap Tokens</Text>
|
||
<TouchableOpacity onPress={() => setShowSettings(true)} style={styles.settingsButton}>
|
||
<Text style={styles.settingsIcon}>⚙️</Text>
|
||
</TouchableOpacity>
|
||
</View>
|
||
|
||
{/* From Token Card */}
|
||
<View style={styles.tokenCard}>
|
||
<View style={styles.tokenCardHeader}>
|
||
<Text style={styles.tokenCardLabel}>From</Text>
|
||
<Text style={styles.tokenCardBalance}>
|
||
Balance: {fromBalanceFormatted} {fromToken.symbol}
|
||
</Text>
|
||
</View>
|
||
|
||
<View style={styles.tokenInputRow}>
|
||
<TextInput
|
||
style={styles.amountInput}
|
||
placeholder="0.0"
|
||
placeholderTextColor="#999"
|
||
keyboardType="decimal-pad"
|
||
value={fromAmount}
|
||
onChangeText={setFromAmount}
|
||
/>
|
||
<TouchableOpacity
|
||
style={styles.tokenSelector}
|
||
onPress={() => setShowTokenSelector('from')}
|
||
>
|
||
<Image source={fromToken.logo} style={styles.tokenLogo} resizeMode="contain" />
|
||
<Text style={styles.tokenSymbol}>{fromToken.symbol}</Text>
|
||
<Text style={styles.tokenSelectorArrow}>▼</Text>
|
||
</TouchableOpacity>
|
||
</View>
|
||
|
||
<TouchableOpacity style={styles.maxButton} onPress={handleMaxClick}>
|
||
<Text style={styles.maxButtonText}>MAX</Text>
|
||
</TouchableOpacity>
|
||
</View>
|
||
|
||
{/* Swap Direction Button */}
|
||
<View style={styles.swapDirectionContainer}>
|
||
<TouchableOpacity style={styles.swapDirectionButton} onPress={handleSwapDirection}>
|
||
<Text style={styles.swapDirectionIcon}>⇅</Text>
|
||
</TouchableOpacity>
|
||
</View>
|
||
|
||
{/* To Token Card */}
|
||
<View style={styles.tokenCard}>
|
||
<View style={styles.tokenCardHeader}>
|
||
<Text style={styles.tokenCardLabel}>To</Text>
|
||
<Text style={styles.tokenCardBalance}>
|
||
Balance: {toBalanceFormatted} {toToken.symbol}
|
||
</Text>
|
||
</View>
|
||
|
||
<View style={styles.tokenInputRow}>
|
||
<TextInput
|
||
style={styles.amountInput}
|
||
placeholder="0.0"
|
||
placeholderTextColor="#999"
|
||
value={toAmount}
|
||
editable={false}
|
||
/>
|
||
<TouchableOpacity
|
||
style={styles.tokenSelector}
|
||
onPress={() => setShowTokenSelector('to')}
|
||
>
|
||
<Image source={toToken.logo} style={styles.tokenLogo} resizeMode="contain" />
|
||
<Text style={styles.tokenSymbol}>{toToken.symbol}</Text>
|
||
<Text style={styles.tokenSelectorArrow}>▼</Text>
|
||
</TouchableOpacity>
|
||
</View>
|
||
</View>
|
||
|
||
{/* Swap Details */}
|
||
<View style={styles.detailsCard}>
|
||
<View style={styles.detailRow}>
|
||
<Text style={styles.detailLabel}>ℹ️ Exchange Rate</Text>
|
||
<Text style={styles.detailValue}>1 {fromToken.symbol} ≈ 1 {toToken.symbol}</Text>
|
||
</View>
|
||
<View style={styles.detailRow}>
|
||
<Text style={styles.detailLabel}>Slippage Tolerance</Text>
|
||
<Text style={styles.detailValueHighlight}>{slippage}%</Text>
|
||
</View>
|
||
</View>
|
||
|
||
{/* Warnings */}
|
||
{hasInsufficientBalance && (
|
||
<View style={[styles.warningCard, styles.errorCard]}>
|
||
<Text style={styles.warningIcon}>⚠️</Text>
|
||
<Text style={styles.warningText}>Insufficient {fromToken.symbol} balance</Text>
|
||
</View>
|
||
)}
|
||
|
||
{/* Swap Button */}
|
||
<TouchableOpacity
|
||
style={[
|
||
styles.swapButton,
|
||
(!fromAmount || hasInsufficientBalance || txStatus !== 'idle') && styles.swapButtonDisabled
|
||
]}
|
||
onPress={() => setShowConfirm(true)}
|
||
disabled={!fromAmount || hasInsufficientBalance || txStatus !== 'idle'}
|
||
>
|
||
<Text style={styles.swapButtonText}>
|
||
{hasInsufficientBalance
|
||
? `Insufficient ${fromToken.symbol} Balance`
|
||
: 'Swap Tokens'}
|
||
</Text>
|
||
</TouchableOpacity>
|
||
</ScrollView>
|
||
|
||
{/* Token Selector Modal */}
|
||
<Modal visible={showTokenSelector !== null} transparent animationType="slide" onRequestClose={() => setShowTokenSelector(null)}>
|
||
<View style={styles.modalOverlay}>
|
||
<View style={styles.modalCard}>
|
||
<Text style={styles.modalHeader}>Select Token</Text>
|
||
{TOKENS.map((token) => (
|
||
<TouchableOpacity
|
||
key={token.symbol}
|
||
style={styles.tokenOption}
|
||
onPress={() => handleTokenSelect(token)}
|
||
>
|
||
<Image source={token.logo} style={styles.tokenOptionLogo} resizeMode="contain" />
|
||
<View style={styles.tokenOptionInfo}>
|
||
<Text style={styles.tokenOptionSymbol}>{token.symbol}</Text>
|
||
<Text style={styles.tokenOptionName}>{token.name}</Text>
|
||
</View>
|
||
</TouchableOpacity>
|
||
))}
|
||
<TouchableOpacity style={styles.modalCloseButton} onPress={() => setShowTokenSelector(null)}>
|
||
<Text style={styles.modalCloseButtonText}>Cancel</Text>
|
||
</TouchableOpacity>
|
||
</View>
|
||
</View>
|
||
</Modal>
|
||
|
||
{/* Settings Modal */}
|
||
<Modal visible={showSettings} transparent animationType="slide" onRequestClose={() => setShowSettings(false)}>
|
||
<View style={styles.modalOverlay}>
|
||
<View style={styles.modalCard}>
|
||
<Text style={styles.modalHeader}>Swap Settings</Text>
|
||
<Text style={styles.settingsLabel}>Slippage Tolerance</Text>
|
||
<View style={styles.slippageButtons}>
|
||
{[0.1, 0.5, 1.0, 2.0].map((val) => (
|
||
<TouchableOpacity
|
||
key={val}
|
||
style={[styles.slippageButton, slippage === val && styles.slippageButtonActive]}
|
||
onPress={() => setSlippage(val)}
|
||
>
|
||
<Text style={[styles.slippageButtonText, slippage === val && styles.slippageButtonTextActive]}>
|
||
{val}%
|
||
</Text>
|
||
</TouchableOpacity>
|
||
))}
|
||
</View>
|
||
<TouchableOpacity style={styles.modalCloseButton} onPress={() => setShowSettings(false)}>
|
||
<Text style={styles.modalCloseButtonText}>Close</Text>
|
||
</TouchableOpacity>
|
||
</View>
|
||
</View>
|
||
</Modal>
|
||
|
||
{/* Confirm Modal */}
|
||
<Modal visible={showConfirm} transparent animationType="slide" onRequestClose={() => setShowConfirm(false)}>
|
||
<View style={styles.modalOverlay}>
|
||
<View style={styles.modalCard}>
|
||
<Text style={styles.modalHeader}>Confirm Swap</Text>
|
||
<View style={styles.confirmDetails}>
|
||
<View style={styles.confirmRow}>
|
||
<Text style={styles.confirmLabel}>You Pay</Text>
|
||
<Text style={styles.confirmValue}>{fromAmount} {fromToken.symbol}</Text>
|
||
</View>
|
||
<View style={styles.confirmRow}>
|
||
<Text style={styles.confirmLabel}>You Receive</Text>
|
||
<Text style={styles.confirmValue}>{toAmount} {toToken.symbol}</Text>
|
||
</View>
|
||
<View style={[styles.confirmRow, styles.confirmRowBorder]}>
|
||
<Text style={styles.confirmLabelSmall}>Slippage</Text>
|
||
<Text style={styles.confirmValueSmall}>{slippage}%</Text>
|
||
</View>
|
||
</View>
|
||
<View style={styles.confirmButtons}>
|
||
<TouchableOpacity style={styles.confirmCancelButton} onPress={() => setShowConfirm(false)}>
|
||
<Text style={styles.confirmCancelButtonText}>Cancel</Text>
|
||
</TouchableOpacity>
|
||
<TouchableOpacity style={styles.confirmSwapButton} onPress={handleConfirmSwap}>
|
||
<Text style={styles.confirmSwapButtonText}>Confirm Swap</Text>
|
||
</TouchableOpacity>
|
||
</View>
|
||
</View>
|
||
</View>
|
||
</Modal>
|
||
</SafeAreaView>
|
||
);
|
||
};
|
||
|
||
const styles = StyleSheet.create({
|
||
container: {
|
||
flex: 1,
|
||
backgroundColor: '#F8F9FA',
|
||
},
|
||
centerContent: {
|
||
flex: 1,
|
||
justifyContent: 'center',
|
||
alignItems: 'center',
|
||
padding: 40,
|
||
},
|
||
emptyIcon: {
|
||
fontSize: 64,
|
||
marginBottom: 16,
|
||
},
|
||
emptyText: {
|
||
fontSize: 16,
|
||
color: '#666',
|
||
textAlign: 'center',
|
||
},
|
||
scrollContent: {
|
||
flex: 1,
|
||
},
|
||
scrollContentContainer: {
|
||
padding: 16,
|
||
},
|
||
header: {
|
||
flexDirection: 'row',
|
||
justifyContent: 'space-between',
|
||
alignItems: 'center',
|
||
marginBottom: 20,
|
||
},
|
||
headerTitle: {
|
||
fontSize: 24,
|
||
fontWeight: 'bold',
|
||
color: '#333',
|
||
},
|
||
settingsButton: {
|
||
width: 40,
|
||
height: 40,
|
||
borderRadius: 20,
|
||
backgroundColor: '#F5F5F5',
|
||
justifyContent: 'center',
|
||
alignItems: 'center',
|
||
},
|
||
settingsIcon: {
|
||
fontSize: 20,
|
||
},
|
||
tokenCard: {
|
||
backgroundColor: '#FFFFFF',
|
||
borderRadius: 16,
|
||
padding: 16,
|
||
marginBottom: 8,
|
||
shadowColor: '#000',
|
||
shadowOffset: { width: 0, height: 2 },
|
||
shadowOpacity: 0.05,
|
||
shadowRadius: 4,
|
||
elevation: 2,
|
||
},
|
||
tokenCardHeader: {
|
||
flexDirection: 'row',
|
||
justifyContent: 'space-between',
|
||
marginBottom: 12,
|
||
},
|
||
tokenCardLabel: {
|
||
fontSize: 14,
|
||
color: '#666',
|
||
},
|
||
tokenCardBalance: {
|
||
fontSize: 12,
|
||
color: '#999',
|
||
},
|
||
tokenInputRow: {
|
||
flexDirection: 'row',
|
||
alignItems: 'center',
|
||
gap: 12,
|
||
},
|
||
amountInput: {
|
||
flex: 1,
|
||
fontSize: 28,
|
||
fontWeight: 'bold',
|
||
color: '#333',
|
||
padding: 0,
|
||
},
|
||
tokenSelector: {
|
||
flexDirection: 'row',
|
||
alignItems: 'center',
|
||
backgroundColor: '#F5F5F5',
|
||
paddingHorizontal: 12,
|
||
paddingVertical: 8,
|
||
borderRadius: 12,
|
||
gap: 8,
|
||
},
|
||
tokenLogo: {
|
||
width: 24,
|
||
height: 24,
|
||
},
|
||
tokenSymbol: {
|
||
fontSize: 16,
|
||
fontWeight: '600',
|
||
color: '#333',
|
||
},
|
||
tokenSelectorArrow: {
|
||
fontSize: 10,
|
||
color: '#666',
|
||
},
|
||
maxButton: {
|
||
alignSelf: 'flex-start',
|
||
marginTop: 8,
|
||
paddingHorizontal: 12,
|
||
paddingVertical: 6,
|
||
backgroundColor: 'rgba(0, 143, 67, 0.1)',
|
||
borderRadius: 8,
|
||
borderWidth: 1,
|
||
borderColor: 'rgba(0, 143, 67, 0.3)',
|
||
},
|
||
maxButtonText: {
|
||
fontSize: 12,
|
||
fontWeight: '600',
|
||
color: KurdistanColors.kesk,
|
||
},
|
||
swapDirectionContainer: {
|
||
alignItems: 'center',
|
||
marginVertical: -12,
|
||
zIndex: 10,
|
||
},
|
||
swapDirectionButton: {
|
||
width: 48,
|
||
height: 48,
|
||
borderRadius: 24,
|
||
backgroundColor: '#FFFFFF',
|
||
borderWidth: 2,
|
||
borderColor: '#E5E5E5',
|
||
justifyContent: 'center',
|
||
alignItems: 'center',
|
||
shadowColor: '#000',
|
||
shadowOffset: { width: 0, height: 2 },
|
||
shadowOpacity: 0.1,
|
||
shadowRadius: 4,
|
||
elevation: 3,
|
||
},
|
||
swapDirectionIcon: {
|
||
fontSize: 24,
|
||
color: '#333',
|
||
},
|
||
detailsCard: {
|
||
backgroundColor: '#FFFFFF',
|
||
borderRadius: 16,
|
||
padding: 16,
|
||
marginTop: 16,
|
||
marginBottom: 16,
|
||
},
|
||
detailRow: {
|
||
flexDirection: 'row',
|
||
justifyContent: 'space-between',
|
||
paddingVertical: 8,
|
||
},
|
||
detailLabel: {
|
||
fontSize: 14,
|
||
color: '#666',
|
||
},
|
||
detailValue: {
|
||
fontSize: 14,
|
||
color: '#333',
|
||
fontWeight: '500',
|
||
},
|
||
detailValueHighlight: {
|
||
fontSize: 14,
|
||
color: '#3B82F6',
|
||
fontWeight: '600',
|
||
},
|
||
warningCard: {
|
||
flexDirection: 'row',
|
||
alignItems: 'center',
|
||
backgroundColor: '#FEF3C7',
|
||
borderRadius: 12,
|
||
padding: 12,
|
||
marginBottom: 16,
|
||
gap: 8,
|
||
},
|
||
errorCard: {
|
||
backgroundColor: '#FEE2E2',
|
||
},
|
||
warningIcon: {
|
||
fontSize: 20,
|
||
},
|
||
warningText: {
|
||
flex: 1,
|
||
fontSize: 14,
|
||
color: '#991B1B',
|
||
},
|
||
swapButton: {
|
||
backgroundColor: KurdistanColors.kesk,
|
||
borderRadius: 16,
|
||
padding: 18,
|
||
alignItems: 'center',
|
||
marginBottom: 20,
|
||
},
|
||
swapButtonDisabled: {
|
||
backgroundColor: '#CCC',
|
||
},
|
||
swapButtonText: {
|
||
fontSize: 18,
|
||
fontWeight: 'bold',
|
||
color: '#FFFFFF',
|
||
},
|
||
loadingOverlay: {
|
||
position: 'absolute',
|
||
top: 0,
|
||
left: 0,
|
||
right: 0,
|
||
bottom: 0,
|
||
backgroundColor: 'rgba(0,0,0,0.7)',
|
||
justifyContent: 'center',
|
||
alignItems: 'center',
|
||
zIndex: 1000,
|
||
},
|
||
loadingCard: {
|
||
backgroundColor: '#FFFFFF',
|
||
borderRadius: 20,
|
||
padding: 32,
|
||
alignItems: 'center',
|
||
gap: 16,
|
||
},
|
||
loadingText: {
|
||
fontSize: 16,
|
||
fontWeight: '600',
|
||
color: '#333',
|
||
},
|
||
modalOverlay: {
|
||
flex: 1,
|
||
backgroundColor: 'rgba(0,0,0,0.5)',
|
||
justifyContent: 'center',
|
||
alignItems: 'center',
|
||
padding: 20,
|
||
},
|
||
modalCard: {
|
||
backgroundColor: '#FFFFFF',
|
||
borderRadius: 20,
|
||
padding: 24,
|
||
width: '100%',
|
||
maxWidth: 400,
|
||
},
|
||
modalHeader: {
|
||
fontSize: 22,
|
||
fontWeight: 'bold',
|
||
color: '#333',
|
||
marginBottom: 20,
|
||
textAlign: 'center',
|
||
},
|
||
tokenOption: {
|
||
flexDirection: 'row',
|
||
alignItems: 'center',
|
||
padding: 16,
|
||
borderRadius: 12,
|
||
backgroundColor: '#F8F9FA',
|
||
marginBottom: 8,
|
||
gap: 12,
|
||
},
|
||
tokenOptionLogo: {
|
||
width: 40,
|
||
height: 40,
|
||
},
|
||
tokenOptionInfo: {
|
||
flex: 1,
|
||
},
|
||
tokenOptionSymbol: {
|
||
fontSize: 16,
|
||
fontWeight: 'bold',
|
||
color: '#333',
|
||
},
|
||
tokenOptionName: {
|
||
fontSize: 12,
|
||
color: '#666',
|
||
},
|
||
modalCloseButton: {
|
||
backgroundColor: '#EEE',
|
||
borderRadius: 12,
|
||
padding: 16,
|
||
alignItems: 'center',
|
||
marginTop: 12,
|
||
},
|
||
modalCloseButtonText: {
|
||
fontSize: 16,
|
||
fontWeight: '600',
|
||
color: '#333',
|
||
},
|
||
settingsLabel: {
|
||
fontSize: 14,
|
||
fontWeight: '500',
|
||
color: '#666',
|
||
marginBottom: 12,
|
||
},
|
||
slippageButtons: {
|
||
flexDirection: 'row',
|
||
gap: 8,
|
||
marginBottom: 20,
|
||
},
|
||
slippageButton: {
|
||
flex: 1,
|
||
padding: 12,
|
||
borderRadius: 12,
|
||
backgroundColor: '#F5F5F5',
|
||
alignItems: 'center',
|
||
},
|
||
slippageButtonActive: {
|
||
backgroundColor: KurdistanColors.kesk,
|
||
},
|
||
slippageButtonText: {
|
||
fontSize: 14,
|
||
fontWeight: '600',
|
||
color: '#666',
|
||
},
|
||
slippageButtonTextActive: {
|
||
color: '#FFFFFF',
|
||
},
|
||
confirmDetails: {
|
||
backgroundColor: '#F8F9FA',
|
||
borderRadius: 12,
|
||
padding: 16,
|
||
marginBottom: 20,
|
||
},
|
||
confirmRow: {
|
||
flexDirection: 'row',
|
||
justifyContent: 'space-between',
|
||
paddingVertical: 8,
|
||
},
|
||
confirmRowBorder: {
|
||
borderTopWidth: 1,
|
||
borderTopColor: '#E5E5E5',
|
||
marginTop: 8,
|
||
paddingTop: 16,
|
||
},
|
||
confirmLabel: {
|
||
fontSize: 14,
|
||
color: '#666',
|
||
},
|
||
confirmValue: {
|
||
fontSize: 16,
|
||
fontWeight: 'bold',
|
||
color: '#333',
|
||
},
|
||
confirmLabelSmall: {
|
||
fontSize: 12,
|
||
color: '#999',
|
||
},
|
||
confirmValueSmall: {
|
||
fontSize: 12,
|
||
color: '#666',
|
||
},
|
||
confirmButtons: {
|
||
flexDirection: 'row',
|
||
gap: 12,
|
||
},
|
||
confirmCancelButton: {
|
||
flex: 1,
|
||
padding: 16,
|
||
borderRadius: 12,
|
||
backgroundColor: '#EEE',
|
||
alignItems: 'center',
|
||
},
|
||
confirmCancelButtonText: {
|
||
fontSize: 16,
|
||
fontWeight: '600',
|
||
color: '#333',
|
||
},
|
||
confirmSwapButton: {
|
||
flex: 1,
|
||
padding: 16,
|
||
borderRadius: 12,
|
||
backgroundColor: KurdistanColors.kesk,
|
||
alignItems: 'center',
|
||
},
|
||
confirmSwapButtonText: {
|
||
fontSize: 16,
|
||
fontWeight: '600',
|
||
color: '#FFFFFF',
|
||
},
|
||
});
|
||
|
||
export default SwapScreen;
|