mirror of
https://github.com/pezkuwichain/pwap.git
synced 2026-04-22 04:27:56 +00:00
feat: Update pool dashboard to display PEZ/wUSDT pool
- Switch pool display from wHEZ/PEZ to PEZ/wUSDT - Fix wUSDT decimal conversion (1e6 instead of 1e12) - Update TokenSwap to support beta testnet endpoint - Add wallet reconnection on network change - Update API endpoint to wss://beta.pezkuwichain.com 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -1,436 +0,0 @@
|
||||
# DEX System Improvements - User Guide
|
||||
|
||||
## Overview
|
||||
|
||||
This document provides a comprehensive guide to the newly implemented DEX improvements for PezkuwiChain beta testnet.
|
||||
|
||||
---
|
||||
|
||||
## 🆕 What's New
|
||||
|
||||
### 1. Pool Monitoring Dashboard ✨
|
||||
- Real-time pool metrics
|
||||
- LP position tracking
|
||||
- Impermanent loss calculator
|
||||
- APR estimations
|
||||
|
||||
### 2. Arbitrage Bot 🤖
|
||||
- Automated price monitoring
|
||||
- Smart arbitrage execution
|
||||
- Pool balance maintenance
|
||||
|
||||
### 3. Enhanced Swap Interface 📈
|
||||
- Price impact visualization
|
||||
- Slippage tolerance controls
|
||||
- Minimum received calculations
|
||||
|
||||
---
|
||||
|
||||
## 📊 Pool Dashboard
|
||||
|
||||
### Access
|
||||
Navigate to: **http://localhost:5173/pool** (after login)
|
||||
|
||||
### Features
|
||||
|
||||
#### Canlı Metrikler (Live Metrics)
|
||||
```
|
||||
┌─────────────────────────────────────────┐
|
||||
│ Total Liquidity │ Current Price │
|
||||
│ $200,000 │ 1 HEZ = 5.0000 PEZ │
|
||||
├─────────────────────────────────────────┤
|
||||
│ Estimated APR │ Constant (k) │
|
||||
│ 109.50% │ 50.0B │
|
||||
└─────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
#### Pool Reserves Tab
|
||||
- View wHEZ reserve
|
||||
- View PEZ reserve
|
||||
- See AMM formula: x × y = k
|
||||
|
||||
#### Your Position Tab (LP'ler için)
|
||||
- LP token balance
|
||||
- Pool share percentage
|
||||
- Token values
|
||||
- Estimated earnings (daily/monthly/yearly)
|
||||
|
||||
#### Impermanent Loss Calculator
|
||||
Calculate potential IL at different price changes:
|
||||
- +10%: -0.05% loss
|
||||
- +25%: -0.62% loss
|
||||
- +50%: -2.02% loss
|
||||
- +100%: -5.72% loss
|
||||
- +200%: -13.40% loss
|
||||
|
||||
---
|
||||
|
||||
## 🔄 Token Swap Improvements
|
||||
|
||||
### Price Impact Display
|
||||
|
||||
Swap interface now shows:
|
||||
- **Green** (<1%): Excellent swap
|
||||
- **Yellow** (1-5%): Good swap
|
||||
- **Red** (>5%): High impact warning
|
||||
|
||||
### Slippage Tolerance
|
||||
|
||||
Default: **0.5%**
|
||||
|
||||
Customize: 0.1% | 0.5% | 1% | Custom
|
||||
|
||||
**Recommendation:**
|
||||
- Normal: 0.5-1%
|
||||
- Volatile markets: 2-5%
|
||||
- Large swaps: 5-10%
|
||||
|
||||
### Minimum Received
|
||||
|
||||
System automatically calculates minimum tokens you'll receive after slippage:
|
||||
|
||||
```
|
||||
Input: 1000 HEZ
|
||||
Expected: 4850.62 PEZ
|
||||
Min Received (0.5% slip): 4826.37 PEZ
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🤖 Arbitrage Bot Usage
|
||||
|
||||
### Purpose
|
||||
Maintains pool price balance by executing arbitrage trades when price deviates from reference.
|
||||
|
||||
### Installation
|
||||
|
||||
```bash
|
||||
cd /home/mamostehp/Pezkuwi-SDK
|
||||
|
||||
# Ensure substrate-interface is installed
|
||||
pip3 install substrate-interface --break-system-packages
|
||||
```
|
||||
|
||||
### Configuration
|
||||
|
||||
Edit `/home/mamostehp/Pezkuwi-SDK/scripts/arbitrage_bot.py`:
|
||||
|
||||
```python
|
||||
CONFIG = {
|
||||
'ws_url': 'ws://127.0.0.1:9944', # Testnet endpoint
|
||||
'reference_price': 5.0, # Target: 1 HEZ = 5 PEZ
|
||||
'min_profit_percent': 2.0, # Min 2% deviation to trade
|
||||
'max_swap_amount_hez': 5000, # Max 5K HEZ per trade
|
||||
'check_interval': 30, # Check every 30 seconds
|
||||
'slippage_tolerance': 0.05, # 5% slippage
|
||||
}
|
||||
```
|
||||
|
||||
### Running the Bot
|
||||
|
||||
**Foreground (for testing):**
|
||||
```bash
|
||||
python3 ./scripts/arbitrage_bot.py
|
||||
```
|
||||
|
||||
**Background (production):**
|
||||
```bash
|
||||
nohup python3 ./scripts/arbitrage_bot.py > /tmp/arb-bot.log 2>&1 &
|
||||
|
||||
# Monitor logs
|
||||
tail -f /tmp/arb-bot.log
|
||||
```
|
||||
|
||||
### Bot Output Example
|
||||
|
||||
```
|
||||
======================================================================
|
||||
🤖 Arbitrage Bot Started
|
||||
======================================================================
|
||||
|
||||
⚙️ Configuration:
|
||||
Reference Price: 1 HEZ = 5.0 PEZ
|
||||
Min Profit: 2.0%
|
||||
Max Swap: 5000 HEZ
|
||||
Check Interval: 30s
|
||||
|
||||
──────────────────────────────────────────────────────────────────────
|
||||
🔍 Check #1 - 2025-11-02 20:30:15
|
||||
──────────────────────────────────────────────────────────────────────
|
||||
📊 Pool Price: 1 HEZ = 5.1234 PEZ
|
||||
📊 Reference: 1 HEZ = 5.0000 PEZ
|
||||
📊 Deviation: +2.47%
|
||||
|
||||
💰 HEZ overpriced by 2.47% → Sell HEZ for PEZ
|
||||
|
||||
💡 Arbitrage opportunity detected!
|
||||
Expected profit: 2.47%
|
||||
|
||||
🔄 Executing arbitrage: HEZ_TO_PEZ
|
||||
Amount: 5000.00 tokens
|
||||
Step 1: Wrapping HEZ to wHEZ...
|
||||
Step 2: Swapping wHEZ to PEZ...
|
||||
✅ Swap successful! Block: 0x1234...
|
||||
|
||||
✨ Arbitrage executed successfully!
|
||||
Total trades: 1
|
||||
|
||||
💤 Sleeping for 30 seconds...
|
||||
```
|
||||
|
||||
### Bot Commands
|
||||
|
||||
**Check if running:**
|
||||
```bash
|
||||
ps aux | grep arbitrage_bot
|
||||
```
|
||||
|
||||
**Stop bot:**
|
||||
```bash
|
||||
pkill -f arbitrage_bot.py
|
||||
```
|
||||
|
||||
**View recent activity:**
|
||||
```bash
|
||||
tail -50 /tmp/arb-bot.log
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📈 Liquidity Mining (Future)
|
||||
|
||||
### Timeline
|
||||
6 weeks implementation (as per LIQUIDITY_MINING_PLAN.md)
|
||||
|
||||
### Expected Features
|
||||
- Stake LP tokens to earn HEZ rewards
|
||||
- 10 HEZ/block emission (~5M HEZ/year)
|
||||
- Target APR: 50-150%
|
||||
- Auto-compound options
|
||||
- Governance participation
|
||||
|
||||
### How to Prepare
|
||||
1. Add liquidity to wHEZ/PEZ pool
|
||||
2. Hold LP tokens in wallet
|
||||
3. Wait for liquidity mining launch announcement
|
||||
|
||||
---
|
||||
|
||||
## 🔐 Security Best Practices
|
||||
|
||||
### For Users
|
||||
1. **Always check price impact** before large swaps
|
||||
2. **Set appropriate slippage** (don't use 50% unless necessary)
|
||||
3. **Verify transaction details** in confirmation dialog
|
||||
4. **Start with small amounts** when testing
|
||||
|
||||
### For LP Providers
|
||||
1. **Understand impermanent loss** before adding liquidity
|
||||
2. **Monitor pool metrics** via dashboard
|
||||
3. **Calculate expected APR** vs IL risk
|
||||
4. **Diversify positions** across pools (when available)
|
||||
|
||||
### For Bot Operators
|
||||
1. **Use dedicated wallet** for bot (not main funds)
|
||||
2. **Monitor bot logs** regularly
|
||||
3. **Set reasonable limits** (max_swap_amount)
|
||||
4. **Test on testnet** before mainnet
|
||||
|
||||
---
|
||||
|
||||
## 🎯 Recommended Safe Swap Limits
|
||||
|
||||
Based on current pool liquidity (100K wHEZ + 500K PEZ):
|
||||
|
||||
| Swap Amount | Price Impact | Risk Level | Recommendation |
|
||||
|-------------|--------------|------------|----------------|
|
||||
| 0 - 1,000 HEZ | <1% | ✅ Low | Safe for all users |
|
||||
| 1,000 - 5,000 HEZ | 1-5% | ⚠️ Medium | Experienced users |
|
||||
| 5,000 - 11,500 HEZ | 5-10% | ⚠️ High | Split into smaller swaps |
|
||||
| 11,500+ HEZ | >10% | ❌ Very High | Not recommended |
|
||||
|
||||
**Pro Tip:** For swaps >10K HEZ, split into multiple 5K swaps over time.
|
||||
|
||||
---
|
||||
|
||||
## 📱 Quick Reference
|
||||
|
||||
### URLs
|
||||
|
||||
| Feature | URL | Requires Login |
|
||||
|---------|-----|----------------|
|
||||
| Home | http://localhost:5173 | No |
|
||||
| Swap | http://localhost:5173/wallet (Swap tab) | Yes |
|
||||
| Pool Dashboard | http://localhost:5173/pool | Yes |
|
||||
| Wallet | http://localhost:5173/wallet | Yes |
|
||||
|
||||
### Default Network Settings
|
||||
|
||||
```
|
||||
Network: PezkuwiChain Beta Testnet
|
||||
Endpoint: ws://127.0.0.1:9944
|
||||
Chain ID: pezkuwichain-beta
|
||||
Block Time: ~6 seconds
|
||||
```
|
||||
|
||||
### Token Info
|
||||
|
||||
| Token | Type | Asset ID | Symbol |
|
||||
|-------|------|----------|--------|
|
||||
| HEZ | Native | - | HEZ |
|
||||
| wHEZ | Wrapped | 0 | wHEZ |
|
||||
| PEZ | Utility | 1 | PEZ |
|
||||
|
||||
### Pool Info
|
||||
|
||||
| Pool | Assets | Liquidity | LP Fee |
|
||||
|------|--------|-----------|--------|
|
||||
| wHEZ/PEZ | 0 & 1 | 100K wHEZ + 500K PEZ | 3% |
|
||||
|
||||
---
|
||||
|
||||
## 🛠️ Troubleshooting
|
||||
|
||||
### Issue: Pool dashboard shows "No pool data"
|
||||
|
||||
**Solution:**
|
||||
1. Check beta testnet is running:
|
||||
```bash
|
||||
ps aux | grep pezkuwi
|
||||
```
|
||||
2. Check pool was initialized:
|
||||
```bash
|
||||
python3 /tmp/init_beta_pools.py
|
||||
```
|
||||
3. Ensure wallet is connected
|
||||
|
||||
### Issue: Swap fails with "1010 invalid transaction"
|
||||
|
||||
**Solution:**
|
||||
- This should be fixed now! Swap paths updated to simple arrays.
|
||||
- If still happens, check slippage tolerance (increase to 1-2%)
|
||||
- Verify account has sufficient balance + gas
|
||||
|
||||
### Issue: Arbitrage bot not trading
|
||||
|
||||
**Possible Causes:**
|
||||
1. Price deviation <2% (working as expected)
|
||||
2. Bot account has insufficient funds
|
||||
3. Pool liquidity exhausted
|
||||
4. Network connection issues
|
||||
|
||||
**Check:**
|
||||
```bash
|
||||
tail -f /tmp/arb-bot.log # See bot activity
|
||||
```
|
||||
|
||||
### Issue: High impermanent loss
|
||||
|
||||
**Understand:**
|
||||
- IL is inherent to AMMs
|
||||
- It's "impermanent" - can recover if price returns
|
||||
- LP fees offset IL over time
|
||||
- Calculator shows worst-case scenarios
|
||||
|
||||
**Mitigation:**
|
||||
- Add liquidity when volatility is low
|
||||
- Hold position long-term for fee accumulation
|
||||
- Monitor APR vs IL regularly
|
||||
|
||||
---
|
||||
|
||||
## 💡 Tips & Tricks
|
||||
|
||||
### For Traders
|
||||
1. **Use limit orders** (when implemented) for better prices
|
||||
2. **Split large swaps** to reduce price impact
|
||||
3. **Trade during high liquidity** periods
|
||||
4. **Monitor arbitrage bot** - trade after it corrects price
|
||||
|
||||
### For LPs
|
||||
1. **Add liquidity in balanced ratios** (current pool ratio)
|
||||
2. **Compound rewards** (when liquidity mining live)
|
||||
3. **Track impermanent loss** via dashboard
|
||||
4. **Calculate APR including fees**
|
||||
|
||||
### For Developers
|
||||
1. **Review pool analysis** in `/tmp/pool-analysis.md`
|
||||
2. **Check liquidity mining plan** in SDK docs
|
||||
3. **Use pool monitoring** for integration testing
|
||||
4. **Monitor bot behavior** before mainnet
|
||||
|
||||
---
|
||||
|
||||
## 📞 Support & Resources
|
||||
|
||||
### Documentation
|
||||
- **Main README**: `/home/mamostehp/DKSweb/README.md`
|
||||
- **Pool Analysis**: `/tmp/pool-analysis.md`
|
||||
- **LM Plan**: `/home/mamostehp/Pezkuwi-SDK/docs/LIQUIDITY_MINING_PLAN.md`
|
||||
|
||||
### Scripts
|
||||
- **Pool Init**: `/tmp/init_beta_pools.py`
|
||||
- **Arb Bot**: `/home/mamostehp/Pezkuwi-SDK/scripts/arbitrage_bot.py`
|
||||
- **Pool Check**: `/tmp/check-pool.mjs`
|
||||
|
||||
### Components
|
||||
- **PoolDashboard**: `/home/mamostehp/DKSweb/src/components/PoolDashboard.tsx`
|
||||
- **TokenSwap**: `/home/mamostehp/DKSweb/src/components/TokenSwap.tsx`
|
||||
|
||||
### Getting Help
|
||||
1. Check this document first
|
||||
2. Review error messages in console
|
||||
3. Check bot logs: `tail -f /tmp/arb-bot.log`
|
||||
4. Review transaction in Polkadot.js Apps
|
||||
|
||||
---
|
||||
|
||||
## 🚀 What's Next?
|
||||
|
||||
### Short Term (Done ✅)
|
||||
- [x] Pool monitoring dashboard
|
||||
- [x] Arbitrage bot
|
||||
- [x] Frontend improvements
|
||||
- [x] Documentation
|
||||
|
||||
### Medium Term (1-2 months)
|
||||
- [ ] Liquidity mining implementation
|
||||
- [ ] Multiple pool support
|
||||
- [ ] Advanced charting
|
||||
- [ ] Transaction history
|
||||
|
||||
### Long Term (3-6 months)
|
||||
- [ ] Governance integration
|
||||
- [ ] Cross-chain swaps
|
||||
- [ ] Limit orders
|
||||
- [ ] Mobile app
|
||||
|
||||
---
|
||||
|
||||
## ⚖️ Disclaimer
|
||||
|
||||
**Beta Testnet Warning:**
|
||||
- This is a **test environment**
|
||||
- Tokens have **NO real value**
|
||||
- Use for **testing purposes only**
|
||||
- Expect **occasional resets**
|
||||
- **Do not** use on mainnet without thorough testing
|
||||
|
||||
**Financial Advice:**
|
||||
- This is **NOT financial advice**
|
||||
- **DYOR** before any transactions
|
||||
- **Understand risks** of AMM trading
|
||||
- **Impermanent loss** is real
|
||||
- **Never invest** more than you can afford to lose
|
||||
|
||||
---
|
||||
|
||||
## 📄 License
|
||||
|
||||
MIT License - See project root for details
|
||||
|
||||
---
|
||||
|
||||
**Last Updated:** 2025-11-02
|
||||
**Version:** 1.0.0-beta
|
||||
**Network:** PezkuwiChain Beta Testnet
|
||||
-175
@@ -1,175 +0,0 @@
|
||||
import { ApiPromise, WsProvider } from '@polkadot/api';
|
||||
|
||||
async function main() {
|
||||
const wsProvider = new WsProvider('ws://127.0.0.1:9944');
|
||||
const api = await ApiPromise.create({ provider: wsProvider });
|
||||
|
||||
console.log('\n' + '='.repeat(70));
|
||||
console.log(' Beta Testnet - DEX Pool Status & Analysis');
|
||||
console.log('='.repeat(70) + '\n');
|
||||
|
||||
const ONE_TOKEN = BigInt(10 ** 12);
|
||||
|
||||
// Pool: wHEZ (0) / PEZ (1)
|
||||
const poolId = [0, 1];
|
||||
|
||||
console.log('📊 Pool: wHEZ (Asset 0) / PEZ (Asset 1)\n');
|
||||
|
||||
try {
|
||||
// Get pool info
|
||||
const poolInfo = await api.query.assetConversion.pools(poolId);
|
||||
|
||||
if (poolInfo.isSome) {
|
||||
const lpTokenId = poolInfo.unwrap().toString();
|
||||
console.log(`✅ Pool exists - LP Token ID: ${lpTokenId}\n`);
|
||||
|
||||
// Get pool account
|
||||
const poolAccount = await api.query.assetConversion.poolAccountIds(poolId);
|
||||
|
||||
if (poolAccount.isSome) {
|
||||
const poolAddr = poolAccount.unwrap().toString();
|
||||
console.log(`🏦 Pool Account: ${poolAddr}\n`);
|
||||
|
||||
// Get reserves
|
||||
const whezBalance = await api.query.assets.account(0, poolAddr);
|
||||
const pezBalance = await api.query.assets.account(1, poolAddr);
|
||||
|
||||
if (whezBalance.isSome && pezBalance.isSome) {
|
||||
const whezReserve = BigInt(whezBalance.unwrap().balance.toString());
|
||||
const pezReserve = BigInt(pezBalance.unwrap().balance.toString());
|
||||
|
||||
console.log('💰 Pool Reserves:');
|
||||
console.log(` wHEZ: ${(Number(whezReserve) / Number(ONE_TOKEN)).toLocaleString('en-US', { minimumFractionDigits: 2 })} wHEZ`);
|
||||
console.log(` PEZ: ${(Number(pezReserve) / Number(ONE_TOKEN)).toLocaleString('en-US', { minimumFractionDigits: 2 })} PEZ\n`);
|
||||
|
||||
// Calculate k (constant product)
|
||||
const k = whezReserve * pezReserve;
|
||||
console.log('💎 AMM Constant Product:');
|
||||
console.log(` k = ${(Number(whezReserve) / Number(ONE_TOKEN)).toFixed(2)} × ${(Number(pezReserve) / Number(ONE_TOKEN)).toFixed(2)} = ${(Number(k) / Number(ONE_TOKEN ** BigInt(2))).toLocaleString('en-US')}\n`);
|
||||
|
||||
// Current price
|
||||
const priceHezToPez = Number(pezReserve) / Number(whezReserve);
|
||||
const pricePezToHez = Number(whezReserve) / Number(pezReserve);
|
||||
|
||||
console.log('💱 Current Prices:');
|
||||
console.log(` 1 HEZ = ${priceHezToPez.toFixed(4)} PEZ`);
|
||||
console.log(` 1 PEZ = ${pricePezToHez.toFixed(6)} HEZ\n`);
|
||||
|
||||
// Swap simulation with 3% fee
|
||||
console.log('🔄 Swap Scenarios (3% LP Fee):');
|
||||
console.log('-'.repeat(70) + '\n');
|
||||
|
||||
function calculateSwap(amountIn, reserveIn, reserveOut) {
|
||||
// 3% fee: effective amount = amountIn * 0.97
|
||||
const amountInWithFee = amountIn * BigInt(97) / BigInt(100);
|
||||
|
||||
// AMM formula: (reserveIn + amountInWithFee) * (reserveOut - amountOut) = k
|
||||
// amountOut = (amountInWithFee * reserveOut) / (reserveIn + amountInWithFee)
|
||||
const numerator = amountInWithFee * reserveOut;
|
||||
const denominator = reserveIn + amountInWithFee;
|
||||
const amountOut = numerator / denominator;
|
||||
|
||||
// Price impact
|
||||
const priceImpact = Number(amountOut) / Number(reserveOut) * 100;
|
||||
|
||||
// Effective rate
|
||||
const effectiveRate = Number(amountOut) / Number(amountIn);
|
||||
|
||||
return { amountOut, priceImpact, effectiveRate };
|
||||
}
|
||||
|
||||
// HEZ → PEZ scenarios
|
||||
console.log('📈 HEZ → PEZ Swaps:\n');
|
||||
const hezAmounts = [100, 1000, 5000, 10000, 25000, 50000];
|
||||
|
||||
for (const amount of hezAmounts) {
|
||||
const amountIn = BigInt(amount) * ONE_TOKEN;
|
||||
const { amountOut, priceImpact, effectiveRate } = calculateSwap(amountIn, whezReserve, pezReserve);
|
||||
|
||||
console.log(` ${amount.toLocaleString('en-US').padStart(7)} HEZ → ${(Number(amountOut) / Number(ONE_TOKEN)).toLocaleString('en-US', { minimumFractionDigits: 2 }).padStart(12)} PEZ (Rate: ${effectiveRate.toFixed(4)}, Impact: ${priceImpact.toFixed(2)}%)`);
|
||||
}
|
||||
|
||||
// PEZ → HEZ scenarios
|
||||
console.log('\n📉 PEZ → HEZ Swaps:\n');
|
||||
const pezAmounts = [500, 5000, 25000, 50000, 100000, 250000];
|
||||
|
||||
for (const amount of pezAmounts) {
|
||||
const amountIn = BigInt(amount) * ONE_TOKEN;
|
||||
const { amountOut, priceImpact, effectiveRate } = calculateSwap(amountIn, pezReserve, whezReserve);
|
||||
|
||||
console.log(` ${amount.toLocaleString('en-US').padStart(7)} PEZ → ${(Number(amountOut) / Number(ONE_TOKEN)).toLocaleString('en-US', { minimumFractionDigits: 2 }).padStart(12)} HEZ (Rate: ${effectiveRate.toFixed(6)}, Impact: ${priceImpact.toFixed(2)}%)`);
|
||||
}
|
||||
|
||||
// Maximum recommended swaps
|
||||
console.log('\n⚠️ Recommended Limits (10% price impact):\n');
|
||||
|
||||
// For 10% impact: solve for amountIn where amountOut/reserveOut = 0.10
|
||||
// This is approximate: amountOut ≈ 0.10 * reserveOut
|
||||
// 0.10 * reserveOut = (0.97 * amountIn * reserveOut) / (reserveIn + 0.97 * amountIn)
|
||||
// Solving: amountIn ≈ (0.10 * reserveIn) / (0.97 - 0.10)
|
||||
|
||||
const maxHezFor10pct = Number(whezReserve) * 0.10 / 0.87 / Number(ONE_TOKEN);
|
||||
const maxPezFor10pct = Number(pezReserve) * 0.10 / 0.87 / Number(ONE_TOKEN);
|
||||
|
||||
console.log(` Max HEZ → PEZ: ~${maxHezFor10pct.toLocaleString('en-US', { maximumFractionDigits: 0 })} HEZ`);
|
||||
console.log(` Max PEZ → HEZ: ~${maxPezFor10pct.toLocaleString('en-US', { maximumFractionDigits: 0 })} PEZ\n`);
|
||||
|
||||
} else {
|
||||
console.log('❌ Pool reserves not found\n');
|
||||
}
|
||||
} else {
|
||||
console.log('❌ Pool account not found\n');
|
||||
}
|
||||
} else {
|
||||
console.log('❌ Pool does not exist\n');
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
console.log(`❌ Error: ${error.message}\n`);
|
||||
}
|
||||
|
||||
console.log('='.repeat(70));
|
||||
console.log(' AMM Mechanisms');
|
||||
console.log('='.repeat(70) + '\n');
|
||||
|
||||
console.log('🔧 Built-in Mechanisms:\n');
|
||||
|
||||
console.log('1️⃣ Automatic Rebalancing (Self-Regulating):');
|
||||
console.log(' ✓ Pool OTOMATIK olarak dengelenir (x × y = k formülü)');
|
||||
console.log(' ✓ Fiyat değişimi supply/demand\'e göre gerçekleşir');
|
||||
console.log(' ✓ Arbitraj botları fiyat dengesini sağlar\n');
|
||||
|
||||
console.log('2️⃣ Liquidity Provider (LP) Mechanism:');
|
||||
console.log(' ✓ Herkes pool\'a likidite ekleyebilir (addLiquidity)');
|
||||
console.log(' ✓ LP tokenları alırlar (pool payı temsil eder)');
|
||||
console.log(' ✓ Fee\'lerden gelir kazanırlar (%3 swap fee)');
|
||||
console.log(' ✓ İstedikleri zaman çıkabilirler (removeLiquidity)\n');
|
||||
|
||||
console.log('3️⃣ Pool ASLA Boşalmaz:');
|
||||
console.log(' ✓ Matematiksel olarak imkansız (x × y = k)');
|
||||
console.log(' ✓ Reserve azaldıkça fiyat üstel olarak artar');
|
||||
console.log(' ✓ %90 reserve swap\'i için extreme yüksek ödeme gerekir');
|
||||
console.log(' ✓ Bu yüksek fiyat arbitraj fırsatı yaratır → likidite gelir\n');
|
||||
|
||||
console.log('4️⃣ NO Automatic Burn Mechanism:');
|
||||
console.log(' ✗ Otomatik yakma mekanizması YOK');
|
||||
console.log(' ✗ Aşırı bakiye birikimi problemi olmaz');
|
||||
console.log(' ✓ Fazla token pool\'a girerse fiyat düşer → arbitraj');
|
||||
console.log(' ✓ Piyasa doğal olarak dengelenir\n');
|
||||
|
||||
console.log('5️⃣ NO Automatic Liquidity Addition:');
|
||||
console.log(' ✗ Otomatik likidite ekleme YOK');
|
||||
console.log(' ✓ LP\'ler incentive ile (fee geliri) manuel ekler');
|
||||
console.log(' ✓ Yüksek volume → yüksek fee → daha fazla LP gelir');
|
||||
console.log(' ✓ Düşük liquidity → yüksek slippage → LP fırsatı\n');
|
||||
|
||||
console.log('💡 Best Practices:');
|
||||
console.log(' • Büyük swapları birden fazla küçük swap\'a bölün');
|
||||
console.log(' • Slippage tolerance ayarlayın (örn: %5)');
|
||||
console.log(' • High impact swaplarda arbitraj beklentisi olsun');
|
||||
console.log(' • Liquidity arttırmak için incentive programları ekleyin\n');
|
||||
|
||||
await api.disconnect();
|
||||
}
|
||||
|
||||
main().catch(console.error);
|
||||
@@ -1,57 +0,0 @@
|
||||
const { ApiPromise, WsProvider, Keyring } = require('@polkadot/api');
|
||||
const { cryptoWaitReady } = require('@polkadot/util-crypto');
|
||||
|
||||
async function main() {
|
||||
await cryptoWaitReady();
|
||||
|
||||
const wsProvider = new WsProvider('ws://127.0.0.1:9944');
|
||||
const api = await ApiPromise.create({ provider: wsProvider });
|
||||
|
||||
// Founder account from seed phrase
|
||||
const keyring = new Keyring({ type: 'sr25519' });
|
||||
const founder = keyring.addFromUri('skill dose toward always latin fish film cabbage praise blouse kingdom depth');
|
||||
|
||||
console.log('🔑 Founder address:', founder.address);
|
||||
console.log('');
|
||||
|
||||
// Get founder balance
|
||||
const { data: balance } = await api.query.system.account(founder.address);
|
||||
console.log('💰 HEZ Balance:', balance.free.toHuman());
|
||||
console.log('');
|
||||
|
||||
// Check PEZ balance (asset ID: 1)
|
||||
const pezBalance = await api.query.assets.account(1, founder.address);
|
||||
if (pezBalance.isSome) {
|
||||
console.log('💰 PEZ Balance:', pezBalance.unwrap().balance.toHuman());
|
||||
} else {
|
||||
console.log('💰 PEZ Balance: 0');
|
||||
}
|
||||
console.log('');
|
||||
|
||||
// Create HEZ/PEZ pool
|
||||
console.log('🏊 Creating HEZ/PEZ pool...');
|
||||
|
||||
// In Substrate asset_conversion, asset1 should be < asset2
|
||||
// Native token (HEZ) is typically 0, PEZ is 1
|
||||
const asset1 = { parents: 0, interior: { Here: null } }; // Native HEZ
|
||||
const asset2 = { parents: 0, interior: { X2: [{ PalletInstance: 50 }, { GeneralIndex: 1 }] } }; // PEZ token (asset ID 1)
|
||||
|
||||
const txHash = await api.tx.assetConversion
|
||||
.createPool(asset1, asset2)
|
||||
.signAndSend(founder, { nonce: -1 });
|
||||
|
||||
console.log('✅ Pool creation submitted:', txHash.toHex());
|
||||
console.log('');
|
||||
console.log('⏳ Waiting for pool to be created...');
|
||||
|
||||
await new Promise(resolve => setTimeout(resolve, 15000));
|
||||
|
||||
console.log('');
|
||||
console.log('✅ Pool should be created!');
|
||||
console.log('');
|
||||
console.log('💧 Next: Add liquidity via frontend');
|
||||
|
||||
await api.disconnect();
|
||||
}
|
||||
|
||||
main().catch(console.error);
|
||||
@@ -1,91 +0,0 @@
|
||||
import { ApiPromise, WsProvider, Keyring } from '@polkadot/api';
|
||||
import { cryptoWaitReady } from '@polkadot/util-crypto';
|
||||
|
||||
async function main() {
|
||||
await cryptoWaitReady();
|
||||
|
||||
const wsProvider = new WsProvider('ws://127.0.0.1:9944');
|
||||
const api = await ApiPromise.create({ provider: wsProvider });
|
||||
|
||||
// Founder account from seed phrase
|
||||
const keyring = new Keyring({ type: 'sr25519' });
|
||||
const founder = keyring.addFromUri('skill dose toward always latin fish film cabbage praise blouse kingdom depth');
|
||||
|
||||
console.log('🔑 Founder address:', founder.address);
|
||||
console.log('');
|
||||
|
||||
// Get founder balance
|
||||
const { data: balance } = await api.query.system.account(founder.address);
|
||||
console.log('💰 HEZ Balance:', balance.free.toHuman());
|
||||
console.log('');
|
||||
|
||||
// Check PEZ balance (asset ID: 1)
|
||||
const pezBalance = await api.query.assets.account(1, founder.address);
|
||||
if (pezBalance.isSome) {
|
||||
console.log('💰 PEZ Balance:', pezBalance.unwrap().balance.toHuman());
|
||||
} else {
|
||||
console.log('💰 PEZ Balance: 0');
|
||||
}
|
||||
console.log('');
|
||||
|
||||
// Create HEZ/PEZ pool
|
||||
console.log('🏊 Creating HEZ/PEZ pool...');
|
||||
|
||||
// For asset conversion pools:
|
||||
// Native HEZ = { parents: 0, interior: 'Here' }
|
||||
// PEZ token (asset ID 1) = { parents: 0, interior: { X2: [{ PalletInstance: 50 }, { GeneralIndex: 1 }] } }
|
||||
|
||||
const asset1 = api.createType('MultiLocation', {
|
||||
parents: 0,
|
||||
interior: api.createType('Junctions', 'Here')
|
||||
});
|
||||
|
||||
const asset2 = api.createType('MultiLocation', {
|
||||
parents: 0,
|
||||
interior: api.createType('Junctions', {
|
||||
X2: [
|
||||
api.createType('Junction', { PalletInstance: 50 }),
|
||||
api.createType('Junction', { GeneralIndex: 1 })
|
||||
]
|
||||
})
|
||||
});
|
||||
|
||||
console.log('Asset 1 (HEZ):', asset1.toHuman());
|
||||
console.log('Asset 2 (PEZ):', asset2.toHuman());
|
||||
console.log('');
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
api.tx.assetConversion
|
||||
.createPool(asset1, asset2)
|
||||
.signAndSend(founder, ({ status, events }) => {
|
||||
console.log('Transaction status:', status.type);
|
||||
|
||||
if (status.isInBlock) {
|
||||
console.log('✅ Pool creation included in block:', status.asInBlock.toHex());
|
||||
console.log('');
|
||||
|
||||
events.forEach(({ event: { data, method, section } }) => {
|
||||
console.log(` ${section}.${method}:`, data.toHuman());
|
||||
});
|
||||
|
||||
console.log('');
|
||||
console.log('✅ HEZ/PEZ pool created successfully!');
|
||||
console.log('');
|
||||
console.log('💧 Next: Add liquidity via Polkadot.js Apps or your frontend');
|
||||
console.log(' - Go to Developer > Extrinsics');
|
||||
console.log(' - Select assetConversion > addLiquidity');
|
||||
console.log(' - Add HEZ and PEZ tokens to the pool');
|
||||
|
||||
api.disconnect();
|
||||
resolve();
|
||||
}
|
||||
})
|
||||
.catch(err => {
|
||||
console.error('❌ Error:', err);
|
||||
api.disconnect();
|
||||
reject(err);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
main().catch(console.error).finally(() => process.exit(0));
|
||||
@@ -47,9 +47,9 @@ const PoolDashboard = () => {
|
||||
setError(null);
|
||||
|
||||
try {
|
||||
// Query wHEZ/PEZ pool
|
||||
const asset1 = 0; // wHEZ
|
||||
const asset2 = 1; // PEZ
|
||||
// Query PEZ/wUSDT pool
|
||||
const asset1 = 1; // PEZ
|
||||
const asset2 = 2; // wUSDT
|
||||
const poolId = [asset1, asset2];
|
||||
|
||||
const poolInfo = await api.query.assetConversion.pools(poolId);
|
||||
@@ -78,25 +78,25 @@ const PoolDashboard = () => {
|
||||
const poolAccount = poolAccountId.toString();
|
||||
|
||||
// Get reserves
|
||||
const whezBalanceData = await api.query.assets.account(asset1, poolAccountId);
|
||||
const pezBalanceData = await api.query.assets.account(asset2, poolAccountId);
|
||||
const pezBalanceData = await api.query.assets.account(asset1, poolAccountId);
|
||||
const wusdtBalanceData = await api.query.assets.account(asset2, poolAccountId);
|
||||
|
||||
let reserve0 = 0;
|
||||
let reserve1 = 0;
|
||||
|
||||
if (whezBalanceData.isSome) {
|
||||
const whezData = whezBalanceData.unwrap().toJSON() as any;
|
||||
reserve0 = Number(whezData.balance) / 1e12;
|
||||
}
|
||||
|
||||
if (pezBalanceData.isSome) {
|
||||
const pezData = pezBalanceData.unwrap().toJSON() as any;
|
||||
reserve1 = Number(pezData.balance) / 1e12;
|
||||
reserve0 = Number(pezData.balance) / 1e12;
|
||||
}
|
||||
|
||||
if (wusdtBalanceData.isSome) {
|
||||
const wusdtData = wusdtBalanceData.unwrap().toJSON() as any;
|
||||
reserve1 = Number(wusdtData.balance) / 1e6; // wUSDT has 6 decimals
|
||||
}
|
||||
|
||||
setPoolData({
|
||||
asset0: 0,
|
||||
asset1: 1,
|
||||
asset0: 1,
|
||||
asset1: 2,
|
||||
reserve0,
|
||||
reserve1,
|
||||
lpTokenId,
|
||||
@@ -227,7 +227,7 @@ const PoolDashboard = () => {
|
||||
<div>
|
||||
<h2 className="text-2xl font-bold text-white flex items-center gap-2">
|
||||
<Droplet className="h-6 w-6 text-blue-400" />
|
||||
wHEZ/PEZ Pool Dashboard
|
||||
PEZ/wUSDT Pool Dashboard
|
||||
</h2>
|
||||
<p className="text-gray-400 mt-1">Monitor liquidity pool metrics and your position</p>
|
||||
</div>
|
||||
@@ -248,7 +248,7 @@ const PoolDashboard = () => {
|
||||
${totalLiquidityUSD.toLocaleString('en-US', { maximumFractionDigits: 0 })}
|
||||
</p>
|
||||
<p className="text-xs text-gray-500 mt-1">
|
||||
{poolData.reserve0.toLocaleString()} wHEZ + {poolData.reserve1.toLocaleString()} PEZ
|
||||
{poolData.reserve0.toLocaleString()} PEZ + {poolData.reserve1.toLocaleString()} wUSDT
|
||||
</p>
|
||||
</div>
|
||||
<DollarSign className="h-8 w-8 text-green-400" />
|
||||
@@ -259,12 +259,12 @@ const PoolDashboard = () => {
|
||||
<Card className="p-4 bg-gray-800/50 border-gray-700">
|
||||
<div className="flex items-start justify-between">
|
||||
<div>
|
||||
<p className="text-sm text-gray-400">HEZ Price</p>
|
||||
<p className="text-sm text-gray-400">PEZ Price</p>
|
||||
<p className="text-2xl font-bold text-white mt-1">
|
||||
{currentPrice.toFixed(4)} PEZ
|
||||
${currentPrice.toFixed(4)}
|
||||
</p>
|
||||
<p className="text-xs text-gray-500 mt-1">
|
||||
1 PEZ = {(1 / currentPrice).toFixed(6)} HEZ
|
||||
1 wUSDT = {(1 / currentPrice).toFixed(4)} PEZ
|
||||
</p>
|
||||
</div>
|
||||
<TrendingUp className="h-8 w-8 text-blue-400" />
|
||||
@@ -319,18 +319,18 @@ const PoolDashboard = () => {
|
||||
<div className="space-y-4">
|
||||
<div className="flex items-center justify-between p-4 bg-gray-900/50 rounded-lg">
|
||||
<div>
|
||||
<p className="text-sm text-gray-400">wHEZ Reserve</p>
|
||||
<p className="text-sm text-gray-400">PEZ Reserve</p>
|
||||
<p className="text-2xl font-bold text-white">{poolData.reserve0.toLocaleString('en-US', { maximumFractionDigits: 2 })}</p>
|
||||
</div>
|
||||
<Badge variant="outline">Asset 0</Badge>
|
||||
<Badge variant="outline">Asset 1</Badge>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center justify-between p-4 bg-gray-900/50 rounded-lg">
|
||||
<div>
|
||||
<p className="text-sm text-gray-400">PEZ Reserve</p>
|
||||
<p className="text-sm text-gray-400">wUSDT Reserve</p>
|
||||
<p className="text-2xl font-bold text-white">{poolData.reserve1.toLocaleString('en-US', { maximumFractionDigits: 2 })}</p>
|
||||
</div>
|
||||
<Badge variant="outline">Asset 1</Badge>
|
||||
<Badge variant="outline">Asset 2</Badge>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -387,11 +387,11 @@ const PoolDashboard = () => {
|
||||
<p className="text-sm text-gray-400 mb-2">Your Position Value</p>
|
||||
<div className="space-y-2">
|
||||
<div className="flex justify-between">
|
||||
<span className="text-gray-300">wHEZ:</span>
|
||||
<span className="text-gray-300">PEZ:</span>
|
||||
<span className="text-white font-semibold">{lpPosition.asset0Amount.toFixed(4)}</span>
|
||||
</div>
|
||||
<div className="flex justify-between">
|
||||
<span className="text-gray-300">PEZ:</span>
|
||||
<span className="text-gray-300">wUSDT:</span>
|
||||
<span className="text-white font-semibold">{lpPosition.asset1Amount.toFixed(4)}</span>
|
||||
</div>
|
||||
</div>
|
||||
@@ -442,7 +442,7 @@ const PoolDashboard = () => {
|
||||
|
||||
<div className="space-y-4">
|
||||
<div className="p-4 bg-gray-900/50 rounded-lg">
|
||||
<p className="text-sm text-gray-400 mb-3">If HEZ price changes by:</p>
|
||||
<p className="text-sm text-gray-400 mb-3">If PEZ price changes by:</p>
|
||||
|
||||
<div className="space-y-2">
|
||||
{[10, 25, 50, 100, 200].map((change) => {
|
||||
|
||||
+120
-23
@@ -6,6 +6,7 @@ import { Input } from '@/components/ui/input';
|
||||
import { Alert, AlertDescription } from '@/components/ui/alert';
|
||||
import { Badge } from '@/components/ui/badge';
|
||||
import { Dialog, DialogContent, DialogHeader, DialogTitle } from '@/components/ui/dialog';
|
||||
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select';
|
||||
import { usePolkadot } from '@/contexts/PolkadotContext';
|
||||
import { useWallet } from '@/contexts/WalletContext';
|
||||
import { ASSET_IDS, formatBalance, parseAmount } from '@/lib/wallet';
|
||||
@@ -14,11 +15,18 @@ import { KurdistanSun } from './KurdistanSun';
|
||||
import { PriceChart } from './trading/PriceChart';
|
||||
import { LimitOrders } from './trading/LimitOrders';
|
||||
|
||||
// Available tokens for swap
|
||||
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: 'wUSDT', emoji: '💵', assetId: 2, name: 'USDT', badge: true, displaySymbol: 'USDT' },
|
||||
] as const;
|
||||
|
||||
const TokenSwap = () => {
|
||||
const { api, isApiReady, selectedAccount } = usePolkadot();
|
||||
const { balances, refreshBalances } = useWallet();
|
||||
const { toast } = useToast();
|
||||
|
||||
|
||||
const [fromToken, setFromToken] = useState('PEZ');
|
||||
const [toToken, setToToken] = useState('HEZ');
|
||||
const [fromAmount, setFromAmount] = useState('');
|
||||
@@ -62,6 +70,12 @@ const TokenSwap = () => {
|
||||
// Pool reserves for AMM calculation
|
||||
const [poolReserves, setPoolReserves] = useState<{ reserve0: number; reserve1: number; asset0: number; asset1: number } | null>(null);
|
||||
|
||||
// Helper: Get display name for token (USDT instead of wUSDT)
|
||||
const getTokenDisplayName = (tokenSymbol: string) => {
|
||||
const token = AVAILABLE_TOKENS.find(t => t.symbol === tokenSymbol);
|
||||
return token?.displaySymbol || tokenSymbol;
|
||||
};
|
||||
|
||||
// Calculate toAmount and price impact using AMM constant product formula
|
||||
const swapCalculations = React.useMemo(() => {
|
||||
if (!fromAmount || !poolReserves || parseFloat(fromAmount) <= 0) {
|
||||
@@ -156,6 +170,8 @@ const TokenSwap = () => {
|
||||
// HEZ → wHEZ (Asset 0) behind the scenes
|
||||
const getPoolAssetId = (token: string) => {
|
||||
if (token === 'HEZ') return 0; // wHEZ
|
||||
if (token === 'PEZ') return 1;
|
||||
if (token === 'wUSDT') return 2;
|
||||
return ASSET_IDS[token as keyof typeof ASSET_IDS];
|
||||
};
|
||||
|
||||
@@ -241,10 +257,17 @@ const TokenSwap = () => {
|
||||
|
||||
console.log('🔍 Raw hex balances:', { balance0Hex, balance1Hex });
|
||||
|
||||
const reserve0 = Number(BigInt(balance0Hex)) / 1e12;
|
||||
const reserve1 = Number(BigInt(balance1Hex)) / 1e12;
|
||||
// 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
|
||||
|
||||
console.log('✅ Reserves found:', { reserve0, reserve1 });
|
||||
const reserve0 = Number(BigInt(balance0Hex)) / (10 ** decimals0);
|
||||
const reserve1 = Number(BigInt(balance1Hex)) / (10 ** decimals1);
|
||||
|
||||
console.log('✅ Reserves found:', { reserve0, reserve1, decimals0, decimals1 });
|
||||
|
||||
// Store pool reserves for AMM calculation
|
||||
setPoolReserves({
|
||||
@@ -396,8 +419,8 @@ const TokenSwap = () => {
|
||||
console.warn('Failed to parse swap path:', err);
|
||||
}
|
||||
|
||||
const fromTokenSymbol = fromAssetId === 0 ? 'wHEZ' : fromAssetId === 1 ? 'PEZ' : `Asset${fromAssetId}`;
|
||||
const toTokenSymbol = toAssetId === 0 ? 'wHEZ' : toAssetId === 1 ? 'PEZ' : `Asset${toAssetId}`;
|
||||
const fromTokenSymbol = fromAssetId === 0 ? 'wHEZ' : fromAssetId === 1 ? 'PEZ' : fromAssetId === 2 ? 'wUSDT' : `Asset${fromAssetId}`;
|
||||
const toTokenSymbol = toAssetId === 0 ? 'wHEZ' : toAssetId === 1 ? 'PEZ' : toAssetId === 2 ? 'wUSDT' : `Asset${toAssetId}`;
|
||||
|
||||
// Only show transactions from current user
|
||||
if (who.toString() === selectedAccount.address) {
|
||||
@@ -769,7 +792,7 @@ const TokenSwap = () => {
|
||||
<div className="flex justify-between mb-2">
|
||||
<span className="text-sm text-gray-400">From</span>
|
||||
<span className="text-sm text-gray-400">
|
||||
Balance: {fromBalance} {fromToken}
|
||||
Balance: {fromBalance} {getTokenDisplayName(fromToken)}
|
||||
</span>
|
||||
</div>
|
||||
<div className="flex gap-3">
|
||||
@@ -781,9 +804,46 @@ const TokenSwap = () => {
|
||||
className="text-2xl font-bold border-0 bg-transparent text-white placeholder:text-gray-600"
|
||||
disabled={!selectedAccount}
|
||||
/>
|
||||
<Button variant="outline" className="min-w-[100px] border-gray-600 hover:border-gray-500">
|
||||
{fromToken === 'PEZ' ? '🟣 PEZ' : '🟡 HEZ'}
|
||||
</Button>
|
||||
<Select
|
||||
value={fromToken}
|
||||
onValueChange={(value) => {
|
||||
setFromToken(value);
|
||||
// Prevent selecting same token for both sides
|
||||
if (value === toToken) {
|
||||
const otherToken = AVAILABLE_TOKENS.find(t => t.symbol !== value);
|
||||
if (otherToken) setToToken(otherToken.symbol);
|
||||
}
|
||||
}}
|
||||
disabled={!selectedAccount}
|
||||
>
|
||||
<SelectTrigger className="min-w-[140px] border-gray-600 hover:border-gray-500">
|
||||
<SelectValue>
|
||||
{(() => {
|
||||
const token = AVAILABLE_TOKENS.find(t => t.symbol === fromToken);
|
||||
return (
|
||||
<span className="flex items-center gap-1.5 relative">
|
||||
{token?.emoji} {token?.displaySymbol || token?.name}
|
||||
{token?.badge && (
|
||||
<span className="w-2 h-2 bg-gradient-to-br from-red-500 via-yellow-400 to-green-500 rounded-sm absolute -bottom-0.5 -right-0.5"></span>
|
||||
)}
|
||||
</span>
|
||||
);
|
||||
})()}
|
||||
</SelectValue>
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
{AVAILABLE_TOKENS.map((token) => (
|
||||
<SelectItem key={token.symbol} value={token.symbol}>
|
||||
<span className="flex items-center gap-2">
|
||||
<span>{token.emoji} {token.name}</span>
|
||||
{token.badge && (
|
||||
<span className="w-2.5 h-2.5 bg-gradient-to-br from-red-500 via-yellow-400 to-green-500 rounded-sm ml-1"></span>
|
||||
)}
|
||||
</span>
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -803,7 +863,7 @@ const TokenSwap = () => {
|
||||
<div className="flex justify-between mb-2">
|
||||
<span className="text-sm text-gray-400">To</span>
|
||||
<span className="text-sm text-gray-400">
|
||||
Balance: {toBalance} {toToken}
|
||||
Balance: {toBalance} {getTokenDisplayName(toToken)}
|
||||
</span>
|
||||
</div>
|
||||
<div className="flex gap-3">
|
||||
@@ -814,9 +874,46 @@ const TokenSwap = () => {
|
||||
placeholder="0.0"
|
||||
className="text-2xl font-bold border-0 bg-transparent text-white placeholder:text-gray-600"
|
||||
/>
|
||||
<Button variant="outline" className="min-w-[100px] border-gray-600 hover:border-gray-500">
|
||||
{toToken === 'PEZ' ? '🟣 PEZ' : '🟡 HEZ'}
|
||||
</Button>
|
||||
<Select
|
||||
value={toToken}
|
||||
onValueChange={(value) => {
|
||||
setToToken(value);
|
||||
// Prevent selecting same token for both sides
|
||||
if (value === fromToken) {
|
||||
const otherToken = AVAILABLE_TOKENS.find(t => t.symbol !== value);
|
||||
if (otherToken) setFromToken(otherToken.symbol);
|
||||
}
|
||||
}}
|
||||
disabled={!selectedAccount}
|
||||
>
|
||||
<SelectTrigger className="min-w-[140px] border-gray-600 hover:border-gray-500">
|
||||
<SelectValue>
|
||||
{(() => {
|
||||
const token = AVAILABLE_TOKENS.find(t => t.symbol === toToken);
|
||||
return (
|
||||
<span className="flex items-center gap-1.5 relative">
|
||||
{token?.emoji} {token?.displaySymbol || token?.name}
|
||||
{token?.badge && (
|
||||
<span className="w-2 h-2 bg-gradient-to-br from-red-500 via-yellow-400 to-green-500 rounded-sm absolute -bottom-0.5 -right-0.5"></span>
|
||||
)}
|
||||
</span>
|
||||
);
|
||||
})()}
|
||||
</SelectValue>
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
{AVAILABLE_TOKENS.map((token) => (
|
||||
<SelectItem key={token.symbol} value={token.symbol}>
|
||||
<span className="flex items-center gap-2">
|
||||
<span>{token.emoji} {token.name}</span>
|
||||
{token.badge && (
|
||||
<span className="w-2.5 h-2.5 bg-gradient-to-br from-red-500 via-yellow-400 to-green-500 rounded-sm ml-1"></span>
|
||||
)}
|
||||
</span>
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -831,7 +928,7 @@ const TokenSwap = () => {
|
||||
{isLoadingRate ? (
|
||||
'Loading...'
|
||||
) : exchangeRate > 0 ? (
|
||||
`1 ${fromToken} = ${exchangeRate.toFixed(4)} ${toToken}`
|
||||
`1 ${getTokenDisplayName(fromToken)} = ${exchangeRate.toFixed(4)} ${getTokenDisplayName(toToken)}`
|
||||
) : (
|
||||
'No pool available'
|
||||
)}
|
||||
@@ -863,7 +960,7 @@ const TokenSwap = () => {
|
||||
{fromAmount && parseFloat(fromAmount) > 0 && lpFee && (
|
||||
<div className="flex justify-between text-sm">
|
||||
<span className="text-gray-400">Liquidity Provider Fee</span>
|
||||
<span className="text-gray-300">{lpFee} {fromToken}</span>
|
||||
<span className="text-gray-300">{lpFee} {getTokenDisplayName(fromToken)}</span>
|
||||
</div>
|
||||
)}
|
||||
|
||||
@@ -871,7 +968,7 @@ const TokenSwap = () => {
|
||||
{fromAmount && parseFloat(fromAmount) > 0 && minimumReceived && (
|
||||
<div className="flex justify-between text-sm">
|
||||
<span className="text-gray-400">Minimum Received</span>
|
||||
<span className="text-gray-300">{minimumReceived} {toToken}</span>
|
||||
<span className="text-gray-300">{minimumReceived} {getTokenDisplayName(toToken)}</span>
|
||||
</div>
|
||||
)}
|
||||
|
||||
@@ -962,7 +1059,7 @@ const TokenSwap = () => {
|
||||
<div className="flex items-center gap-2">
|
||||
<ArrowDownUp className="w-4 h-4 text-blue-400" />
|
||||
<span className="text-sm font-semibold text-white">
|
||||
{tx.fromToken} → {tx.toToken}
|
||||
{getTokenDisplayName(tx.fromToken)} → {getTokenDisplayName(tx.toToken)}
|
||||
</span>
|
||||
</div>
|
||||
<span className="text-xs text-gray-500">
|
||||
@@ -972,11 +1069,11 @@ const TokenSwap = () => {
|
||||
<div className="text-sm text-gray-400 space-y-1">
|
||||
<div className="flex justify-between">
|
||||
<span>Sent:</span>
|
||||
<span className="text-red-400">-{tx.fromAmount} {tx.fromToken}</span>
|
||||
<span className="text-red-400">-{tx.fromAmount} {getTokenDisplayName(tx.fromToken)}</span>
|
||||
</div>
|
||||
<div className="flex justify-between">
|
||||
<span>Received:</span>
|
||||
<span className="text-green-400">+{tx.toAmount} {tx.toToken}</span>
|
||||
<span className="text-green-400">+{tx.toAmount} {getTokenDisplayName(tx.toToken)}</span>
|
||||
</div>
|
||||
<div className="flex justify-between text-xs pt-1 border-t border-gray-700">
|
||||
<span>{new Date(tx.timestamp).toLocaleDateString()}</span>
|
||||
@@ -1034,15 +1131,15 @@ const TokenSwap = () => {
|
||||
<div className="p-4 bg-gray-800 border border-gray-700 rounded-lg">
|
||||
<div className="flex justify-between mb-2">
|
||||
<span className="text-gray-300">You Pay</span>
|
||||
<span className="font-bold text-white">{fromAmount} {fromToken}</span>
|
||||
<span className="font-bold text-white">{fromAmount} {getTokenDisplayName(fromToken)}</span>
|
||||
</div>
|
||||
<div className="flex justify-between mb-2">
|
||||
<span className="text-gray-300">You Receive</span>
|
||||
<span className="font-bold text-white">{toAmount} {toToken}</span>
|
||||
<span className="font-bold text-white">{toAmount} {getTokenDisplayName(toToken)}</span>
|
||||
</div>
|
||||
<div className="flex justify-between mt-3 pt-3 border-t border-gray-700 text-sm">
|
||||
<span className="text-gray-400">Exchange Rate</span>
|
||||
<span className="text-gray-400">1 {fromToken} = {exchangeRate.toFixed(4)} {toToken}</span>
|
||||
<span className="text-gray-400">1 {getTokenDisplayName(fromToken)} = {exchangeRate.toFixed(4)} {getTokenDisplayName(toToken)}</span>
|
||||
</div>
|
||||
<div className="flex justify-between text-sm">
|
||||
<span className="text-gray-400">Slippage</span>
|
||||
|
||||
@@ -13,6 +13,7 @@ interface TokenBalances {
|
||||
HEZ: string;
|
||||
PEZ: string;
|
||||
wHEZ: string;
|
||||
wUSDT: string;
|
||||
}
|
||||
|
||||
interface WalletContextType {
|
||||
@@ -43,7 +44,7 @@ export const WalletProvider: React.FC<{ children: React.ReactNode }> = ({ childr
|
||||
});
|
||||
|
||||
const [balance, setBalance] = useState<string>('0');
|
||||
const [balances, setBalances] = useState<TokenBalances>({ HEZ: '0', PEZ: '0', wHEZ: '0' });
|
||||
const [balances, setBalances] = useState<TokenBalances>({ HEZ: '0', PEZ: '0', wHEZ: '0', wUSDT: '0' });
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
|
||||
// Fetch all token balances when account changes
|
||||
@@ -97,13 +98,32 @@ export const WalletProvider: React.FC<{ children: React.ReactNode }> = ({ childr
|
||||
console.error('❌ Failed to fetch wHEZ balance:', err);
|
||||
}
|
||||
|
||||
// Fetch wUSDT (Asset ID: 2)
|
||||
let wusdtBalance = '0';
|
||||
try {
|
||||
const wusdtData = await polkadot.api.query.assets.account(ASSET_IDS.WUSDT, address);
|
||||
console.log('📊 Raw wUSDT data:', wusdtData.toHuman());
|
||||
|
||||
if (wusdtData.isSome) {
|
||||
const assetData = wusdtData.unwrap();
|
||||
const wusdtAmount = assetData.balance.toString();
|
||||
wusdtBalance = formatBalance(wusdtAmount);
|
||||
console.log('✅ wUSDT balance found:', wusdtBalance);
|
||||
} else {
|
||||
console.warn('⚠️ wUSDT asset not found for this account');
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('❌ Failed to fetch wUSDT balance:', err);
|
||||
}
|
||||
|
||||
setBalances({
|
||||
HEZ: hezBalance,
|
||||
PEZ: pezBalance,
|
||||
wHEZ: whezBalance,
|
||||
wUSDT: wusdtBalance,
|
||||
});
|
||||
|
||||
console.log('✅ Balances updated:', { HEZ: hezBalance, PEZ: pezBalance, wHEZ: whezBalance });
|
||||
console.log('✅ Balances updated:', { HEZ: hezBalance, PEZ: pezBalance, wHEZ: whezBalance, wUSDT: wusdtBalance });
|
||||
} catch (err) {
|
||||
console.error('Failed to fetch balances:', err);
|
||||
setError('Failed to fetch balances');
|
||||
|
||||
Reference in New Issue
Block a user