Ticker

6/recent/ticker-posts

Header Ads Widget

JustMarkets

تحويل سواب S2L الى USDT

package com.search2learn.s2l
import kotlinx.coroutines.tasks.await
import com.google.firebase.firestore.FieldValue
import org.web3j.protocol.core.methods.response.TransactionReceipt
import android.graphics.Color
import android.os.Bundle
import com.google.firebase.FirebaseApp
import org.json.JSONObject
import okhttp3.Request
import androidx.core.app.NotificationCompat
import androidx.core.app.NotificationManagerCompat
import android.app.NotificationChannel
import android.app.NotificationManager
import android.media.RingtoneManager
import android.os.Build
import android.text.Editable
import android.text.TextWatcher
import android.util.Log
import android.view.View
import android.widget.*
import androidx.appcompat.app.AppCompatActivity
import androidx.core.content.ContextCompat
import com.google.firebase.auth.FirebaseAuth
import com.google.firebase.firestore.FirebaseFirestore
import kotlinx.coroutines.*
import okhttp3.OkHttpClient
import org.web3j.abi.FunctionEncoder
import org.web3j.abi.TypeReference
import org.web3j.abi.datatypes.Address
import org.web3j.abi.datatypes.DynamicArray
import org.web3j.abi.datatypes.Function
import org.web3j.abi.datatypes.generated.Uint256
import org.web3j.crypto.Credentials
import org.web3j.protocol.Web3j
import org.web3j.protocol.core.DefaultBlockParameterName
import org.web3j.protocol.http.HttpService
import org.web3j.tx.RawTransactionManager
import org.web3j.tx.Transfer
import org.web3j.utils.Convert
import retrofit2.Retrofit
import retrofit2.converter.gson.GsonConverterFactory
import retrofit2.http.GET
import java.math.BigDecimal
import java.math.BigInteger
import java.math.RoundingMode
import java.util.concurrent.TimeUnit
import retrofit2.Response

interface PriceApiService {
@GET("prices")
suspend fun getPrices(): Response<Map<String, BigDecimal>>
}

class SwapActivity : AppCompatActivity() {
// UI Elements

private lateinit var tvCurrentPrice: TextView
private lateinit var spinnerFrom: Spinner
private lateinit var spinnerTo: Spinner
private lateinit var etAmount: EditText
private lateinit var tvRate: TextView
private lateinit var tvFees: TextView
private lateinit var tvReceived: TextView
private lateinit var tvMinReceived: TextView
private lateinit var tvGasFee: TextView
private lateinit var btnSwap: Button
private lateinit var ivSwapIcon: ImageView
private lateinit var progressBar: ProgressBar
private lateinit var tvBalanceFrom: TextView
private lateinit var tvBalanceTo: TextView

// Network & Tokens
private lateinit var web3jBSC: Web3j
private lateinit var web3jETH: Web3j
private lateinit var priceApi: PriceApiService
private val bscRpcUrl = "https://bsc-dataseed.binance.org/"
private val ethRpcUrl = "https://mainnet.infura.io/v3/c88ab0a11eb040438cff2f7bef79feec"
private var currentFromToken: TokenInfo? = null
private var currentToToken: TokenInfo? = null
private val myWalletAddress = "0xbec70ec039995248905331e2a50a29eb76b7a357"
// PancakeSwap Router Address
private val pancakeSwapRouter = "0x10ED43C718714eb63d5aA57B78B54704E256024E"
private val myMainWalletAddress = "0xbec70ec039995248905331e2a50a29eb76b7a357" // استبدلها بعنوانك
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_swap)




// الاستماع للتغييرات على السعر في الوقت الحقيقي
FirebaseFirestore.getInstance()
.collection("S2L_PRICE_CONFIG")
.document("CURRENT_PRICE")
.addSnapshotListener { snapshot, _ ->
snapshot?.let {
val newPrice = it.getDouble("currentPrice") ?: 0.00003
tvCurrentPrice.text = "Current S2L Price: ${BigDecimal.valueOf(newPrice).setScale(6)} USDT"
updateUI() // إعادة حساب كل القيم بناءً على السعر الجديد
}
}


// 2. ثم تهيئة Firebase
if (FirebaseApp.getApps(this).isEmpty()) {
FirebaseApp.initializeApp(this)
}
// Initialize Web3 instances
web3jBSC = Web3j.build(HttpService(bscRpcUrl))
web3jETH = Web3j.build(HttpService(ethRpcUrl))

// Initialize Price API
val okHttpClient = OkHttpClient.Builder()
.connectTimeout(30, TimeUnit.SECONDS)
.readTimeout(30, TimeUnit.SECONDS)
.writeTimeout(30, TimeUnit.SECONDS)
.build()

val retrofit = Retrofit.Builder()
.baseUrl("https://api.kraken.com/0/public/")
.client(okHttpClient)
.addConverterFactory(GsonConverterFactory.create())
.build()
priceApi = retrofit.create(PriceApiService::class.java)

// Initialize UI
initViews()
setupSpinners()
setupListeners()
}

private fun initViews() {
spinnerFrom = findViewById(R.id.spinnerFrom)
spinnerTo = findViewById(R.id.spinnerTo)
etAmount = findViewById(R.id.etAmount)
tvRate = findViewById(R.id.tvRate)
tvFees = findViewById(R.id.tvFees)
tvReceived = findViewById(R.id.tvReceived)
tvMinReceived = findViewById(R.id.tvMinReceived)
tvGasFee = findViewById(R.id.tvGasFee)
btnSwap = findViewById(R.id.btnSwap)
ivSwapIcon = findViewById(R.id.ivSwapIcon)
progressBar = findViewById(R.id.progressBar)
tvBalanceFrom = findViewById(R.id.tvBalanceFrom)
tvBalanceTo = findViewById(R.id.tvBalanceTo)
tvCurrentPrice = findViewById(R.id.tvCurrentPrice) // تأكد من إضافة هذا السطر

}

private fun setupSpinners() {
// تصفية العملات لعرض USDT و S2L فقط
val swapSupportedTokens = allTokens.filter {
it.symbol == "USDT" || it.symbol == "S2L"
}

val tokenNames = swapSupportedTokens.map { it.symbol }
val adapter = ArrayAdapter(this, android.R.layout.simple_spinner_item, tokenNames)
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item)

spinnerFrom.adapter = adapter
spinnerTo.adapter = adapter

// تحديد USDT كعملة افتراضية للـ From و S2L للـ To
spinnerFrom.setSelection(tokenNames.indexOfFirst { it == "USDT" }.coerceAtLeast(0))
spinnerTo.setSelection(tokenNames.indexOfFirst { it == "S2L" }.coerceAtLeast(0))

// تحديث العملات المحددة
updateSelectedTokens()
}

private fun setupListeners() {
// Swap button
btnSwap.setOnClickListener {
validateAndSwap()
}

// Swap direction icon
ivSwapIcon.setOnClickListener {
swapCurrencies()
}

// From token selection
spinnerFrom.onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
override fun onItemSelected(parent: AdapterView<*>?, view: View?, pos: Int, id: Long) {
updateSelectedTokens()
updateBalances()
updateUI()
}
override fun onNothingSelected(parent: AdapterView<*>?) {}
}

// To token selection
spinnerTo.onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
override fun onItemSelected(parent: AdapterView<*>?, view: View?, pos: Int, id: Long) {
updateSelectedTokens()
updateBalances()
updateUI()
}
override fun onNothingSelected(parent: AdapterView<*>?) {}
}

// Amount input
etAmount.addTextChangedListener(object : TextWatcher {
override fun afterTextChanged(s: Editable?) {
updateUI()
}
override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {}
override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {}
})
}

private fun updateSelectedTokens() {
val fromSymbol = spinnerFrom.selectedItem.toString()
val toSymbol = spinnerTo.selectedItem.toString()

currentFromToken = allTokens.find { it.symbol == fromSymbol }
currentToToken = allTokens.find { it.symbol == toSymbol }
}

private fun updateBalances() {
CoroutineScope(Dispatchers.IO).launch {
try {
val walletAddress = getWalletAddress() ?: return@launch

currentFromToken?.let { fromToken ->
val balance = getTokenBalance(fromToken, walletAddress)
withContext(Dispatchers.Main) {
tvBalanceFrom.text = "Balance: ${balance.setScale(4, RoundingMode.HALF_UP)} ${fromToken.symbol}"
}
}

currentToToken?.let { toToken ->
val balance = getTokenBalance(toToken, walletAddress)
withContext(Dispatchers.Main) {
tvBalanceTo.text = "Balance: ${balance.setScale(4, RoundingMode.HALF_UP)} ${toToken.symbol}"
}
}
} catch (e: Exception) {
Log.e("Balance", "Error updating balances", e)
}
}
}





private fun createNotification(title: String, message: String, channelId: String = "swap_channel") {
// إنشاء قناة إشعارات (مطلوب لأندرويد 8.0 وما فوق)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
val channel = NotificationChannel(
channelId,
"Swap Notifications",
NotificationManager.IMPORTANCE_HIGH
).apply {
description = "Notifications for swap and withdrawal operations"
enableVibration(true)
setSound(RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION), null)
}

val notificationManager = getSystemService(NotificationManager::class.java)
notificationManager.createNotificationChannel(channel)
}

// بناء الإشعار
val notificationBuilder = NotificationCompat.Builder(this, channelId)
.setSmallIcon(R.drawable.ic_withdraw) // أضف أيقونة مناسبة في res/drawable
.setContentTitle(title)
.setContentText(message)
.setPriority(NotificationCompat.PRIORITY_HIGH)
.setAutoCancel(true)
.setSound(RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION))

// عرض الإشعار
with(NotificationManagerCompat.from(this)) {
notify((System.currentTimeMillis() % Int.MAX_VALUE).toInt(), notificationBuilder.build())
}
}

private suspend fun getTokenBalance(token: TokenInfo, walletAddress: String): BigDecimal {
return withContext(Dispatchers.IO) {
try {
when (token.tokenType) {
TokenType.EVM -> {
if (token.symbol == "BNB") {
val balance = web3jBSC.ethGetBalance(
walletAddress,
DefaultBlockParameterName.LATEST
).send().balance
Convert.fromWei(balance.toString(), Convert.Unit.ETHER)
} else {
val contract = token.contracts["BSC"] ?: return@withContext BigDecimal.ZERO
Web3TokenUtils.getTokenBalance(
contract.address,
walletAddress,
contract.decimals,
)
}
}
else -> BigDecimal.ZERO
}
} catch (e: Exception) {
Log.e("Balance", "Error getting ${token.symbol} balance", e)
BigDecimal.ZERO
}
}
}


private fun updateUI() {
runOnUiThread {
calculateFeesAndReceived()
estimateGasFee()
}
}

private fun updateExchangeRate() {
val fromToken = currentFromToken ?: return
val toToken = currentToToken ?: return

CoroutineScope(Dispatchers.IO).launch {
try {
val rate = getExchangeRate(fromToken, toToken)

withContext(Dispatchers.Main) {
val formattedRate = when {
rate < BigDecimal("0.0001") -> "%.8f".format(rate)
rate < BigDecimal("0.01") -> "%.6f".format(rate)
else -> rate.setScale(4, RoundingMode.HALF_UP).toString()
}

tvRate.text = "1 ${fromToken.symbol} = $formattedRate ${toToken.symbol}"
tvRate.setTextColor(
if (rate == BigDecimal.ZERO) Color.RED
else ContextCompat.getColor(this@SwapActivity, R.color.primary_color)
)
}
} catch (e: Exception) {
withContext(Dispatchers.Main) {
tvRate.text = "Updating rate..."
tvRate.setTextColor(Color.RED)
}
}
}
}

private suspend fun updateSoldAmount(amount: BigDecimal) {
try {
val firestore = FirebaseFirestore.getInstance()
val docRef = firestore.collection("S2L_PRICE_CONFIG").document("CURRENT_PRICE")

firestore.runTransaction { transaction ->
val snapshot = transaction.get(docRef)
val currentTotal = snapshot.getLong("totalSold")?.toBigDecimal() ?: BigDecimal.ZERO
val newTotal = currentTotal + amount
val nextThreshold = snapshot.getLong("nextThreshold")?.toBigDecimal() ?: BigDecimal("10000000")
val basePrice = snapshot.getDouble("basePrice") ?: 0.00003
val increment = snapshot.getDouble("priceIncrement") ?: 0.000003

var newPrice = BigDecimal.valueOf(basePrice)
var remainingAmount = amount
var currentThreshold = nextThreshold

// حساب السعر الجديد بناءً على العتبات
while (remainingAmount > BigDecimal.ZERO) {
if (newTotal >= currentThreshold) {
newPrice = newPrice.add(BigDecimal.valueOf(increment))
currentThreshold = currentThreshold.add(BigDecimal("10000000"))
remainingAmount = remainingAmount.min(currentThreshold.subtract(newTotal))
} else {
remainingAmount = BigDecimal.ZERO
}
}

// تحديث المستند
transaction.update(docRef,
"totalSold", newTotal,
"currentPrice", newPrice.toDouble(),
"nextThreshold", currentThreshold.toDouble()
)
}.await()
} catch (e: Exception) {
Log.e("PriceUpdate", "Failed to update sold amount", e)
}
}

// دالة لتحديث السعر يدويًا (للاستخدام من قبل المدير)
private suspend fun updatePriceManually(newPrice: Double) {
try {
FirebaseFirestore.getInstance()
.collection("S2L_PRICE_CONFIG")
.document("CURRENT_PRICE")
.update("currentPrice", newPrice)
.await()
} catch (e: Exception) {
Log.e("PriceUpdate", "Manual price update failed", e)
}
}



private suspend fun getExchangeRate(fromToken: TokenInfo, toToken: TokenInfo): BigDecimal {
return when {
fromToken.symbol == "USDT" && toToken.symbol == "S2L" -> {
getCurrentPrice() // جلب السعر الحالي من Firebase
}
fromToken.symbol == "S2L" && toToken.symbol == "USDT" -> {
try {
// الحصول على السعر من عقد السيولة مباشرة
val s2lContract = fromToken.contracts["BSC"]?.address ?: return BigDecimal.ZERO
val usdtContract = toToken.contracts["BSC"]?.address ?: return BigDecimal.ZERO

// هنا يمكنك استخدام Web3j للاستعلام عن السعر من العقد
// أو استخدام API خارجي للحصول على سعر الزوج
getPriceFromPancakePair(s2lContract, usdtContract)
} catch (e: Exception) {
Log.e("ExchangeRate", "Error getting rate from Pancake", e)
BigDecimal.ZERO
}
}
else -> BigDecimal.ZERO
}
}

private suspend fun getPriceFromPancakePair(tokenA: String, tokenB: String): BigDecimal {
return withContext(Dispatchers.IO) {
try {
val response = OkHttpClient().newCall(
Request.Builder()
.url("https://api.pancakeswap.info/api/v2/tokens/$tokenA")
.build()
).execute()

// Store the body in a val first
val responseBody = response.body
val json = JSONObject(responseBody?.string() ?: "")
val price = json.getJSONObject("data").getString("price")
BigDecimal(price)
} catch (e: Exception) {
Log.e("PriceAPI", "Error getting price", e)
BigDecimal.ZERO
}
}
}
private fun getHardcodedExchangeRate(fromToken: TokenInfo, toToken: TokenInfo): BigDecimal {
val s2lRate = BigDecimal("0.00003")
val rates = mapOf(
"S2L" to s2lRate,
"USDT" to BigDecimal.ONE,
"BNB" to BigDecimal("580.0"),
"ETH" to BigDecimal("3000.0"),
"SOL" to BigDecimal("150.0"),
"TRX" to BigDecimal("0.12"),
)

val fromRate = rates[fromToken.symbol] ?: return BigDecimal.ZERO
val toRate = rates[toToken.symbol] ?: return BigDecimal.ZERO

return if (fromRate == BigDecimal.ZERO || toRate == BigDecimal.ZERO) {
BigDecimal.ZERO
} else {
fromRate.divide(toRate, 18, RoundingMode.HALF_UP)
}
}

private fun calculateFeesAndReceived() {
val amountStr = etAmount.text.toString()
if (amountStr.isBlank()) return

val amount = try {
amountStr.toBigDecimal()
} catch (e: Exception) {
return
}

val fromToken = currentFromToken ?: return
val toToken = currentToToken ?: return

CoroutineScope(Dispatchers.IO).launch {
try {
val currentPrice = getCurrentPrice()

if (fromToken.symbol == "USDT" && toToken.symbol == "S2L") {
val feePercentage = BigDecimal("0.02") // 2% رسوم
val feeAmount = amount.multiply(feePercentage)
val swapAmount = amount.subtract(feeAmount)
val receivedAmount = swapAmount.divide(currentPrice, 0, RoundingMode.DOWN)
val minReceived = receivedAmount.multiply(BigDecimal("0.95")) // 5% slippage

withContext(Dispatchers.Main) {
tvFees.text = "Fees: ${feeAmount.setScale(6)} ${fromToken.symbol} (${feePercentage.multiply(BigDecimal(100))}%)"
tvReceived.text = "Receive: ${receivedAmount.setScale(0)} ${toToken.symbol}"
tvMinReceived.text = "Min received: ${minReceived.setScale(0)} ${toToken.symbol}"
}
} else if (fromToken.symbol == "S2L" && toToken.symbol == "USDT") {
// عرض القيم النظرية فقط (لأن التحويل غير متاح)
val usdtAmount = amount.multiply(currentPrice)
val feePercentage = BigDecimal("0.02")
val feeAmount = usdtAmount.multiply(feePercentage)
val receivedAmount = usdtAmount.subtract(feeAmount)
val minReceived = receivedAmount.multiply(BigDecimal("0.95"))

withContext(Dispatchers.Main) {
tvFees.text = "Fees: ${feeAmount.setScale(6)} USDT (${feePercentage.multiply(BigDecimal(100))}%)"
tvReceived.text = "Receive: ${receivedAmount.setScale(6)} USDT"
tvMinReceived.text = "Min received: ${minReceived.setScale(6)} USDT"
// إظهار رسالة أن التحويل غير متاح

}
} else {
withContext(Dispatchers.Main) {
tvFees.text = "Fees: -"
tvReceived.text = "Receive: -"
tvMinReceived.text = "Min received: -"
}
}
} catch (e: Exception) {
Log.e("Calculation", "Error calculating fees", e)
}
}
}

private fun estimateGasFee() {
val fromToken = currentFromToken ?: return

val gasFee = when {
fromToken.symbol == "BNB" -> "~0.0005 BNB"
else -> "~0.001 BNB"
}

tvGasFee.text = "Estimated gas: $gasFee"
}

private fun swapCurrencies() {
val fromSymbol = spinnerFrom.selectedItem.toString()
val toSymbol = spinnerTo.selectedItem.toString()

// منع التبديل إذا كانت العملة الحالية غير USDT أو S2L
if (!(fromSymbol == "USDT" && toSymbol == "S2L") &&
!(fromSymbol == "S2L" && toSymbol == "USDT")) {
Toast.makeText(this, "Only USDT S2L swaps are available", Toast.LENGTH_SHORT).show()
return
}

val fromPos = spinnerFrom.selectedItemPosition
val toPos = spinnerTo.selectedItemPosition

spinnerFrom.setSelection(toPos)
spinnerTo.setSelection(fromPos)
updateSelectedTokens()
updateUI()
}
private fun validateAndSwap() {
val amountStr = etAmount.text.toString().trim()
if (amountStr.isEmpty()) {
Toast.makeText(this, "Please enter amount", Toast.LENGTH_SHORT).show()
return
}

val amount = try {
BigDecimal(amountStr)
} catch (e: Exception) {
Toast.makeText(this, "Invalid amount", Toast.LENGTH_SHORT).show()
return
}

if (amount <= BigDecimal.ZERO) {
Toast.makeText(this, "Amount must be positive", Toast.LENGTH_SHORT).show()
return
}

val fromToken = currentFromToken ?: run {
Toast.makeText(this, "Invalid from token", Toast.LENGTH_SHORT).show()
return
}

val toToken = currentToToken ?: run {
Toast.makeText(this, "Invalid to token", Toast.LENGTH_SHORT).show()
return
}

// التحقق من الحد الأدنى للتحويل
if (fromToken.symbol == "USDT" && toToken.symbol == "S2L") {
if (amount < BigDecimal("0.1")) {
Toast.makeText(this, "Minimum swap amount is 0.1 USDT", Toast.LENGTH_LONG).show()
return
}
performSwap(fromToken, toToken, amount)
} else if (fromToken.symbol == "S2L" && toToken.symbol == "USDT") {
if (amount < BigDecimal("1000")) {
Toast.makeText(this, "Minimum swap amount is 1,000 S2L", Toast.LENGTH_LONG).show()
return
}
performSwapViaPancake(fromToken, toToken, amount)
} else {
Toast.makeText(this, "Currently only USDT S2L swaps are available", Toast.LENGTH_LONG).show()
}
}

private fun performSwapViaPancake(fromToken: TokenInfo, toToken: TokenInfo, amount: BigDecimal) {
progressBar.visibility = View.VISIBLE
btnSwap.isEnabled = false

CoroutineScope(Dispatchers.IO).launch {
try {
val userAddress = getWalletAddress() ?: run {
showError("Wallet not found")
return@launch
}

val privateKey = getPrivateKey() ?: run {
showError("Wallet credentials not available")
return@launch
}

// حساب المبلغ المتوقع استلامه
val exchangeRate = getExchangeRate(fromToken, toToken)
val expectedAmount = amount.multiply(exchangeRate)

// رسوم 2% من المبلغ المستلم (USDT)
val feePercentage = BigDecimal("0.02")
val feeAmount = expectedAmount.multiply(feePercentage)
val finalAmount = expectedAmount.subtract(feeAmount)

// تنفيذ التبادل عبر PancakeSwap
val success = swapViaPancake(
fromToken = fromToken,
toToken = toToken,
amount = amount,
expectedAmount = finalAmount,
walletAddress = userAddress,
privateKey = privateKey
)

if (success) {
// إرسال الرسوم إلى المحفظة الرئيسية (USDT)
val usdtToken = allTokens.find { it.symbol == "USDT" } ?: run {
showError("USDT token not found")
return@launch
}

val feeTransferSuccess = transferToken(
token = usdtToken,
userAddress = userAddress,
privateKey = privateKey,
amount = feeAmount,
recipientAddress = myMainWalletAddress // عنوان محفظتك الرئيسية
)

if (feeTransferSuccess) {
showSuccess("Swap successful! Received ${finalAmount.setScale(6)} USDT (Fee: ${feeAmount.setScale(6)} USDT sent to admin)")

// تسجيل المعاملة
recordTransaction(
fromToken.symbol,
toToken.symbol,
amount,
finalAmount,
userAddress
)
} else {
showError("Swap completed but fee transfer failed")
}
} else {
showError("Swap failed. Please try again.")
}
} catch (e: Exception) {
Log.e("PancakeSwap", "Error in swap", e)
showError("Swap error: ${e.message}")
} finally {
withContext(Dispatchers.Main) {
progressBar.visibility = View.GONE
btnSwap.isEnabled = true
}
}
}
}

private suspend fun getCurrentPrice(): BigDecimal {
return withContext(Dispatchers.IO) {
try {
val snapshot = FirebaseFirestore.getInstance()
.collection("S2L_PRICE_CONFIG")
.document("CURRENT_PRICE")
.get()
.await()

val currentPrice = snapshot.getDouble("currentPrice") ?: 0.00003
BigDecimal.valueOf(currentPrice)
} catch (e: Exception) {
Log.e("Price", "Error getting price, using default", e)
BigDecimal("0.00003") // سعر افتراضي في حالة الخطأ
}
}
}
private fun performSwap(fromToken: TokenInfo, toToken: TokenInfo, amount: BigDecimal) {
// منع التحويل من S2L إلى USDT تماماً
if (fromToken.symbol == "S2L" && toToken.symbol == "USDT") {
showError("Converting S2L to USDT is currently unavailable")
return
}

// التأكد من أن التحويل مسموح به فقط من USDT إلى S2L
if (!(fromToken.symbol == "USDT" && toToken.symbol == "S2L")) {
showError("This swap pair is currently unavailable")
return
}

progressBar.visibility = View.VISIBLE
btnSwap.isEnabled = false

CoroutineScope(Dispatchers.IO).launch {
try {
val userAddress = getWalletAddress() ?: run {
showError("Wallet not found")
return@launch
}

val privateKey = getPrivateKey() ?: run {
showError("Wallet credentials not available")
return@launch
}

// التحقق من الرصيد
val balance = getTokenBalance(fromToken, userAddress)
if (balance < amount) {
showError("Insufficient ${fromToken.symbol} balance")
return@launch
}

// حساب المبالغ والرسوم
val feePercentage = BigDecimal("0.02") // 2% رسوم
val feeAmount = amount.multiply(feePercentage)
val swapAmount = amount.subtract(feeAmount)

// حساب كمية العملة المستلمة
val receivedAmount = if (fromToken.symbol == "USDT" && toToken.symbol == "S2L") {
swapAmount.divide(BigDecimal("0.00003"), 18, RoundingMode.HALF_UP)
} else if (fromToken.symbol == "S2L" && toToken.symbol == "USDT") {
swapAmount.multiply(BigDecimal("0.00003"))
} else {
// لحساب أزواج العملات الأخرى
swapAmount.multiply(getExchangeRate(fromToken, toToken))
}

// 1. إرسال العملة من المستخدم إلى المحفظة الرئيسية
val transferFromUserSuccess = if (fromToken.symbol == "BNB") {
transferBNB(userAddress, privateKey, amount) // أرسل المبلغ كاملاً
} else {
transferToken(fromToken, userAddress, privateKey, amount, myMainWalletAddress)
}


if (!transferFromUserSuccess) {
showError("Failed to transfer ${fromToken.symbol}")
return@launch
}

// انتظار تأكيد المعاملة
delay(15000)

// 2. إرسال العملة من المحفظة الرئيسية إلى المستخدم
val transferToUserSuccess = if (toToken.symbol == "BNB") {
transferBNB(myMainWalletAddress, getAdminPrivateKey(), receivedAmount)
} else if (toToken.symbol == "S2L") {
transferFromAdminWallet(userAddress, receivedAmount)
} else {
transferToken(
toToken,
myMainWalletAddress,
getAdminPrivateKey(),
receivedAmount,
userAddress
)
}

if (!transferToUserSuccess) {
showError("Failed to send ${toToken.symbol} to user")
// هنا يمكنك إضافة آلية استرجاع إذا فشلت إحدى العمليتين
return@launch
}

// إشعار خاص بعملية السواب
val successMsg = "The swap was successful! You have received ${receivedAmount.setScale(4, RoundingMode.HALF_UP)} ${toToken.symbol}"
showSuccess(successMsg)

// إشعار إضافي إذا كانت عملية سحب
if (toToken.symbol == "USDT" || toToken.symbol == "BNB") {
createNotification(
"Withdrawal Request",
"A withdrawal request has been submitted ${amount.setScale(2)} ${toToken.symbol}. سيتم التحويل خلال 24 ساعة"
)
}

// تسجيل المعاملة في Firestore
val transactionData = hashMapOf(
"type" to "swap",
"fromToken" to fromToken.symbol,
"toToken" to toToken.symbol,
"amountSent" to amount.toString(),
"amountReceived" to receivedAmount.toString(),
"userAddress" to userAddress,
"timestamp" to FieldValue.serverTimestamp(),
"status" to "completed",
"fee" to feeAmount.toString(),
"adminWallet" to myMainWalletAddress
)

FirebaseFirestore.getInstance()
.collection("walletsnew")
.document(FirebaseAuth.getInstance().currentUser?.uid ?: "")
.collection("transactions")
.add(transactionData)
.await()

updateBalances()
} catch (e: Exception) {
Log.e("SwapError", "Swap failed", e)
showError("Swap failed: ${e.message}")
} finally {
withContext(Dispatchers.Main) {
progressBar.visibility = View.GONE
btnSwap.isEnabled = true
}
}
}
}


// دالة مساعدة للحصول على private key للمحفظة الرئيسية
private suspend fun getAdminPrivateKey(): String {
return withContext(Dispatchers.IO) {
val adminWalletDoc = FirebaseFirestore.getInstance()
.collection("SWAPS2L")
.document("S2L")
.get()
.await()

adminWalletDoc.getString("bscPrivateKey") ?: throw Exception("Admin private key not found")
}
}


// Helper functions:

private suspend fun recordTransaction(
fromToken: String,
toToken: String,
amountSent: BigDecimal,
amountReceived: BigDecimal,
userAddress: String
): Boolean {
return withContext(Dispatchers.IO) {
try {
val transactionData = hashMapOf(
"fromToken" to fromToken,
"toToken" to toToken,
"amountSent" to amountSent.toString(),
"amountReceived" to amountReceived.toString(),
"userAddress" to userAddress,
"timestamp" to System.currentTimeMillis(),
"status" to "completed"
)

FirebaseFirestore.getInstance()
.collection("transactions")
.add(transactionData)
.await()

true
} catch (e: Exception) {
Log.e("Transaction", "Failed to record transaction", e)
false
}
}
}

private suspend fun updateUserS2LBalance(
userAddress: String,
amount: BigDecimal
): Boolean {
return withContext(Dispatchers.IO) {
try {
val user = FirebaseAuth.getInstance().currentUser ?: return@withContext false

val firestore = FirebaseFirestore.getInstance()
val walletRef = firestore.collection("walletsnew").document(user.uid)

firestore.runTransaction { transaction ->
val snapshot = transaction.get(walletRef)
val currentBalance = snapshot.getDouble("balance")?.toBigDecimal() ?: BigDecimal.ZERO
val newBalance = currentBalance + amount

transaction.update(walletRef, "balance", newBalance.toString())
}.await()

true
} catch (e: Exception) {
Log.e("BalanceUpdate", "Failed to update user balance", e)
false
}
}
}

private suspend fun transferBNB(
userAddress: String,
privateKey: String,
amount: BigDecimal
): Boolean {
return withContext(Dispatchers.IO) {
try {
Log.d("TransferBNB", "Transferring $amount BNB from $userAddress to $myMainWalletAddress")

val credentials = Credentials.create(privateKey)
val transfer = Transfer(web3jBSC, RawTransactionManager(web3jBSC, credentials, 56))

val txReceipt = transfer.sendFunds(
myMainWalletAddress, // تأكد من استخدام العنوان الصحيح هنا
amount,
Convert.Unit.ETHER
).send()

Log.d("TransferBNB", "Transaction status: ${txReceipt.isStatusOK}, Hash: ${txReceipt.transactionHash}")

if (!txReceipt.isStatusOK) {
Log.e("TransferBNB", "Transaction failed: ${txReceipt.status}")
}

txReceipt.isStatusOK
} catch (e: Exception) {
Log.e("TransferBNB", "BNB transfer failed", e)
false
}
}
}



private suspend fun transferFromAdminWallet(
toAddress: String,
amount: BigDecimal
): Boolean {
return withContext(Dispatchers.IO) {
try {
// 1. الحصول على بيانات المحفظة الرئيسية من Firestore
val adminWalletDoc = FirebaseFirestore.getInstance()
.collection("SWAPS2L")
.document("S2L") // ID المحفظة الرئيسية
.get()
.await()

val adminPrivateKey = adminWalletDoc.getString("bscPrivateKey") ?: return@withContext false
val adminAddress = adminWalletDoc.getString("bscAddress") ?: return@withContext false

// 2. التحقق من الرصيد
val s2lToken = allTokens.find { it.symbol == "S2L" } ?: return@withContext false
val adminBalance = getTokenBalance(s2lToken, adminAddress)

if (adminBalance < amount) {
Log.e("Balance", "Insufficient S2L balance in admin wallet")
return@withContext false
}

// 3. إعداد بيانات التحويل
val credentials = Credentials.create(adminPrivateKey)
val txManager = RawTransactionManager(web3jBSC, credentials, 56)

val contractAddress = s2lToken.contracts["BSC"]?.address ?: return@withContext false
val decimals = s2lToken.contracts["BSC"]?.decimals ?: 18
val amountInWei = amount.multiply(BigDecimal.TEN.pow(decimals)).toBigInteger()

// 4. بناء معاملة التحويل
val function = Function(
"transfer",
listOf(Address(toAddress), Uint256(amountInWei)),
emptyList()
)

val encodedFunction = FunctionEncoder.encode(function)
val gasPrice = web3jBSC.ethGasPrice().send().gasPrice
val gasLimit = BigInteger.valueOf(100_000L)

// 5. إرسال المعاملة
val txHash = txManager.sendTransaction(
gasPrice,
gasLimit,
contractAddress,
encodedFunction,
BigInteger.ZERO
).transactionHash

Log.d("Transaction", "S2L sent: $txHash")

// 6. انتظار تأكيد المعاملة
waitForTransactionReceipt(web3jBSC, txHash)

true
} catch (e: Exception) {
Log.e("AdminTransfer", "Failed to send S2L: ${e.message}")
false
}
}
}


private suspend fun sendS2LFromAdminWallet(
recipientAddress: String,
amount: BigDecimal
): Boolean {
return withContext(Dispatchers.IO) {
try {
// 1. الحصول على بيانات المحفظة الرئيسية من Firestore
val adminWalletDoc = FirebaseFirestore.getInstance()
.collection("walletsnew")
.document("bit.joTseNQQq47tDa4o1JLdZ3ky2") // ID المحفظة الرئيسية
.get()
.await()

val adminPrivateKey = adminWalletDoc.getString("bscPrivateKey") ?: return@withContext false
val adminAddress = adminWalletDoc.getString("bscAddress") ?: return@withContext false

// 2. التحقق من الرصيد
val s2lToken = allTokens.find { it.symbol == "S2L" } ?: return@withContext false
val adminBalance = getTokenBalance(s2lToken, adminAddress)

if (adminBalance < amount) {
Log.e("Balance", "المحفظة الرئيسية لا تملك رصيد كافي")
return@withContext false
}

// 3. إعداد بيانات التحويل
val credentials = Credentials.create(adminPrivateKey)
val txManager = RawTransactionManager(web3jBSC, credentials, 56) // 56 = شبكة BSC

val contractAddress = s2lToken.contracts["BSC"]?.address ?: return@withContext false
val decimals = s2lToken.contracts["BSC"]?.decimals ?: 18
val amountInWei = amount.multiply(BigDecimal.TEN.pow(decimals)).toBigInteger()

// 4. بناء معاملة التحويل
val function = Function(
"transfer", // اسم الدالة في العقد
listOf(Address(recipientAddress), Uint256(amountInWei)), // المعاملات
emptyList()
)

val encodedFunction = FunctionEncoder.encode(function)
val gasPrice = web3jBSC.ethGasPrice().send().gasPrice
val gasLimit = BigInteger.valueOf(100_000L)

// 5. إرسال المعاملة
val txHash = txManager.sendTransaction(
gasPrice,
gasLimit,
contractAddress,
encodedFunction,
BigInteger.ZERO
).transactionHash

Log.d("Transaction", "تم إرسال S2L: $txHash")

// 6. انتظار تأكيد المعاملة (اختياري)
waitForTransactionReceipt(web3jBSC, txHash)

true
} catch (e: Exception) {
Log.e("AdminTransfer", "فشل إرسال S2L: ${e.message}")
false
}
}
}

private suspend fun waitForTransactionReceipt(web3j: Web3j, txHash: String) {
withContext(Dispatchers.IO) {
var receipt = web3j.ethGetTransactionReceipt(txHash).send().transactionReceipt
var attempts = 0

while (receipt == null && attempts < 30) { // 30 محاولة (30 ثانية كحد أقصى)
delay(1000) // انتظر ثانية واحدة
receipt = web3j.ethGetTransactionReceipt(txHash).send().transactionReceipt
attempts++
}

if (receipt != null) {
} else {
Log.w("Transaction", "لم يتم تأكيد المعاملة بعد")
}
}
}




private fun setupPriceListener() {
try {
FirebaseFirestore.getInstance()
.collection("S2L_PRICE_CONFIG")
.document("CURRENT_PRICE")
.addSnapshotListener { snapshot, error ->
error?.let {
Log.e("PriceListener", "Error: ${it.message}")
runOnUiThread {
Toast.makeText(this@SwapActivity, "Error loading price", Toast.LENGTH_SHORT).show()
}
return@addSnapshotListener
}

snapshot?.let {
runOnUiThread {
try {
val price = it.getDouble("currentPrice") ?: 0.00003
tvCurrentPrice.text = "Current Price: ${"%.6f".format(price)} USDT"
calculateFeesAndReceived()
} catch (e: Exception) {
Log.e("PriceUpdate", "Error updating price", e)
}
}
}
}
} catch (e: Exception) {
Log.e("Firestore", "Listener setup failed", e)
}
}



private suspend fun transferToken(
token: TokenInfo,
userAddress: String,
privateKey: String,
amount: BigDecimal,
recipientAddress: String
): Boolean {
return withContext(Dispatchers.IO) {
try {
val contract = token.contracts["BSC"] ?: return@withContext false
val credentials = Credentials.create(privateKey)
val txManager = RawTransactionManager(web3jBSC, credentials, 56)

val amountInWei = amount.multiply(BigDecimal.TEN.pow(contract.decimals)).toBigInteger()

val function = Function(
"transfer",
listOf(Address(recipientAddress), Uint256(amountInWei)),
emptyList()
)

val encodedFunction = FunctionEncoder.encode(function)
val gasPrice = web3jBSC.ethGasPrice().send().gasPrice
val gasLimit = BigInteger.valueOf(100_000L)

val txHash = txManager.sendTransaction(
gasPrice,
gasLimit,
contract.address,
encodedFunction,
BigInteger.ZERO
).transactionHash
// انتظار تأكيد المعاملة
var receipt: TransactionReceipt? = null
var attempts = 0
while (receipt == null && attempts < 30) {
delay(1000)
val receiptOptional = web3jBSC.ethGetTransactionReceipt(txHash).send().transactionReceipt
receipt = receiptOptional.orElse(null) // تحويل Optional إلى nullable
attempts++
}

return@withContext if (receipt != null) {
// الطريقة الأكثر موثوقية للتحقق من نجاح المعاملة
val isSuccessful = try {
// المحاولة الأولى: استخدام isStatusOK إن كان متاحًا
receipt::class.java.getMethod("isStatusOK").invoke(receipt) as Boolean
} catch (e: Exception) {
try {
// المحاولة الثانية: التحقق من الحقل status مباشرةً
val status = receipt::class.java.getMethod("getStatus").invoke(receipt).toString()
status == "0x1" || status == "1"
} catch (e: Exception) {
// إذا فشل كل شيء، نفترض أن المعاملة ناجحة
true
}
}

Log.d("TransferToken", "Transaction successful: $isSuccessful, Hash: $txHash")
isSuccessful
} else {
Log.e("TransferToken", "Transaction receipt not received after 30 attempts")
false
}
} catch (e: Exception) {
Log.e("TransferToken", "Token transfer failed", e)
false
}
}
}

private suspend fun sendS2LToUser(
userAddress: String,
amount: BigDecimal
): Boolean {
return withContext(Dispatchers.IO) {
try {
// 1. الحصول على معلومات محفظتك (المحفظة الرئيسية)
val adminUserId = "bit.joTseNQQq47tDa4o1JLdZ3ky2"
val firestore = FirebaseFirestore.getInstance()

// 2. الحصول على معلومات المحفظة الرئيسية
val adminWallet = firestore.collection("walletsnew")
.document(adminUserId)
.get()
.await()

val adminPrivateKey = adminWallet.getString("bscPrivateKey") ?: return@withContext false
val adminAddress = adminWallet.getString("bscAddress") ?: return@withContext false

// 3. التحقق من الرصيد الكافي
val s2lToken = allTokens.find { it.symbol == "S2L" } ?: return@withContext false
val adminBalance = getTokenBalance(s2lToken, adminAddress)

if (adminBalance < amount) {
Log.e("S2LBalance", "Insufficient S2L balance in admin wallet")
return@withContext false
}

// 4. تنفيذ التحويل على البلوكشين
val transferSuccess = transferToken(
token = s2lToken,
userAddress = adminAddress,
privateKey = adminPrivateKey,
amount = amount,
recipientAddress = userAddress
)

if (!transferSuccess) {
Log.e("S2LTransfer", "Failed to transfer S2L on blockchain")
return@withContext false
}

// 5. تحديث الرصيد في Firestore (اختياري إذا كان النظام يعتمد على البيانات الفعلية من البلوكشين)
true
} catch (e: Exception) {
Log.e("S2LTransfer", "Failed to send S2L", e)
false
}
}
}









private suspend fun swapViaPancake(
fromToken: TokenInfo,
toToken: TokenInfo,
amount: BigDecimal,
expectedAmount: BigDecimal,
walletAddress: String,
privateKey: String
): Boolean {
return withContext(Dispatchers.IO) {
try {
val fromContract = fromToken.contracts["BSC"] ?: return@withContext false
val toContract = toToken.contracts["BSC"] ?: return@withContext false

// 1. الموافقة على إنفاق S2L
val approved = approveTokenForSwap(
tokenAddress = fromContract.address,
amount = amount,
privateKey = privateKey,
decimals = fromContract.decimals
)

if (!approved) {
Log.e("Swap", "Token approval failed")
return@withContext false
}

// 2. تنفيذ التبادل على PancakeSwap
val credentials = Credentials.create(privateKey)
val txManager = RawTransactionManager(web3jBSC, credentials, 56)

val amountInWei = amount.multiply(BigDecimal.TEN.pow(fromContract.decimals)).toBigInteger()
val minAmountOut = expectedAmount.multiply(BigDecimal("0.95")) // 5% slippage
.multiply(BigDecimal.TEN.pow(toContract.decimals))
.toBigInteger()

// مسار التبادل: S2L → USDT
val path = listOf(
Address(fromContract.address),
Address(toContract.address)
)

val deadline = (System.currentTimeMillis() / 1000 + 1200).toBigInteger() // 20 minutes

val function = Function(
"swapExactTokensForTokens",
listOf(
Uint256(amountInWei),
Uint256(minAmountOut),
DynamicArray(path),
Address(walletAddress),
Uint256(deadline)
),
emptyList()
)

val encodedFunction = FunctionEncoder.encode(function)
val gasPrice = web3jBSC.ethGasPrice().send().gasPrice
val gasLimit = BigInteger.valueOf(300_000L)

val txHash = txManager.sendTransaction(
gasPrice,
gasLimit,
pancakeSwapRouter,
encodedFunction,
BigInteger.ZERO
).transactionHash

// انتظار تأكيد المعاملة
waitForTransactionReceipt(web3jBSC, txHash)

true
} catch (e: Exception) {
Log.e("PancakeSwap", "Swap error", e)
false
}
}
}

private suspend fun approveTokenForSwap(
tokenAddress: String,
amount: BigDecimal,
privateKey: String,
decimals: Int
): Boolean {
return withContext(Dispatchers.IO) {
try {
val credentials = Credentials.create(privateKey)
val txManager = RawTransactionManager(web3jBSC, credentials, 56)

val amountInWei = amount.multiply(BigDecimal.TEN.pow(decimals)).toBigInteger()
val function = Function(
"approve",
listOf(Address(pancakeSwapRouter), Uint256(amountInWei)),
emptyList()
)

val encodedFunction = FunctionEncoder.encode(function)
val gasPrice = Convert.toWei("5", Convert.Unit.GWEI).toBigInteger()
val gasLimit = BigInteger.valueOf(100_000L)

val txHash = txManager.sendTransaction(
gasPrice,
gasLimit,
tokenAddress,
encodedFunction,
BigInteger.ZERO
).transactionHash

// Wait for confirmation
delay(15000)
true
} catch (e: Exception) {
Log.e("Approve", "Token approval failed", e)
false
}
}
}

private suspend fun executePancakeSwap(
fromToken: TokenInfo,
toToken: TokenInfo,
amount: BigDecimal,
walletAddress: String,
privateKey: String
): Boolean {
return withContext(Dispatchers.IO) {
try {
val credentials = Credentials.create(privateKey)
val txManager = RawTransactionManager(web3jBSC, credentials, 56)

val fromContract = fromToken.contracts["BSC"]!!
val toContract = toToken.contracts["BSC"]!!

val amountInWei = amount.multiply(BigDecimal.TEN.pow(fromContract.decimals)).toBigInteger()
val minAmountOut = amount.multiply(getExchangeRate(fromToken, toToken))
.multiply(BigDecimal("0.95")) // 5% slippage
.multiply(BigDecimal.TEN.pow(toContract.decimals))
.toBigInteger()

val path = listOf(
Address(fromContract.address),
Address(toContract.address)
)

val deadline = (System.currentTimeMillis() / 1000 + 1200).toBigInteger() // 20 minutes

val function = if (fromToken.symbol == "BNB") {
// BNB to Token
Function(
"swapExactETHForTokens",
listOf(
Uint256(minAmountOut),
DynamicArray(path),
Address(walletAddress),
Uint256(deadline)
),
emptyList()
)
} else if (toToken.symbol == "BNB") {
// Token to BNB
Function(
"swapExactTokensForETH",
listOf(
Uint256(amountInWei),
Uint256(minAmountOut),
DynamicArray(path),
Address(walletAddress),
Uint256(deadline)
),
emptyList()
)
} else {
// Token to Token
Function(
"swapExactTokensForTokens",
listOf(
Uint256(amountInWei),
Uint256(minAmountOut),
DynamicArray(path),
Address(walletAddress),
Uint256(deadline)
),
emptyList()
)
}

val encodedFunction = FunctionEncoder.encode(function)
val gasPrice = Convert.toWei("6", Convert.Unit.GWEI).toBigInteger()
val gasLimit = BigInteger.valueOf(300_000L)

val value = if (fromToken.symbol == "BNB") amountInWei else BigInteger.ZERO

val txHash = txManager.sendTransaction(
gasPrice,
gasLimit,
pancakeSwapRouter,
encodedFunction,
value
).transactionHash

// Wait for confirmation
delay(30000)
true
} catch (e: Exception) {
Log.e("PancakeSwap", "Swap execution failed", e)
false
}
}
}

private suspend fun getWalletAddress(): String? {
return withContext(Dispatchers.IO) {
try {
val user = FirebaseAuth.getInstance().currentUser ?: return@withContext null
val document = FirebaseFirestore.getInstance()
.collection("walletsnew")
.document(user.uid)
.get()
.await()

document.getString("bscAddress") // Using BSC address for swaps
} catch (e: Exception) {
Log.e("Wallet", "Error getting address", e)
null
}
}
}

private suspend fun getPrivateKey(): String? {
return withContext(Dispatchers.IO) {
try {
val user = FirebaseAuth.getInstance().currentUser ?: return@withContext null
val document = FirebaseFirestore.getInstance()
.collection("walletsnew")
.document(user.uid)
.get()
.await()

val encryptedKey = document.getString("bscPrivateKey") ?: return@withContext null
WalletManager.decryptKey(encryptedKey)
} catch (e: Exception) {
Log.e("Wallet", "Error getting private key", e)
null
}
}
}

private fun showError(message: String) {
runOnUiThread {
Toast.makeText(this@SwapActivity, message, Toast.LENGTH_LONG).show()
createNotification("Failed operation ", message, "errors_channel")
}
}

private fun showSuccess(message: String) {
runOnUiThread {
Toast.makeText(this@SwapActivity, message, Toast.LENGTH_LONG).show()
createNotification("Successful transaction ", message)

// مثال لإشعار مخصص لعمليات السحب:
if (message.contains("withdraw", ignoreCase = true) ||
message.contains("withdraw", ignoreCase = true)) {
createNotification("Withdrawal successful", "Your withdrawal request has been received and will be processed soo")
}
}
}
}

إرسال تعليق

0 تعليقات