From 19f8700b78f324861df7eb49ab6c315eb5746d6f Mon Sep 17 00:00:00 2001 From: Timur Gordon <31495328+timurgordon@users.noreply.github.com> Date: Tue, 29 Apr 2025 01:11:51 +0200 Subject: [PATCH] feat: Implement comprehensive DeFi platform in Digital Assets dashboard Add a complete DeFi platform with the following features: - Tabbed interface for different DeFi functionalities - Lending & Borrowing system with APY calculations - Liquidity Pools with LP token rewards - Staking options for tokens and digital assets - Token Swap interface with real-time exchange rates - Collateralization system for loans and synthetic assets - Interactive JavaScript functionality for real-time calculations This enhancement provides users with a complete suite of DeFi tools directly integrated into the Digital Assets dashboard. --- actix_mvc_app/src/views/assets/index.html | 1508 +++++++++++++++++++++ actix_mvc_app/static/js/defi.js | 571 ++++++++ 2 files changed, 2079 insertions(+) create mode 100644 actix_mvc_app/static/js/defi.js diff --git a/actix_mvc_app/src/views/assets/index.html b/actix_mvc_app/src/views/assets/index.html index e4b71f8..8fcbe01 100644 --- a/actix_mvc_app/src/views/assets/index.html +++ b/actix_mvc_app/src/views/assets/index.html @@ -62,6 +62,1511 @@ + +
+
+ + DeFi Platform +
+
+ + +
+ +
+
+

Welcome to the ZAZ DeFi Platform!

+

Our decentralized finance platform allows you to maximize the value of your digital assets through various financial services.

+
+

Use the tabs above to explore lending, borrowing, liquidity pools, staking, swapping, and collateralization features.

+
+
+ + +
+
+
+
+
+ Lend Your Assets +
+
+

Earn interest by lending your digital assets to the ZAZ DeFi platform.

+ +
+
+ + +
+ +
+ +
+ + TFT +
+
+ +
+ + +
+ +
+
+ Estimated Interest: + 0.00 TFT +
+
+ Return Amount: + 0.00 TFT +
+
+ +
+ +
+
+
+
+
+ +
+
+
+ Borrow Against Assets +
+
+

Borrow digital assets using your existing assets as collateral.

+ +
+
+ + +
+ +
+ + +
+ +
+ +
+ + TFT +
+
You can borrow up to 70% of your collateral value.
+
+ +
+ + +
+ +
+
+ Collateral Ratio: + 0% +
+
+ Interest Due: + 0.00 TFT +
+
+ Total Repayment: + 0.00 TFT +
+
+
+
+
+ +
+ +
+
+
+
+
+
+ + +
+
+ Your Active Positions +
+
+ + +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
AssetAmountInterest RateStart DateEnd DateEarned InterestStatusActions
ThreeFold Token (TFT)1,000 TFT4.2% APY2025-04-012025-05-013.5 TFTActive + +
Zanzibar Token (ZAZ)500 ZAZ6.8% APY2025-03-152025-06-158.5 ZAZActive + +
+
+
+ +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + +
Borrowed AssetAmountCollateralCollateral RatioInterest RateStart DateDue DateActions
ThreeFold Token (TFT)500 TFTBeach Property NFT +
+
+
+
+ 65% +
+
5.2% APR2025-04-102025-05-10 + +
+
+
+
+
+
+
+ + +
+
+
+
+
About Liquidity Pools
+

Liquidity pools are collections of tokens locked in smart contracts that provide liquidity for decentralized trading. By adding your assets to a liquidity pool, you earn a share of the trading fees generated by the pool.

+
+
+
+ + +
+
+
+
+ Available Liquidity Pools +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
PoolTotal Liquidity24h VolumeAPYYour LiquidityYour ShareActions
+
+
+ TFT + ZAZ +
+ TFT-ZAZ +
+
$1,250,000$45,00012.5%$2,5000.2% +
+ + +
+
+
+
+ TFT + USDT +
+ TFT-USDT +
+
$3,750,000$125,0008.2%$00% +
+ + +
+
+
+
+ ZAZ + USDT +
+ ZAZ-USDT +
+
$850,000$32,00015.8%$5,0000.59% +
+ + +
+
+
+
+ BTC + USDT +
+ BTC-USDT +
+
$5,250,000$450,0006.5%$00% +
+ + +
+
+
+
+ ETH + USDT +
+ ETH-USDT +
+
$4,100,000$320,0007.2%$00% +
+ + +
+
+
+
+
+
+
+ + +
+
+
+
+ Your Liquidity Positions +
+
+
+ +
+
+
+
+
+ TFT + ZAZ +
+ TFT-ZAZ +
+
+
+
+ Your Liquidity: + $2,500 +
+
+ Pool Share: + 0.2% +
+
+ TFT: + 500 TFT +
+
+ ZAZ: + 1,250 ZAZ +
+
+ Earned Fees: + $45.20 +
+
+ APY: + 12.5% +
+
+ + + +
+
+
+
+ + +
+
+
+
+
+ ZAZ + USDT +
+ ZAZ-USDT +
+
+
+
+ Your Liquidity: + $5,000 +
+
+ Pool Share: + 0.59% +
+
+ ZAZ: + 2,500 ZAZ +
+
+ USDT: + 2,500 USDT +
+
+ Earned Fees: + $128.75 +
+
+ APY: + 15.8% +
+
+ + + +
+
+
+
+
+
+
+
+
+ + +
+
+
+
+ Create New Liquidity Pool +
+
+
+
+
+
+ + +
+
+ +
+ + TFT +
+
+
+
+
+ + +
+
+ +
+ + ZAZ +
+
+
+
+
+ +
+ 1 + TFT + = + + ZAZ +
+
+
+ + +
This fee is charged on each trade and distributed to liquidity providers.
+
+
+ +
+
+
+
+
+
+
+ + +
+
+
+
+
About Swapping
+

Swap allows you to exchange one token for another at the current market rate. Swaps are executed through liquidity pools with a small fee that goes to liquidity providers.

+
+
+
+ +
+
+ +
+
+ Swap Tokens +
+
+ +
+ + +
+ + +
+ +
+ + +
+ +
+
+ +
+ Balance: 5,000 ZAZ + ≈ $2,500.00 +
+
+
+
+ + +
+
+
+ Exchange Rate: + 1 TFT = 0.5 ZAZ +
+
+ Minimum Received: + 0 ZAZ +
+
+ Price Impact: + < 0.1% +
+
+ Liquidity Provider Fee: + 0.3% +
+
+
+ + +
+ +
+
+
+
+ +
+ +
+
+ Recent Swaps +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TimeFromToValue
2025-04-15 14:32 +
+ TFT + 500 TFT +
+
+
+ ZAZ + 250 ZAZ +
+
$250.00
2025-04-14 09:17 +
+ USDT + 1,000 USDT +
+
+
+ TFT + 2,000 TFT +
+
$1,000.00
2025-04-12 16:45 +
+ ZAZ + 100 ZAZ +
+
+
+ USDT + 50 USDT +
+
$50.00
+
+
+
+ + +
+
+ Market Rates +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
PairRate24h ChangeVolume (24h)
+
+
+ TFT + ZAZ +
+ TFT/ZAZ +
+
0.5+2.3%$125,000
+
+
+ TFT + USDT +
+ TFT/USDT +
+
0.5-1.2%$250,000
+
+
+ ZAZ + USDT +
+ ZAZ/USDT +
+
0.5+3.7%$175,000
+
+
+
+
+
+
+ + +
+
+
+
+
About Collateralization
+

Use your digital assets as collateral to secure loans or generate synthetic assets. Maintain a healthy collateral ratio to avoid liquidation.

+
+
+
+ +
+
+ +
+
+ Collateralize Assets +
+
+
+ +
+ + +
+ + +
+ +
+ + TFT +
+
+ Available: 10,000 TFT ($5,000) +
+
+ + +
+ +
+ $ + +
+
+ + +
+ + +
+ + +
+ + +
+ + +
+ +
+ $ + + +
+
+ Maximum Loan: $0.00 +
+
+ + + + + + + + +
+ +
+ + + + +
+
+ + +
+ +
+ $ + + per TFT +
+
+ Your collateral will be liquidated if the price falls below this level. +
+
+ + +
+ +
+
+
+
+
+ +
+ +
+
+ Your Active Collateral Positions +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
AssetCollateral ValueBorrowed/GeneratedCollateral RatioLiquidation PriceStatusActions
+
+ TFT + 2,000 TFT +
+
$1,000$700 (Loan) +
+
+
+
+ 143% +
+
$0.35Healthy +
+ + +
+
+
+ + Beach Property NFT +
+
$25,00010,000 sUSD +
+
+
+
+ 250% +
+
$10,000Warning +
+ + +
+
+
+ ZAZ + 1,000 ZAZ +
+
$5000.1 sBTC +
+
+
+
+ 333% +
+
$0.15Healthy +
+ + +
+
+
+
+
+ + +
+
+ Collateral Health +
+
+
+
Overall Collateral Health
+
+
60%
+
+
+ Health score represents the overall safety of your collateral positions. Higher is better. +
+
+ +
+
+
+
+
Total Collateral Value
+

$26,500

+
+
+
+
+
+
+
Total Borrowed/Generated
+

$11,150

+
+
+
+
+ +
+ Your Beach Property NFT collateral is close to the liquidation threshold. Consider adding more collateral or repaying part of your synthetic assets. +
+
+
+
+
+
+ + +
+
+
+
+
About Staking
+

Staking allows you to lock your digital assets for a period of time to support network operations and earn rewards. The longer you stake, the higher rewards you can earn.

+
+
+
+ + +
+
+
+
+ Available Staking Options +
+
+
+ +
+
+
+
+ TFT +
ThreeFold Token (TFT)
+
+
+
+
+ Total Staked: + 5,250,000 TFT +
+
+ Your Stake: + 1,000 TFT +
+
+ APY: + 8.5% +
+ +
+ + +
+ +
+ +
+ + TFT +
+
+ +
+
+ Estimated Rewards: + 0 TFT +
+
+ +
+ +
+
+
+
+ + +
+
+
+
+ ZAZ +
Zanzibar Token (ZAZ)
+
+
+
+
+ Total Staked: + 2,750,000 ZAZ +
+
+ Your Stake: + 500 ZAZ +
+
+ APY: + 12.0% +
+ +
+ + +
+ +
+ +
+ + ZAZ +
+
+ +
+
+ Estimated Rewards: + 0 ZAZ +
+
+ +
+ +
+
+
+
+ + +
+
+
+
+ +
Digital Asset Staking
+
+
+
+

Stake your NFTs and other digital assets to earn passive income.

+ +
+ + +
+ +
+ + +
+ +
+
+ Estimated Rewards: + $0.00 +
+
+ Reward Token: + ZAZ +
+
+ +
+ +
+
+
+
+
+
+
+
+
+ + +
+
+
+
+ Your Active Stakes +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
AssetAmountValueStart DateEnd DateAPYEarned RewardsStatusActions
+
+ TFT + ThreeFold Token (TFT) +
+
1,000 TFT$5002025-03-152025-06-1510.2%22.5 TFTActive + +
+
+ ZAZ + Zanzibar Token (ZAZ) +
+
500 ZAZ$2502025-04-012025-05-0112.0%5.0 ZAZActive + +
+
+ + Beach Property NFT +
+
1 NFT$25,0002025-02-102026-02-1010.0%450 ZAZActive + +
+
+
+
+
+
+
+
+
+
+
@@ -192,3 +1697,6 @@
{% endblock %} + + + diff --git a/actix_mvc_app/static/js/defi.js b/actix_mvc_app/static/js/defi.js new file mode 100644 index 0000000..6c7bdc8 --- /dev/null +++ b/actix_mvc_app/static/js/defi.js @@ -0,0 +1,571 @@ +// DeFi Platform JavaScript Functionality + +document.addEventListener('DOMContentLoaded', function() { + // Initialize tooltips + var tooltipTriggerList = [].slice.call(document.querySelectorAll('[data-bs-toggle="tooltip"]')); + var tooltipList = tooltipTriggerList.map(function (tooltipTriggerEl) { + return new bootstrap.Tooltip(tooltipTriggerEl); + }); + + // =============== LENDING & BORROWING TAB =============== + // Lending form calculations + const lendingAmountInput = document.getElementById('lendingAmount'); + const lendingAssetSelect = document.getElementById('lendingAsset'); + const lendingTermSelect = document.getElementById('lendingTerm'); + const estimatedReturnsElement = document.getElementById('estimatedReturns'); + const totalReturnElement = document.getElementById('totalReturn'); + + if (lendingAmountInput && lendingAssetSelect && lendingTermSelect) { + const calculateLendingReturns = () => { + const amount = parseFloat(lendingAmountInput.value) || 0; + const asset = lendingAssetSelect.value; + const termDays = parseInt(lendingTermSelect.value) || 30; + + // Get APY from the selected option's text + const selectedOption = lendingTermSelect.options[lendingTermSelect.selectedIndex]; + const apyMatch = selectedOption.text.match(/\((\d+\.\d+)%\)/); + const apy = apyMatch ? parseFloat(apyMatch[1]) / 100 : 0.05; // Default to 5% if not found + + // Calculate returns (simple interest for demonstration) + const returns = amount * apy * (termDays / 365); + const total = amount + returns; + + if (estimatedReturnsElement) { + estimatedReturnsElement.textContent = returns.toFixed(2) + ' ' + asset; + } + if (totalReturnElement) { + totalReturnElement.textContent = total.toFixed(2) + ' ' + asset; + } + }; + + lendingAmountInput.addEventListener('input', calculateLendingReturns); + lendingAssetSelect.addEventListener('change', calculateLendingReturns); + lendingTermSelect.addEventListener('change', calculateLendingReturns); + } + + // Borrowing form calculations + const borrowingAmountInput = document.getElementById('borrowingAmount'); + const borrowingAssetSelect = document.getElementById('borrowingAsset'); + const borrowingTermSelect = document.getElementById('borrowingTerm'); + const borrowingCollateralSelect = document.getElementById('collateralAsset'); + const borrowingCollateralAmountInput = document.getElementById('collateralAmount'); + const interestDueElement = document.getElementById('interestDue'); + const totalRepaymentElement = document.getElementById('totalRepayment'); + const borrowingCollateralRatioElement = document.getElementById('collateralRatio'); + + if (borrowingAmountInput && borrowingAssetSelect && borrowingCollateralSelect && borrowingCollateralAmountInput) { + const calculateBorrowingDetails = () => { + const amount = parseFloat(borrowingAmountInput.value) || 0; + const asset = borrowingAssetSelect.value; + const termDays = parseInt(borrowingTermSelect.value) || 30; + + // Get APR from the selected option's text + const selectedOption = borrowingTermSelect.options[borrowingTermSelect.selectedIndex]; + const aprMatch = selectedOption.text.match(/\((\d+\.\d+)%\)/); + const apr = aprMatch ? parseFloat(aprMatch[1]) / 100 : 0.08; // Default to 8% if not found + + // Calculate interest and total repayment + const interest = amount * apr * (termDays / 365); + const total = amount + interest; + + if (interestDueElement) { + interestDueElement.textContent = interest.toFixed(2) + ' ' + asset; + } + if (totalRepaymentElement) { + totalRepaymentElement.textContent = total.toFixed(2) + ' ' + asset; + } + + // Calculate collateral ratio + const collateralAmount = parseFloat(borrowingCollateralAmountInput.value) || 0; + const collateralAsset = borrowingCollateralSelect.value; + let collateralValue = 0; + + // Mock prices for demonstration + const assetPrices = { + 'TFT': 0.5, + 'ZAZ': 0.5, + 'USDT': 1.0 + }; + + if (collateralAsset in assetPrices) { + collateralValue = collateralAmount * assetPrices[collateralAsset]; + } else { + // For other assets, assume the value is the amount (simplified) + collateralValue = collateralAmount; + } + + const borrowValue = amount * (asset === 'USDT' ? 1 : assetPrices[asset] || 0.5); + const ratio = borrowValue > 0 ? (collateralValue / borrowValue * 100) : 0; + + if (borrowingCollateralRatioElement) { + borrowingCollateralRatioElement.textContent = ratio.toFixed(0) + '%'; + + // Update color based on ratio + if (ratio >= 200) { + borrowingCollateralRatioElement.className = 'text-success'; + } else if (ratio >= 150) { + borrowingCollateralRatioElement.className = 'text-warning'; + } else { + borrowingCollateralRatioElement.className = 'text-danger'; + } + } + }; + + borrowingAmountInput.addEventListener('input', calculateBorrowingDetails); + borrowingAssetSelect.addEventListener('change', calculateBorrowingDetails); + borrowingTermSelect.addEventListener('change', calculateBorrowingDetails); + borrowingCollateralSelect.addEventListener('change', calculateBorrowingDetails); + borrowingCollateralAmountInput.addEventListener('input', calculateBorrowingDetails); + } + + // =============== LIQUIDITY POOLS TAB =============== + // Add Liquidity form calculations + const poolSelect = document.getElementById('liquidityPool'); + const token1AmountInput = document.getElementById('token1Amount'); + const token2AmountInput = document.getElementById('token2Amount'); + const lpTokensElement = document.getElementById('lpTokensReceived'); + const poolShareElement = document.getElementById('poolShare'); + + if (poolSelect && token1AmountInput && token2AmountInput) { + const calculateLiquidityDetails = () => { + const token1Amount = parseFloat(token1AmountInput.value) || 0; + const token2Amount = parseFloat(token2AmountInput.value) || 0; + + // Mock calculations for demonstration + const lpTokens = Math.sqrt(token1Amount * token2Amount); + const poolShare = token1Amount > 0 ? (lpTokens / (lpTokens + 1000) * 100) : 0; + + if (lpTokensElement) { + lpTokensElement.textContent = lpTokens.toFixed(2); + } + if (poolShareElement) { + poolShareElement.textContent = poolShare.toFixed(2) + '%'; + } + }; + + token1AmountInput.addEventListener('input', calculateLiquidityDetails); + token2AmountInput.addEventListener('input', calculateLiquidityDetails); + + // Handle pool selection to update token labels + poolSelect.addEventListener('change', function() { + const selectedOption = poolSelect.options[poolSelect.selectedIndex]; + const token1Label = document.getElementById('token1Label'); + const token2Label = document.getElementById('token2Label'); + + if (selectedOption.value === 'tft-zaz') { + if (token1Label) token1Label.textContent = 'TFT'; + if (token2Label) token2Label.textContent = 'ZAZ'; + } else if (selectedOption.value === 'zaz-usdt') { + if (token1Label) token1Label.textContent = 'ZAZ'; + if (token2Label) token2Label.textContent = 'USDT'; + } + + calculateLiquidityDetails(); + }); + } + + // =============== STAKING TAB =============== + // TFT Staking calculations + const tftStakeAmountInput = document.getElementById('tftStakeAmount'); + const tftStakingPeriodSelect = document.getElementById('tftStakingPeriod'); + const tftEstimatedRewardsElement = document.getElementById('tftEstimatedRewards'); + + if (tftStakeAmountInput && tftStakingPeriodSelect && tftEstimatedRewardsElement) { + const calculateTftStakingRewards = () => { + const amount = parseFloat(tftStakeAmountInput.value) || 0; + const termDays = parseInt(tftStakingPeriodSelect.value) || 30; + + // Get APY from the selected option's text + const selectedOption = tftStakingPeriodSelect.options[tftStakingPeriodSelect.selectedIndex]; + const apyMatch = selectedOption.text.match(/\((\d+\.\d+)%\)/); + const apy = apyMatch ? parseFloat(apyMatch[1]) / 100 : 0.085; // Default to 8.5% if not found + + // Calculate rewards (simple interest for demonstration) + const rewards = amount * apy * (termDays / 365); + + tftEstimatedRewardsElement.textContent = rewards.toFixed(2) + ' TFT'; + }; + + tftStakeAmountInput.addEventListener('input', calculateTftStakingRewards); + tftStakingPeriodSelect.addEventListener('change', calculateTftStakingRewards); + } + + // ZAZ Staking calculations + const zazStakeAmountInput = document.getElementById('zazStakeAmount'); + const zazStakingPeriodSelect = document.getElementById('zazStakingPeriod'); + const zazEstimatedRewardsElement = document.getElementById('zazEstimatedRewards'); + + if (zazStakeAmountInput && zazStakingPeriodSelect && zazEstimatedRewardsElement) { + const calculateZazStakingRewards = () => { + const amount = parseFloat(zazStakeAmountInput.value) || 0; + const termDays = parseInt(zazStakingPeriodSelect.value) || 30; + + // Get APY from the selected option's text + const selectedOption = zazStakingPeriodSelect.options[zazStakingPeriodSelect.selectedIndex]; + const apyMatch = selectedOption.text.match(/\((\d+\.\d+)%\)/); + const apy = apyMatch ? parseFloat(apyMatch[1]) / 100 : 0.12; // Default to 12% if not found + + // Calculate rewards (simple interest for demonstration) + const rewards = amount * apy * (termDays / 365); + + zazEstimatedRewardsElement.textContent = rewards.toFixed(2) + ' ZAZ'; + }; + + zazStakeAmountInput.addEventListener('input', calculateZazStakingRewards); + zazStakingPeriodSelect.addEventListener('change', calculateZazStakingRewards); + } + + // Asset Staking calculations + const assetStakingSelect = document.getElementById('assetStaking'); + const assetStakingPeriodSelect = document.getElementById('assetStakingPeriod'); + const assetEstimatedRewardsElement = document.getElementById('assetEstimatedRewards'); + + if (assetStakingSelect && assetStakingPeriodSelect && assetEstimatedRewardsElement) { + const calculateAssetStakingRewards = () => { + const selectedOption = assetStakingSelect.options[assetStakingSelect.selectedIndex]; + if (selectedOption.value === '') return; + + const assetValue = parseFloat(selectedOption.dataset.value) || 0; + const termDays = parseInt(assetStakingPeriodSelect.value) || 30; + + // Get APY from the selected option's text + const periodOption = assetStakingPeriodSelect.options[assetStakingPeriodSelect.selectedIndex]; + const apyMatch = periodOption.text.match(/\((\d+\.\d+)%\)/); + const apy = apyMatch ? parseFloat(apyMatch[1]) / 100 : 0.035; // Default to 3.5% if not found + + // Calculate rewards in USD (simple interest for demonstration) + const rewards = assetValue * apy * (termDays / 365); + + assetEstimatedRewardsElement.textContent = '$' + rewards.toFixed(2); + }; + + assetStakingSelect.addEventListener('change', calculateAssetStakingRewards); + assetStakingPeriodSelect.addEventListener('change', calculateAssetStakingRewards); + } + + // =============== SWAP TAB =============== + // Token swap calculations + const swapFromAmountInput = document.getElementById('swapFromAmount'); + const swapToAmountElement = document.getElementById('swapToAmount'); + const fromTokenDropdown = document.getElementById('fromTokenDropdown'); + const toTokenDropdown = document.getElementById('toTokenDropdown'); + const exchangeRateElement = document.getElementById('exchangeRate'); + const minimumReceivedElement = document.getElementById('minimumReceived'); + const priceImpactElement = document.getElementById('priceImpact'); + const swapDirectionButton = document.getElementById('swapDirectionButton'); + const maxFromButton = document.getElementById('maxFromButton'); + const fromTokenSymbolElement = document.getElementById('fromTokenSymbol'); + const toTokenSymbolElement = document.getElementById('toTokenSymbol'); + const fromTokenImgElement = document.getElementById('fromTokenImg'); + const toTokenImgElement = document.getElementById('toTokenImg'); + const fromTokenBalanceElement = document.getElementById('fromTokenBalance'); + const toTokenBalanceElement = document.getElementById('toTokenBalance'); + + // Mock token data + const tokenData = { + 'TFT': { price: 0.5, balance: '10,000 TFT', usdValue: '5,000.00' }, + 'ZAZ': { price: 0.5, balance: '5,000 ZAZ', usdValue: '2,500.00' }, + 'USDT': { price: 1.0, balance: '2,500 USDT', usdValue: '2,500.00' } + }; + + if (swapFromAmountInput && swapToAmountElement) { + let fromToken = 'TFT'; + let toToken = 'ZAZ'; + + const calculateSwap = () => { + const fromAmount = parseFloat(swapFromAmountInput.value) || 0; + + // Calculate exchange rate + const fromPrice = tokenData[fromToken].price; + const toPrice = tokenData[toToken].price; + const rate = fromPrice / toPrice; + + // Calculate to amount + const toAmount = fromAmount * rate; + + // Update UI + swapToAmountElement.value = toAmount.toFixed(2); + + if (exchangeRateElement) { + exchangeRateElement.textContent = `1 ${fromToken} = ${rate.toFixed(4)} ${toToken}`; + } + + if (minimumReceivedElement) { + // 0.5% slippage for demonstration + const minReceived = toAmount * 0.995; + minimumReceivedElement.textContent = `${minReceived.toFixed(2)} ${toToken}`; + } + + if (priceImpactElement) { + // Mock price impact calculation + const impact = fromAmount > 1000 ? '0.5%' : '< 0.1%'; + priceImpactElement.textContent = impact; + priceImpactElement.className = fromAmount > 1000 ? 'text-warning' : 'text-success'; + } + }; + + // Initialize from token dropdown items + const fromTokenItems = document.querySelectorAll('[aria-labelledby="fromTokenDropdown"] .dropdown-item'); + fromTokenItems.forEach(item => { + item.addEventListener('click', function(e) { + e.preventDefault(); + fromToken = this.dataset.token; + fromTokenSymbolElement.textContent = fromToken; + fromTokenImgElement.src = this.dataset.img; + fromTokenBalanceElement.textContent = tokenData[fromToken].balance; + calculateSwap(); + }); + }); + + // Initialize to token dropdown items + const toTokenItems = document.querySelectorAll('[aria-labelledby="toTokenDropdown"] .dropdown-item'); + toTokenItems.forEach(item => { + item.addEventListener('click', function(e) { + e.preventDefault(); + toToken = this.dataset.token; + toTokenSymbolElement.textContent = toToken; + toTokenImgElement.src = this.dataset.img; + toTokenBalanceElement.textContent = tokenData[toToken].balance; + calculateSwap(); + }); + }); + + // Swap direction button + if (swapDirectionButton) { + swapDirectionButton.addEventListener('click', function() { + // Swap tokens + const tempToken = fromToken; + fromToken = toToken; + toToken = tempToken; + + // Update UI + fromTokenSymbolElement.textContent = fromToken; + toTokenSymbolElement.textContent = toToken; + + const tempImg = fromTokenImgElement.src; + fromTokenImgElement.src = toTokenImgElement.src; + toTokenImgElement.src = tempImg; + + fromTokenBalanceElement.textContent = tokenData[fromToken].balance; + toTokenBalanceElement.textContent = tokenData[toToken].balance; + + // Swap amounts + const tempAmount = swapFromAmountInput.value; + swapFromAmountInput.value = swapToAmountElement.value; + + calculateSwap(); + }); + } + + // Max button + if (maxFromButton) { + maxFromButton.addEventListener('click', function() { + // Set max amount based on token balance + const balance = parseInt(tokenData[fromToken].balance.split(' ')[0].replace(/,/g, '')); + swapFromAmountInput.value = balance; + calculateSwap(); + }); + } + + swapFromAmountInput.addEventListener('input', calculateSwap); + + // Initial calculation + calculateSwap(); + } + + // =============== COLLATERAL TAB =============== + // Collateral form calculations + const collateralAssetSelect = document.getElementById('collateralAsset'); + const collateralAmountInput = document.getElementById('collateralAmount'); + const collateralValueElement = document.getElementById('collateralValue'); + const collateralUnitElement = document.getElementById('collateralUnit'); + const collateralAvailableElement = document.getElementById('collateralAvailable'); + const collateralAvailableUSDElement = document.getElementById('collateralAvailableUSD'); + const collateralPurposeSelect = document.getElementById('collateralPurpose'); + const loanTermGroup = document.getElementById('loanTermGroup'); + const loanAmountGroup = document.getElementById('loanAmountGroup'); + const syntheticAssetGroup = document.getElementById('syntheticAssetGroup'); + const syntheticAmountGroup = document.getElementById('syntheticAmountGroup'); + const loanAmountInput = document.getElementById('loanAmount'); + const maxLoanAmountElement = document.getElementById('maxLoanAmount'); + const syntheticAmountInput = document.getElementById('syntheticAmount'); + const maxSyntheticAmountElement = document.getElementById('maxSyntheticAmount'); + const collateralRatioElement = document.getElementById('collateralRatio'); + const liquidationPriceElement = document.getElementById('liquidationPrice'); + const liquidationUnitElement = document.getElementById('liquidationUnit'); + + if (collateralAssetSelect && collateralAmountInput) { + const calculateCollateralDetails = () => { + if (collateralAssetSelect.selectedIndex === 0) return; + + const selectedOption = collateralAssetSelect.options[collateralAssetSelect.selectedIndex]; + const assetType = selectedOption.dataset.type; + const assetValue = parseFloat(selectedOption.dataset.value) || 0; + const assetAmount = parseFloat(selectedOption.dataset.amount) || 0; + const assetUnit = selectedOption.dataset.unit || ''; + + // Update UI with asset details + if (collateralUnitElement) collateralUnitElement.textContent = assetUnit; + if (collateralAvailableElement) collateralAvailableElement.textContent = assetAmount.toLocaleString() + ' ' + assetUnit; + if (collateralAvailableUSDElement) collateralAvailableUSDElement.textContent = '$' + assetValue.toLocaleString(); + if (liquidationUnitElement) liquidationUnitElement.textContent = assetUnit; + + // Calculate collateral value + const amount = parseFloat(collateralAmountInput.value) || 0; + let collateralValue = 0; + + if (assetType === 'token') { + // For tokens, calculate based on token price + const tokenPrice = assetValue / assetAmount; + collateralValue = amount * tokenPrice; + } else { + // For NFTs and other assets, use the full value if amount is 1 + collateralValue = amount === 1 ? assetValue : 0; + } + + if (collateralValueElement) collateralValueElement.value = collateralValue.toFixed(2); + + // Calculate max loan amount (75% of collateral value) + const maxLoanAmount = collateralValue * 0.75; + if (maxLoanAmountElement) maxLoanAmountElement.textContent = maxLoanAmount.toFixed(2); + + // Calculate max synthetic amount (50% of collateral value) + const maxSyntheticAmount = collateralValue * 0.5; + if (maxSyntheticAmountElement) maxSyntheticAmountElement.textContent = maxSyntheticAmount.toFixed(2); + + // Calculate collateral ratio and liquidation price + updateCollateralRatio(); + }; + + const updateCollateralRatio = () => { + const collateralValue = parseFloat(collateralValueElement.value) || 0; + const purpose = collateralPurposeSelect.value; + + let borrowedValue = 0; + if (purpose === 'loan') { + borrowedValue = parseFloat(loanAmountInput.value) || 0; + } else if (purpose === 'synthetic') { + borrowedValue = parseFloat(syntheticAmountInput.value) || 0; + } else { + // For leverage trading, assume 2x leverage + borrowedValue = collateralValue; + } + + // Calculate ratio + const ratio = borrowedValue > 0 ? (collateralValue / borrowedValue * 100) : 0; + + if (collateralRatioElement) { + collateralRatioElement.value = ratio.toFixed(0) + '%'; + } + + // Calculate liquidation price + if (liquidationPriceElement) { + const selectedOption = collateralAssetSelect.options[collateralAssetSelect.selectedIndex]; + if (selectedOption.selectedIndex === 0) return; + + const assetType = selectedOption.dataset.type; + const assetValue = parseFloat(selectedOption.dataset.value) || 0; + const assetAmount = parseFloat(selectedOption.dataset.amount) || 0; + const collateralAmount = parseFloat(collateralAmountInput.value) || 0; + + if (assetType === 'token' && collateralAmount > 0) { + const currentPrice = assetValue / assetAmount; + const liquidationThreshold = purpose === 'loan' ? 1.2 : 1.5; // 120% for loans, 150% for synthetic + const liquidationPrice = (borrowedValue / collateralAmount) * liquidationThreshold; + liquidationPriceElement.value = liquidationPrice.toFixed(4); + } else { + liquidationPriceElement.value = (borrowedValue * 1.2).toFixed(2); + } + } + }; + + // Handle collateral asset selection + collateralAssetSelect.addEventListener('change', function() { + collateralAmountInput.value = ''; + calculateCollateralDetails(); + }); + + // Handle collateral amount input + collateralAmountInput.addEventListener('input', calculateCollateralDetails); + + // Handle purpose selection + collateralPurposeSelect.addEventListener('change', function() { + const purpose = collateralPurposeSelect.value; + + // Show/hide relevant form groups + if (loanTermGroup) loanTermGroup.style.display = purpose === 'loan' ? 'block' : 'none'; + if (loanAmountGroup) loanAmountGroup.style.display = purpose === 'loan' ? 'block' : 'none'; + if (syntheticAssetGroup) syntheticAssetGroup.style.display = purpose === 'synthetic' ? 'block' : 'none'; + if (syntheticAmountGroup) syntheticAmountGroup.style.display = purpose === 'synthetic' ? 'block' : 'none'; + + updateCollateralRatio(); + }); + + // Handle loan amount input + if (loanAmountInput) { + loanAmountInput.addEventListener('input', updateCollateralRatio); + + // Max loan button + const maxLoanButton = document.getElementById('maxLoanButton'); + if (maxLoanButton) { + maxLoanButton.addEventListener('click', function() { + const maxLoan = parseFloat(maxLoanAmountElement.textContent) || 0; + loanAmountInput.value = maxLoan.toFixed(2); + updateCollateralRatio(); + }); + } + } + + // Handle synthetic amount input + if (syntheticAmountInput) { + syntheticAmountInput.addEventListener('input', updateCollateralRatio); + + // Max synthetic button + const maxSyntheticButton = document.getElementById('maxSyntheticButton'); + if (maxSyntheticButton) { + maxSyntheticButton.addEventListener('click', function() { + const maxSynthetic = parseFloat(maxSyntheticAmountElement.textContent) || 0; + syntheticAmountInput.value = maxSynthetic.toFixed(2); + updateCollateralRatio(); + }); + } + } + + // Handle synthetic asset selection + const syntheticAssetSelect = document.getElementById('syntheticAsset'); + const syntheticUnitElement = document.getElementById('syntheticUnit'); + const maxSyntheticUnitElement = document.getElementById('maxSyntheticUnit'); + + if (syntheticAssetSelect && syntheticUnitElement && maxSyntheticUnitElement) { + syntheticAssetSelect.addEventListener('change', function() { + const asset = syntheticAssetSelect.value; + syntheticUnitElement.textContent = asset; + maxSyntheticUnitElement.textContent = asset; + }); + } + } + + // Initialize tab functionality if not already handled by Bootstrap + const tabLinks = document.querySelectorAll('.nav-link[data-bs-toggle="tab"]'); + tabLinks.forEach(tabLink => { + tabLink.addEventListener('click', function(e) { + e.preventDefault(); + const targetId = this.getAttribute('href'); + const targetTab = document.querySelector(targetId); + + // Hide all tabs + document.querySelectorAll('.tab-pane').forEach(tab => { + tab.classList.remove('show', 'active'); + }); + + // Show the target tab + if (targetTab) { + targetTab.classList.add('show', 'active'); + } + + // Update active state on nav links + tabLinks.forEach(link => link.classList.remove('active')); + this.classList.add('active'); + }); + }); +});