diff --git a/.github/workflows/cd.yml b/.github/workflows/cd.yml
index 4deafcaa4..92f66133c 100644
--- a/.github/workflows/cd.yml
+++ b/.github/workflows/cd.yml
@@ -27,6 +27,9 @@ jobs:
distribution: 'temurin'
java-version: 20.0.2+9
+ - name: Accept Android SDK licenses
+ run: yes | $ANDROID_SDK_ROOT/cmdline-tools/latest/bin/sdkmanager --licenses
+
- name: Decode signing certificate into a file
env:
CERTIFICATE_BASE64: ${{ secrets.ANDROID_DIST_SIGNING_KEY }}
diff --git a/.idea/gradle.xml b/.idea/gradle.xml
index fb6ee35d1..ba65d0894 100644
--- a/.idea/gradle.xml
+++ b/.idea/gradle.xml
@@ -47,6 +47,7 @@
+
@@ -69,6 +70,9 @@
+
+
+
diff --git a/.idea/kotlinc.xml b/.idea/kotlinc.xml
index d4b7accba..c224ad564 100644
--- a/.idea/kotlinc.xml
+++ b/.idea/kotlinc.xml
@@ -1,6 +1,6 @@
-
+
\ No newline at end of file
diff --git a/apps/signer/build.gradle.kts b/apps/signer/build.gradle.kts
index 10c0c5cb5..7f017ed7d 100644
--- a/apps/signer/build.gradle.kts
+++ b/apps/signer/build.gradle.kts
@@ -10,7 +10,7 @@ android {
defaultConfig {
applicationId = Build.namespacePrefix("signer")
minSdk = Build.minSdkVersion
- targetSdk = 34
+ targetSdk = Build.compileSdkVersion
versionCode = 22
versionName = "0.2.2"
}
@@ -58,36 +58,36 @@ android {
}
dependencies {
- implementation(Dependence.AndroidX.core)
- implementation(Dependence.AndroidX.appCompat)
- implementation(Dependence.AndroidX.activity)
- implementation(Dependence.AndroidX.fragment)
- implementation(Dependence.AndroidX.recyclerView)
- implementation(Dependence.AndroidX.viewPager2)
- implementation(Dependence.AndroidX.splashscreen)
+ implementation(libs.androidX.core)
+ implementation(libs.androidX.appCompat)
+ implementation(libs.androidX.activity)
+ implementation(libs.androidX.fragment)
+ implementation(libs.androidX.recyclerView)
+ implementation(libs.androidX.viewPager2)
+ implementation(libs.androidX.splashscreen)
- implementation(Dependence.UI.material)
- implementation(Dependence.UI.flexbox)
- implementation(Dependence.AndroidX.Camera.base)
- implementation(Dependence.AndroidX.Camera.core)
- implementation(Dependence.AndroidX.Camera.lifecycle)
- implementation(Dependence.AndroidX.Camera.view)
- implementation(Dependence.AndroidX.security)
- implementation(Dependence.AndroidX.constraintlayout)
- implementation(Dependence.AndroidX.lifecycleSavedState)
- implementation(project(Dependence.Lib.blockchain))
- implementation(project(Dependence.Lib.extensions))
+ implementation(libs.material)
+ implementation(libs.flexbox)
+ implementation(libs.cameraX.base)
+ implementation(libs.cameraX.core)
+ implementation(libs.cameraX.lifecycle)
+ implementation(libs.cameraX.view)
+ implementation(libs.androidX.security)
+ implementation(libs.androidX.constraintlayout)
+ implementation(libs.androidX.lifecycleSavedState)
+ implementation(project(ProjectModules.Lib.blockchain))
+ implementation(project(ProjectModules.Lib.extensions))
- implementation(Dependence.KotlinX.guava)
+ implementation(libs.kotlinX.coroutines.guava)
- implementation(project(Dependence.UIKit.core)) {
+ implementation(project(ProjectModules.UIKit.core)) {
exclude("com.airbnb.android", "lottie")
exclude("com.facebook.fresco", "fresco")
}
- implementation(project(Dependence.Lib.qr))
- implementation(project(Dependence.Lib.security))
- implementation(project(Dependence.Lib.icu))
- implementation(Dependence.Koin.core)
+ implementation(project(ProjectModules.Lib.qr))
+ implementation(project(ProjectModules.Lib.security))
+ implementation(project(ProjectModules.Lib.icu))
+ implementation(libs.koin.core)
}
diff --git a/apps/signer/src/main/java/com/tonapps/signer/screen/sign/SignViewModel.kt b/apps/signer/src/main/java/com/tonapps/signer/screen/sign/SignViewModel.kt
index 7a7724169..949e2d83f 100644
--- a/apps/signer/src/main/java/com/tonapps/signer/screen/sign/SignViewModel.kt
+++ b/apps/signer/src/main/java/com/tonapps/signer/screen/sign/SignViewModel.kt
@@ -200,7 +200,7 @@ class SignViewModel(
private fun formatCoins(coins: Coins): String {
val value = BigDecimal(coins.amount.toLong() / 1000000000L.toDouble())
- return CurrencyFormatter.format("TON", value, 9).toString()
+ return CurrencyFormatter.format("TON", value).toString()
}
private fun parseAddress(address: MsgAddressInt, bounceable: Boolean = true): String {
diff --git a/apps/wallet/api/build.gradle.kts b/apps/wallet/api/build.gradle.kts
index 38e336367..9dd4ebe08 100644
--- a/apps/wallet/api/build.gradle.kts
+++ b/apps/wallet/api/build.gradle.kts
@@ -2,6 +2,7 @@ plugins {
id("com.android.library")
id("org.jetbrains.kotlin.android")
id("kotlin-parcelize")
+ kotlin("plugin.serialization")
}
android {
@@ -23,16 +24,16 @@ android {
}
dependencies {
- implementation(Dependence.Koin.core)
- implementation(Dependence.KotlinX.guava)
- implementation(project(Dependence.Module.tonApi))
- implementation(project(Dependence.Lib.network))
- implementation(project(Dependence.Lib.blockchain))
- implementation(project(Dependence.Lib.extensions))
- implementation(project(Dependence.Lib.icu))
- implementation(Dependence.GooglePlay.cronet)
- implementation(Dependence.Squareup.okhttp)
- implementation(Dependence.Squareup.sse)
- implementation(Dependence.Squareup.moshi)
- implementation(Dependence.Squareup.moshiAdapters)
+ implementation(libs.kotlinX.serialization.core)
+ implementation(libs.kotlinX.serialization.json)
+ implementation(libs.kotlinX.coroutines.guava)
+ implementation(libs.koin.core)
+ implementation(project(ProjectModules.Module.tonApi))
+ implementation(project(ProjectModules.Lib.network))
+ implementation(project(ProjectModules.Lib.blockchain))
+ implementation(project(ProjectModules.Lib.extensions))
+ implementation(project(ProjectModules.Lib.icu))
+ implementation(libs.google.play.cronet)
+ implementation(libs.okhttp)
+ implementation(libs.okhttp.sse)
}
diff --git a/apps/wallet/api/src/main/java/com/tonapps/wallet/api/API.kt b/apps/wallet/api/src/main/java/com/tonapps/wallet/api/API.kt
index 3ff24163f..06ffd3a9f 100644
--- a/apps/wallet/api/src/main/java/com/tonapps/wallet/api/API.kt
+++ b/apps/wallet/api/src/main/java/com/tonapps/wallet/api/API.kt
@@ -3,7 +3,6 @@ package com.tonapps.wallet.api
import android.content.Context
import android.net.Uri
import android.util.ArrayMap
-import com.squareup.moshi.JsonAdapter
import com.tonapps.blockchain.ton.contract.BaseWalletContract
import com.tonapps.blockchain.ton.contract.WalletVersion
import com.tonapps.blockchain.ton.extensions.EmptyPrivateKeyEd25519
@@ -21,25 +20,27 @@ import com.tonapps.network.post
import com.tonapps.network.postJSON
import com.tonapps.network.requestBuilder
import com.tonapps.network.sse
-import com.tonapps.wallet.api.core.SourceAPI
import com.tonapps.wallet.api.entity.AccountDetailsEntity
import com.tonapps.wallet.api.entity.AccountEventEntity
import com.tonapps.wallet.api.entity.BalanceEntity
import com.tonapps.wallet.api.entity.ChartEntity
import com.tonapps.wallet.api.entity.ConfigEntity
+import com.tonapps.wallet.api.entity.EthenaEntity
import com.tonapps.wallet.api.entity.OnRampArgsEntity
import com.tonapps.wallet.api.entity.OnRampMerchantEntity
+import com.tonapps.wallet.api.entity.SwapEntity
import com.tonapps.wallet.api.entity.TokenEntity
import com.tonapps.wallet.api.internal.ConfigRepository
import com.tonapps.wallet.api.internal.InternalApi
+import com.tonapps.wallet.api.internal.SwapApi
import com.tonapps.wallet.api.tron.TronApi
-import io.batteryapi.apis.BatteryApi
-import io.batteryapi.apis.BatteryApi.UnitsGetBalance
+import io.Serializer
+import io.batteryapi.apis.DefaultApi
import io.batteryapi.models.Balance
import io.batteryapi.models.Config
+import io.batteryapi.models.EstimateGaslessCostRequest
import io.batteryapi.models.RechargeMethods
-import io.tonapi.infrastructure.ClientException
-import io.tonapi.infrastructure.Serializer
+import io.infrastructure.ClientException
import io.tonapi.models.Account
import io.tonapi.models.AccountAddress
import io.tonapi.models.AccountEvent
@@ -58,6 +59,8 @@ import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.emptyFlow
import kotlinx.coroutines.flow.filter
+import kotlinx.coroutines.flow.filterNotNull
+import kotlinx.coroutines.flow.map
import kotlinx.coroutines.withContext
import okhttp3.MediaType.Companion.toMediaType
import okhttp3.OkHttpClient
@@ -69,7 +72,6 @@ import org.json.JSONObject
import org.ton.api.pub.PublicKeyEd25519
import org.ton.cell.Cell
import org.ton.crypto.hex
-import java.math.BigDecimal
import java.util.Locale
class API(
@@ -78,6 +80,7 @@ class API(
) : CoreAPI(context) {
private val internalApi = InternalApi(context, defaultHttpClient, appVersionName)
+ private val swapApi = SwapApi(defaultHttpClient)
private val configRepository = ConfigRepository(context, scope, internalApi)
val config: ConfigEntity
@@ -93,8 +96,12 @@ class API(
private val bridgeUrl: String
get() = "${config.tonConnectBridgeHost}/bridge"
- @Volatile
- private var cachedCountry: String? = null
+ val country: String
+ get() = internalApi.country
+
+ fun setCountry(deviceCountry: String, storeCountry: String?) = internalApi.setCountry(deviceCountry, storeCountry)
+
+ suspend fun initConfig() = configRepository.initConfig()
suspend fun tonapiFetch(
url: String,
@@ -140,19 +147,14 @@ class API(
Provider(config.tonapiMainnetHost, config.tonapiTestnetHost, tonAPIHttpClient)
}
- private val batteryApi by lazy {
- SourceAPI(
- BatteryApi(config.batteryHost, tonAPIHttpClient),
- BatteryApi(config.batteryTestnetHost, tonAPIHttpClient)
- )
+ private val batteryProvider: BatteryProvider by lazy {
+ BatteryProvider(config.batteryHost, config.batteryTestnetHost, tonAPIHttpClient)
}
- private val emulationJSONAdapter: JsonAdapter by lazy {
- Serializer.moshi.adapter(MessageConsequences::class.java)
+ val tron: TronApi by lazy {
+ TronApi(config, defaultHttpClient, batteryProvider.default.get(false))
}
- val tron = TronApi(config, defaultHttpClient, batteryApi.get(false))
-
fun accounts(testnet: Boolean) = provider.accounts.get(testnet)
fun jettons(testnet: Boolean) = provider.jettons.get(testnet)
@@ -173,7 +175,11 @@ class API(
fun rates() = provider.rates.get(false)
- fun battery(testnet: Boolean) = batteryApi.get(testnet)
+ fun battery(testnet: Boolean) = batteryProvider.default.get(testnet)
+
+ fun batteryWallet(testnet: Boolean) = batteryProvider.wallet.get(testnet)
+
+ fun batteryEmulation(testnet: Boolean) = batteryProvider.emulation.get(testnet)
fun getBatteryConfig(testnet: Boolean): Config? {
return withRetry { battery(testnet).getConfig() }
@@ -183,22 +189,36 @@ class API(
return withRetry { battery(testnet).getRechargeMethods(false) }
}
- fun getOnRampData(country: String) = internalApi.getOnRampData(country)
+ fun getOnRampData() = internalApi.getOnRampData()
+
+ fun getOnRampPaymentMethods() = internalApi.getOnRampPaymentMethods()
- suspend fun calculateOnRamp(args: OnRampArgsEntity): List = withContext(Dispatchers.IO) {
- val data = internalApi.calculateOnRamp(args) ?: return@withContext emptyList()
- val items = JSONObject(data).getJSONArray("items")
- items.map { OnRampMerchantEntity(it) }
+ fun getOnRampMerchants() = internalApi.getOnRampMerchants()
+
+ fun getSwapAssets(): JSONArray = runCatching {
+ swapApi.getSwapAssets()?.let(::JSONArray)
+ }.getOrNull() ?: JSONArray()
+
+ @kotlin.Throws
+ suspend fun calculateOnRamp(args: OnRampArgsEntity): OnRampMerchantEntity.Data = withContext(Dispatchers.IO) {
+ val data = internalApi.calculateOnRamp(args) ?: throw Exception("Empty response")
+ val json = JSONObject(data)
+ val items = json.getJSONArray("items").map { OnRampMerchantEntity(it) }
+ val suggested = json.optJSONArray("suggested")?.map { OnRampMerchantEntity(it) } ?: emptyList()
+ OnRampMerchantEntity.Data(
+ items = items,
+ suggested = suggested
+ )
}
- suspend fun getEthenaStakingAPY(address: String): BigDecimal = withContext(Dispatchers.IO) {
- internalApi.getEthenaStakingAPY(address)
+ suspend fun getEthena(accountId: String): EthenaEntity? = withContext(Dispatchers.IO) {
+ withRetry { internalApi.getEthena(accountId) }
}
fun getBatteryBalance(
tonProofToken: String,
testnet: Boolean,
- units: UnitsGetBalance = UnitsGetBalance.ton
+ units: DefaultApi.UnitsGetBalance = DefaultApi.UnitsGetBalance.ton
): Balance? {
return withRetry { battery(testnet).getBalance(tonProofToken, units) }
}
@@ -210,7 +230,7 @@ class API(
private fun isOkStatus(testnet: Boolean): Boolean {
try {
val status = withRetry {
- provider.blockchain.get(testnet).status()
+ provider.utilities.get(testnet).status()
} ?: return false
if (!status.restOnline) {
return false
@@ -235,6 +255,12 @@ class API(
return seeHttpClient.sse(url, onFailure = onFailure)
}
+ suspend fun refreshConfig(testnet: Boolean) {
+ configRepository.refresh(testnet)
+ }
+
+ fun swapStream(from: SwapAssetParam, to: SwapAssetParam, userAddress: String) = swapApi.stream(from, to, userAddress)
+
suspend fun getPageTitle(url: String): String = withContext(Dispatchers.IO) {
try {
val headers = ArrayMap().apply {
@@ -276,6 +302,14 @@ class API(
"UQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJKZ"
}
+ suspend fun getDnsExpiring(
+ accountId: String,
+ testnet: Boolean,
+ period: Int
+ ) = withContext(Dispatchers.IO) {
+ withRetry { accounts(testnet).getAccountDnsExpiring(accountId, period).items } ?: emptyList()
+ }
+
fun getEvents(
accountId: String,
testnet: Boolean,
@@ -329,6 +363,7 @@ class API(
lt = event.lt,
inProgress = event.inProgress,
extra = 0L,
+ progress = 0f,
)
listOf(accountEvent)
}
@@ -400,7 +435,7 @@ class API(
accounts(testnet).getAccountJettonsBalances(
accountId = accountId,
currencies = currency?.let { listOf(it) },
- extensions = extensions,
+ supportedExtensions = extensions,
).balances
} ?: return null
return jettonsBalances.map { BalanceEntity(it) }.filter { it.value.isPositive }
@@ -439,7 +474,11 @@ class API(
val wallets = withRetry {
wallet(testnet).getWalletsByPublicKey(query).accounts
} ?: return emptyList()
- wallets.map { AccountDetailsEntity(query, it, testnet) }.map {
+ wallets.map { AccountDetailsEntity(
+ query = query,
+ wallet = it,
+ testnet = testnet
+ ) }.map {
if (it.walletVersion == WalletVersion.UNKNOWN) {
it.copy(
walletVersion = BaseWalletContract.resolveVersion(
@@ -566,7 +605,7 @@ class API(
cell: Cell,
testnet: Boolean,
): String? {
- val request = io.batteryapi.models.EstimateGaslessCostRequest(cell.base64(), false)
+ val request = EstimateGaslessCostRequest(cell.base64(), false)
return withRetry {
battery(testnet).estimateGaslessCost(jettonMaster, request, tonProofToken).commission
@@ -601,7 +640,11 @@ class API(
val withBattery = supportedByBattery && allowedByBattery
val string = response.body?.string() ?: return null
- val consequences = emulationJSONAdapter.fromJson(string) ?: return null
+ val consequences = try {
+ Serializer.JSON.decodeFromString(string)
+ } catch (e: Throwable) {
+ return null
+ }
return Pair(consequences, withBattery)
}
@@ -619,7 +662,7 @@ class API(
val request = EmulateMessageToWalletRequest(
boc = boc,
params = params,
- safeMode = safeModeEnabled
+ // safeMode = safeModeEnabled
)
withRetry {
emulation(testnet).emulateMessageToWallet(request)
@@ -667,12 +710,15 @@ class API(
return@withContext SendBlockchainState.STATUS_ERROR
}
+ val meta = hashMapOf(
+ "platform" to "android",
+ "version" to appVersionName,
+ "source" to source,
+ "confirmation_time" to confirmationTime.toString()
+ )
val request = SendBlockchainMessageRequest(
boc = boc,
- platform = "android",
- version = appVersionName,
- source = source,
- confirmationTime = confirmationTime
+ meta = meta
)
withRetry {
blockchain(testnet).sendBlockchainMessage(request)
@@ -889,17 +935,26 @@ class API(
}
}
- fun getServerTime(testnet: Boolean) = withRetry {
- liteServer(testnet).getRawTime().time
- } ?: (System.currentTimeMillis() / 1000).toInt()
-
- suspend fun resolveCountry(): String? = withContext(Dispatchers.IO) {
- if (cachedCountry == null) {
- cachedCountry = internalApi.resolveCountry()
+ fun getServerTime(testnet: Boolean): Int {
+ /*val time = serverTimeProvider.getServerTime(testnet)
+ if (time == null) {
+ val serverTimeSeconds = withRetry { liteServer(testnet).getRawTime().time }
+ if (serverTimeSeconds == null) {
+ return (System.currentTimeMillis() / 1000).toInt()
+ }
+ serverTimeProvider.setServerTime(testnet, serverTimeSeconds)
+ return serverTimeSeconds
+ }
+ return time*/
+ val serverTimeSeconds = withRetry { liteServer(testnet).getRawTime().time }
+ if (serverTimeSeconds == null) {
+ return (System.currentTimeMillis() / 1000).toInt()
}
- cachedCountry
+ return serverTimeSeconds
}
+ suspend fun resolveCountry(): String? = internalApi.resolveCountry()
+
suspend fun reportNtfSpam(
nftAddress: String,
scam: Boolean
diff --git a/apps/wallet/api/src/main/java/com/tonapps/wallet/api/BatteryProvider.kt b/apps/wallet/api/src/main/java/com/tonapps/wallet/api/BatteryProvider.kt
new file mode 100644
index 000000000..0ef717b48
--- /dev/null
+++ b/apps/wallet/api/src/main/java/com/tonapps/wallet/api/BatteryProvider.kt
@@ -0,0 +1,23 @@
+package com.tonapps.wallet.api
+
+import com.tonapps.wallet.api.core.BatteryAPI
+import com.tonapps.wallet.api.core.SourceAPI
+import okhttp3.OkHttpClient
+
+internal class BatteryProvider(
+ mainnetHost: String,
+ testnetHost: String,
+ okHttpClient: OkHttpClient,
+) {
+
+ private val main = BatteryAPI(mainnetHost, okHttpClient)
+ private val test = BatteryAPI(testnetHost, okHttpClient)
+
+ val default = SourceAPI(main.default, test.default)
+
+ val emulation = SourceAPI(main.emulation, test.emulation)
+
+ val wallet = SourceAPI(main.wallet, test.wallet)
+
+
+}
\ No newline at end of file
diff --git a/apps/wallet/api/src/main/java/com/tonapps/wallet/api/CoreAPI.kt b/apps/wallet/api/src/main/java/com/tonapps/wallet/api/CoreAPI.kt
index 906dcec0b..e11e3a200 100644
--- a/apps/wallet/api/src/main/java/com/tonapps/wallet/api/CoreAPI.kt
+++ b/apps/wallet/api/src/main/java/com/tonapps/wallet/api/CoreAPI.kt
@@ -26,7 +26,7 @@ abstract class CoreAPI(private val context: Context) {
val defaultHttpClient = baseOkHttpClientBuilder(
cronetEngine = { cronetEngine },
- timeoutSeconds = 15,
+ timeoutSeconds = 30,
interceptors = listOf(
UserAgentInterceptor(userAgent),
)
@@ -34,7 +34,7 @@ abstract class CoreAPI(private val context: Context) {
val seeHttpClient = baseOkHttpClientBuilder(
cronetEngine = { null },
- timeoutSeconds = 30,
+ timeoutSeconds = 60,
interceptors = listOf(
UserAgentInterceptor(userAgent),
)
@@ -71,7 +71,7 @@ abstract class CoreAPI(private val context: Context) {
private fun baseOkHttpClientBuilder(
cronetEngine: () -> CronetEngine?,
- timeoutSeconds: Long = 5,
+ timeoutSeconds: Long = 30,
interceptors: List = emptyList()
): OkHttpClient.Builder {
val builder = OkHttpClient().newBuilder()
diff --git a/apps/wallet/api/src/main/java/com/tonapps/wallet/api/Extensions.kt b/apps/wallet/api/src/main/java/com/tonapps/wallet/api/Extensions.kt
index a299e0091..3719a108f 100644
--- a/apps/wallet/api/src/main/java/com/tonapps/wallet/api/Extensions.kt
+++ b/apps/wallet/api/src/main/java/com/tonapps/wallet/api/Extensions.kt
@@ -1,35 +1,15 @@
package com.tonapps.wallet.api
import android.os.SystemClock
-import io.tonapi.infrastructure.Serializer
-import com.squareup.moshi.adapter
-import io.tonapi.infrastructure.ClientException
import android.util.Log
-import android.widget.Toast
-import com.google.firebase.crashlytics.BuildConfig
import com.google.firebase.crashlytics.FirebaseCrashlytics
import com.tonapps.network.OkHttpError
-import io.tonapi.infrastructure.ClientError
-import io.tonapi.infrastructure.Response
-import io.tonapi.infrastructure.ServerError
-import kotlinx.coroutines.delay
+import io.infrastructure.ClientError
+import io.infrastructure.ClientException
+import io.infrastructure.ServerError
import kotlinx.coroutines.CancellationException
import kotlinx.io.IOException
import java.net.SocketTimeoutException
-import java.net.UnknownHostException
-
-@OptIn(ExperimentalStdlibApi::class)
-inline fun toJSON(obj: T?): String {
- if (obj == null) {
- return ""
- }
- return Serializer.moshi.adapter().toJson(obj)
-}
-
-@OptIn(ExperimentalStdlibApi::class)
-inline fun fromJSON(json: String): T {
- return Serializer.moshi.adapter().fromJson(json)!!
-}
fun withRetry(
times: Int = 5,
@@ -44,14 +24,15 @@ fun withRetry(
} catch (e: CancellationException) {
throw e
} catch (e: SocketTimeoutException) {
+ Log.e("RetryLogNew", "SocketTimeoutException occurred: ${e.message}", e)
SystemClock.sleep(delay + 100)
return null
} catch (e: IOException) {
- Log.e("WithRetryLog", "IOException: ${e.message}", e)
+ Log.e("RetryLogNew", "IOException occurred: ${e.message}", e)
SystemClock.sleep(delay + 100)
return null
} catch (e: Throwable) {
- Log.e("WithRetryLog", "Error: ${e.message}", e)
+ Log.e("RetryLogNew", "Error occurred: ${e.message}", e)
val statusCode = e.getHttpStatusCode()
if (statusCode == 429 || statusCode == 401 || statusCode == 502 || statusCode == 520) {
SystemClock.sleep(delay + 100)
diff --git a/apps/wallet/api/src/main/java/com/tonapps/wallet/api/Provider.kt b/apps/wallet/api/src/main/java/com/tonapps/wallet/api/Provider.kt
index 05c65ed23..dd14fd69f 100644
--- a/apps/wallet/api/src/main/java/com/tonapps/wallet/api/Provider.kt
+++ b/apps/wallet/api/src/main/java/com/tonapps/wallet/api/Provider.kt
@@ -42,4 +42,6 @@ internal class Provider(
val wallet = SourceAPI(main.wallet, test.wallet)
val gasless = SourceAPI(main.gasless, test.gasless)
+
+ val utilities = SourceAPI(main.utilities, test.utilities)
}
\ No newline at end of file
diff --git a/apps/wallet/api/src/main/java/com/tonapps/wallet/api/ServerTimeProvider.kt b/apps/wallet/api/src/main/java/com/tonapps/wallet/api/ServerTimeProvider.kt
new file mode 100644
index 000000000..b409a3e61
--- /dev/null
+++ b/apps/wallet/api/src/main/java/com/tonapps/wallet/api/ServerTimeProvider.kt
@@ -0,0 +1,48 @@
+package com.tonapps.wallet.api
+
+import android.content.Context
+import android.os.SystemClock
+import com.tonapps.extensions.prefs
+import androidx.core.content.edit
+
+internal class ServerTimeProvider(context: Context) {
+
+ private companion object {
+ const val SERVER_TIME_KEY = "server_time"
+ const val LOCAL_TIME_KEY = "local_time"
+
+ // 24 hours in milliseconds
+ private const val CACHE_EXPIRATION_MS = 24 * 60 * 60 * 1000L
+
+ private fun getServerTimePrefKey(testnet: Boolean) = "${SERVER_TIME_KEY}_${if (testnet) "test" else "main"}"
+
+ private fun getLocalTimePrefKey(testnet: Boolean) = "${LOCAL_TIME_KEY}_${if (testnet) "test" else "main"}"
+
+ }
+
+ private val prefs = context.prefs("server_time")
+
+ fun setServerTime(testnet: Boolean, serverTimeSeconds: Int) {
+ val localTimeMillis = SystemClock.elapsedRealtime()
+
+ prefs.edit {
+ putInt(getServerTimePrefKey(testnet), serverTimeSeconds)
+ putLong(getLocalTimePrefKey(testnet), localTimeMillis)
+ }
+ }
+
+ fun getServerTime(testnet: Boolean): Int? {
+ val savedServerSeconds = prefs.getInt(getServerTimePrefKey(testnet), 0)
+ val savedLocalMillis = prefs.getLong(getLocalTimePrefKey(testnet), 0L)
+ if (0 >= savedServerSeconds || 0 >= savedLocalMillis) {
+ return null
+ }
+ val elapsedTimeMillis = SystemClock.elapsedRealtime() - savedLocalMillis
+ if (elapsedTimeMillis > CACHE_EXPIRATION_MS) {
+ return null
+ }
+ val elapsedSeconds = elapsedTimeMillis / 1000
+ val currentServerTime = savedServerSeconds + elapsedSeconds
+ return currentServerTime.toInt()
+ }
+}
\ No newline at end of file
diff --git a/apps/wallet/api/src/main/java/com/tonapps/wallet/api/SwapAssetParam.kt b/apps/wallet/api/src/main/java/com/tonapps/wallet/api/SwapAssetParam.kt
new file mode 100644
index 000000000..e1bb70ec2
--- /dev/null
+++ b/apps/wallet/api/src/main/java/com/tonapps/wallet/api/SwapAssetParam.kt
@@ -0,0 +1,24 @@
+package com.tonapps.wallet.api
+
+import android.net.Uri
+
+data class SwapAssetParam(
+ val address: String,
+ val amount: String?,
+) {
+
+ val isEmpty: Boolean
+ get() = amount.isNullOrBlank() || amount == "0"
+
+ fun apply(prefix: String, builder: Uri.Builder): Uri.Builder {
+ if (address.equals("ton", true)) {
+ builder.appendQueryParameter("${prefix}Asset", "0:0000000000000000000000000000000000000000000000000000000000000000")
+ } else {
+ builder.appendQueryParameter("${prefix}Asset", address)
+ }
+ amount?.let {
+ builder.appendQueryParameter("${prefix}Amount", it)
+ }
+ return builder
+ }
+}
\ No newline at end of file
diff --git a/apps/wallet/api/src/main/java/com/tonapps/wallet/api/core/BaseAPI.kt b/apps/wallet/api/src/main/java/com/tonapps/wallet/api/core/BaseAPI.kt
index f9f003714..f4627c99d 100644
--- a/apps/wallet/api/src/main/java/com/tonapps/wallet/api/core/BaseAPI.kt
+++ b/apps/wallet/api/src/main/java/com/tonapps/wallet/api/core/BaseAPI.kt
@@ -1,6 +1,5 @@
package com.tonapps.wallet.api.core
-import io.batteryapi.apis.BatteryApi
import io.tonapi.apis.AccountsApi
import io.tonapi.apis.BlockchainApi
import io.tonapi.apis.ConnectApi
@@ -15,6 +14,7 @@ import io.tonapi.apis.RatesApi
import io.tonapi.apis.StakingApi
import io.tonapi.apis.StorageApi
import io.tonapi.apis.TracesApi
+import io.tonapi.apis.UtilitiesApi
import io.tonapi.apis.WalletApi
import okhttp3.OkHttpClient
@@ -53,4 +53,6 @@ class BaseAPI(
val gasless: GaslessApi by lazy { GaslessApi(basePath, okHttpClient) }
+ val utilities: UtilitiesApi by lazy { UtilitiesApi(basePath, okHttpClient) }
+
}
\ No newline at end of file
diff --git a/apps/wallet/api/src/main/java/com/tonapps/wallet/api/core/BatteryAPI.kt b/apps/wallet/api/src/main/java/com/tonapps/wallet/api/core/BatteryAPI.kt
new file mode 100644
index 000000000..52703cddc
--- /dev/null
+++ b/apps/wallet/api/src/main/java/com/tonapps/wallet/api/core/BatteryAPI.kt
@@ -0,0 +1,18 @@
+package com.tonapps.wallet.api.core
+
+import io.batteryapi.apis.DefaultApi
+import io.batteryapi.apis.EmulationApi
+import io.batteryapi.apis.WalletApi
+import okhttp3.OkHttpClient
+
+class BatteryAPI(
+ basePath: String,
+ okHttpClient: OkHttpClient
+) {
+
+ val emulation: EmulationApi by lazy { EmulationApi(basePath, okHttpClient) }
+
+ val default: DefaultApi by lazy { DefaultApi(basePath, okHttpClient) }
+
+ val wallet: WalletApi by lazy { WalletApi(basePath, okHttpClient) }
+}
\ No newline at end of file
diff --git a/apps/wallet/api/src/main/java/com/tonapps/wallet/api/entity/AccountDetailsEntity.kt b/apps/wallet/api/src/main/java/com/tonapps/wallet/api/entity/AccountDetailsEntity.kt
index cb14f235a..2e1a7c20f 100644
--- a/apps/wallet/api/src/main/java/com/tonapps/wallet/api/entity/AccountDetailsEntity.kt
+++ b/apps/wallet/api/src/main/java/com/tonapps/wallet/api/entity/AccountDetailsEntity.kt
@@ -9,6 +9,7 @@ import com.tonapps.blockchain.ton.extensions.toRawAddress
import com.tonapps.blockchain.ton.extensions.toUserFriendly
import io.tonapi.models.Account
import io.tonapi.models.AccountStatus
+import io.tonapi.models.Wallet
import kotlinx.parcelize.Parcelize
@Parcelize
@@ -61,6 +62,22 @@ data class AccountDetailsEntity(
testnet = testnet,
)
+ constructor(
+ query: String,
+ wallet: Wallet,
+ testnet: Boolean,
+ new: Boolean = false
+ ) : this(
+ query = query,
+ preview = AccountEntity(wallet, testnet),
+ active = wallet.status == AccountStatus.active,
+ walletVersion = resolveVersion(testnet, wallet.interfaces, wallet.address),
+ balance = wallet.balance,
+ new = new,
+ initialized = wallet.status == AccountStatus.active || wallet.status == AccountStatus.frozen,
+ testnet = testnet,
+ )
+
private companion object {
private fun resolveVersion(testnet: Boolean, interfaces: List?, address: String): WalletVersion {
diff --git a/apps/wallet/api/src/main/java/com/tonapps/wallet/api/entity/AccountEntity.kt b/apps/wallet/api/src/main/java/com/tonapps/wallet/api/entity/AccountEntity.kt
index b72a2aa44..9d563eab0 100644
--- a/apps/wallet/api/src/main/java/com/tonapps/wallet/api/entity/AccountEntity.kt
+++ b/apps/wallet/api/src/main/java/com/tonapps/wallet/api/entity/AccountEntity.kt
@@ -9,8 +9,10 @@ import com.tonapps.blockchain.ton.extensions.toWalletAddress
import com.tonapps.extensions.short4
import io.tonapi.models.Account
import io.tonapi.models.AccountAddress
+import io.tonapi.models.Wallet
import kotlinx.parcelize.Parcelize
import org.ton.block.AddrStd
+import androidx.core.net.toUri
@Parcelize
data class AccountEntity(
@@ -43,7 +45,7 @@ data class AccountEntity(
address = model.address.toUserFriendly(model.isWallet, testnet),
accountId = model.address.toRawAddress(),
name = model.name,
- iconUri = model.icon?.let { Uri.parse(it) },
+ iconUri = model.icon?.toUri(),
isWallet = model.isWallet,
isScam = model.isScam
)
@@ -52,8 +54,17 @@ data class AccountEntity(
address = account.address.toUserFriendly(account.isWallet, testnet),
accountId = account.address.toRawAddress(),
name = account.name,
- iconUri = account.icon?.let { Uri.parse(it) },
+ iconUri = account.icon?.toUri(),
isWallet = account.isWallet,
isScam = account.isScam ?: false
)
+
+ constructor(wallet: Wallet, testnet: Boolean) : this(
+ address = wallet.address.toUserFriendly(wallet.isWallet, testnet),
+ accountId = wallet.address.toRawAddress(),
+ name = wallet.name,
+ iconUri = wallet.icon?.toUri(),
+ isWallet = wallet.isWallet,
+ isScam = false
+ )
}
diff --git a/apps/wallet/api/src/main/java/com/tonapps/wallet/api/entity/BalanceEntity.kt b/apps/wallet/api/src/main/java/com/tonapps/wallet/api/entity/BalanceEntity.kt
index d021ea68c..f70510830 100644
--- a/apps/wallet/api/src/main/java/com/tonapps/wallet/api/entity/BalanceEntity.kt
+++ b/apps/wallet/api/src/main/java/com/tonapps/wallet/api/entity/BalanceEntity.kt
@@ -55,6 +55,9 @@ data class BalanceEntity(
val customPayloadApiUri: String?
get() = token.customPayloadApiUri
+ val blockchain: Blockchain
+ get() = token.blockchain
+
constructor(jettonBalance: JettonBalance) : this(
token = TokenEntity(jettonBalance.jetton, jettonBalance.extensions, jettonBalance.lock),
value = Coins.of(BigDecimal(jettonBalance.balance).movePointLeft(jettonBalance.jetton.decimals), jettonBalance.jetton.decimals),
diff --git a/apps/wallet/api/src/main/java/com/tonapps/wallet/api/entity/ConfigEntity.kt b/apps/wallet/api/src/main/java/com/tonapps/wallet/api/entity/ConfigEntity.kt
index 44266a929..a82fdae0a 100644
--- a/apps/wallet/api/src/main/java/com/tonapps/wallet/api/entity/ConfigEntity.kt
+++ b/apps/wallet/api/src/main/java/com/tonapps/wallet/api/entity/ConfigEntity.kt
@@ -8,6 +8,7 @@ import com.tonapps.icu.Coins
import kotlinx.parcelize.IgnoredOnParcel
import kotlinx.parcelize.Parcelize
import org.json.JSONObject
+import androidx.core.net.toUri
@Parcelize
data class ConfigEntity(
@@ -35,7 +36,6 @@ data class ConfigEntity(
val batteryHost: String,
val batteryTestnetHost: String,
val batteryBeta: Boolean,
- val batteryDisabled: Boolean,
val batterySendDisabled: Boolean,
val batteryMeanFees: String,
val batteryMeanPriceNft: String,
@@ -59,15 +59,13 @@ data class ConfigEntity(
val apkDownloadUrl: String?,
val apkName: AppVersion?,
val tronApiUrl: String,
+ val enabledStaking: List,
+ val qrScannerExtends: List,
): Parcelable {
@IgnoredOnParcel
val swapUri: Uri
- get() = Uri.parse(stonfiUrl)
-
- @IgnoredOnParcel
- val isBatteryDisabled: Boolean
- get() = batteryDisabled || batterySendDisabled
+ get() = stonfiUrl.toUri()
@IgnoredOnParcel
val domains: List by lazy {
@@ -81,6 +79,26 @@ data class ConfigEntity(
ApkEntity(url, name)
}
+ @IgnoredOnParcel
+ val meanFees: Coins by lazy {
+ Coins.of(batteryMeanFees)
+ }
+
+ @IgnoredOnParcel
+ val meanFeeNft: Coins by lazy {
+ Coins.of(batteryMeanPriceNft)
+ }
+
+ @IgnoredOnParcel
+ val meanFeeSwap: Coins by lazy {
+ Coins.of(batteryMeanPriceSwap)
+ }
+
+ @IgnoredOnParcel
+ val meanFeeJetton: Coins by lazy {
+ Coins.of(batteryMeanPriceJetton)
+ }
+
constructor(json: JSONObject, debug: Boolean) : this(
empty = false,
supportLink = json.getString("supportLink"),
@@ -110,7 +128,6 @@ data class ConfigEntity(
batteryHost = json.optString("batteryHost", "https://battery.tonkeeper.com"),
batteryTestnetHost = json.optString("batteryTestnetHost", "https://testnet-battery.tonkeeper.com"),
batteryBeta = json.optBoolean("battery_beta", true),
- batteryDisabled = json.optBoolean("disable_battery", false),
batterySendDisabled = json.optBoolean("disable_battery_send", false),
batteryMeanFees = json.optString("batteryMeanFees", "0.0055"),
disableBatteryIapModule = json.optBoolean("disable_battery_iap_module", false),
@@ -136,6 +153,12 @@ data class ConfigEntity(
apkDownloadUrl = json.optString("apk_download_url"),
apkName = json.optString("apk_name")?.let { AppVersion(it.removePrefix("v")) },
tronApiUrl = json.optString("tron_api_url", "https://api.trongrid.io"),
+ enabledStaking = json.optJSONArray("enabled_staking")?.let { array ->
+ (0 until array.length()).map { array.getString(it) }
+ } ?: emptyList(),
+ qrScannerExtends = json.optJSONArray("qr_scanner_extends")?.let { array ->
+ QRScannerExtendsEntity.of(array)
+ } ?: emptyList()
)
constructor() : this(
@@ -163,7 +186,6 @@ data class ConfigEntity(
batteryHost = "https://battery.tonkeeper.com",
batteryTestnetHost = "https://testnet-battery.tonkeeper.com",
batteryBeta = true,
- batteryDisabled = false,
batterySendDisabled = false,
batteryMeanFees = "0.0055",
disableBatteryIapModule = false,
@@ -187,6 +209,8 @@ data class ConfigEntity(
apkDownloadUrl = null,
apkName = null,
tronApiUrl = "https://api.trongrid.io",
+ enabledStaking = emptyList(),
+ qrScannerExtends = emptyList()
)
companion object {
diff --git a/apps/wallet/api/src/main/java/com/tonapps/wallet/api/entity/EthenaEntity.kt b/apps/wallet/api/src/main/java/com/tonapps/wallet/api/entity/EthenaEntity.kt
new file mode 100644
index 000000000..e07c77c0b
--- /dev/null
+++ b/apps/wallet/api/src/main/java/com/tonapps/wallet/api/entity/EthenaEntity.kt
@@ -0,0 +1,87 @@
+package com.tonapps.wallet.api.entity
+
+import android.os.Parcelable
+import com.tonapps.extensions.map
+import kotlinx.parcelize.Parcelize
+import org.json.JSONObject
+import java.math.BigDecimal
+
+@Parcelize
+data class EthenaEntity(
+ val methods: List,
+ val about: About,
+) : Parcelable {
+
+ @Parcelize
+ data class About(
+ val description: String,
+ val tsusdeDescription: String,
+ val faqUrl: String,
+ val aboutUrl: String,
+ val stakeTitle: String,
+ val stakeDescription: String,
+ ) : Parcelable {
+ constructor(json: JSONObject) : this(
+ description = json.getString("description"),
+ tsusdeDescription = json.getString("tsusde_description"),
+ faqUrl = json.getString("faq_url"),
+ aboutUrl = json.getString("about_url"),
+ stakeTitle = json.getString("tsusde_stake_title"),
+ stakeDescription = json.getString("tsusde_stake_description"),
+ )
+ }
+
+ @Parcelize
+ data class Method(
+ val type: Type,
+ val name: String,
+ val apy: BigDecimal,
+ val apyTitle: String,
+ val apyDescription: String,
+ val bonusApy: BigDecimal?,
+ val bonusTitle: String?,
+ val bonusDescription: String?,
+ val eligibleBonusUrl: String?,
+ val depositUrl: String,
+ val withdrawalUrl: String,
+ val jettonMaster: String,
+ val links: List,
+ ) : Parcelable {
+ enum class Type(val id: String) {
+ STONFI("stonfi"),
+ AFFLUENT("affluent");
+
+ companion object {
+ fun fromId(id: String): Type {
+ return entries.find { it.id.equals(id, ignoreCase = true) }
+ ?: throw IllegalArgumentException("Invalid type: $id")
+ }
+ }
+ }
+
+ constructor(json: JSONObject) : this(
+ type = Type.fromId(json.getString("type")),
+ name = json.getString("name"),
+ apy = BigDecimal.valueOf(json.getDouble("apy")),
+ apyTitle = json.getString("apy_title"),
+ apyDescription = json.getString("apy_description"),
+ bonusApy = json.optString("bonus_apy").takeIf { it.isNotEmpty() }?.let {
+ BigDecimal(it)
+ },
+ bonusTitle = json.optString("apy_bonus_title"),
+ bonusDescription = json.optString("apy_bonus_description"),
+ eligibleBonusUrl = json.optString("eligible_bonus_url"),
+ depositUrl = json.getString("deposit_url"),
+ withdrawalUrl = json.getString("withdrawal_url"),
+ jettonMaster = json.getString("jetton_master"),
+ links = json.optJSONArray("links")?.let { array ->
+ (0 until array.length()).map { array.getString(it) }
+ } ?: emptyList()
+ )
+ }
+
+ constructor(json: JSONObject) : this(
+ methods = json.getJSONArray("methods").map { Method(it) },
+ about = About(json.getJSONObject("about"))
+ )
+}
diff --git a/apps/wallet/api/src/main/java/com/tonapps/wallet/api/entity/FlagsEntity.kt b/apps/wallet/api/src/main/java/com/tonapps/wallet/api/entity/FlagsEntity.kt
index 25d7eb95f..c35ea92ea 100644
--- a/apps/wallet/api/src/main/java/com/tonapps/wallet/api/entity/FlagsEntity.kt
+++ b/apps/wallet/api/src/main/java/com/tonapps/wallet/api/entity/FlagsEntity.kt
@@ -14,7 +14,14 @@ data class FlagsEntity(
val disableLegacyBlur: Boolean,
val disableSigner: Boolean,
val safeModeEnabled: Boolean,
-): Parcelable {
+ val disableStaking: Boolean,
+ val disableTron: Boolean,
+ val disableBattery: Boolean,
+ val disableGasless: Boolean,
+ val disableUsde: Boolean,
+ val disableNativeSwap: Boolean,
+ val disableOnboardingStory: Boolean
+) : Parcelable {
constructor(json: JSONObject) : this(
disableSwap = json.optBoolean("disable_swap", false),
@@ -23,7 +30,14 @@ data class FlagsEntity(
disableBlur = json.optBoolean("disable_blur", false),
disableLegacyBlur = json.optBoolean("disable_legacy_blur", false),
disableSigner = json.optBoolean("disable_signer", false),
- safeModeEnabled = json.optBoolean("safe_mode_enabled", false)
+ safeModeEnabled = json.optBoolean("safe_mode_enabled", false),
+ disableStaking = json.optBoolean("disable_staking", false),
+ disableTron = json.optBoolean("disable_tron", false),
+ disableBattery = json.optBoolean("disable_battery", false),
+ disableGasless = json.optBoolean("disable_gaseless", false),
+ disableUsde = json.optBoolean("disable_usde", false),
+ disableNativeSwap = json.optBoolean("disable_native_swap", false),
+ disableOnboardingStory = json.optBoolean("disable_onboarding_story", false)
)
constructor() : this(
@@ -33,6 +47,13 @@ data class FlagsEntity(
disableBlur = false,
disableLegacyBlur = false,
disableSigner = false,
- safeModeEnabled = false
+ safeModeEnabled = false,
+ disableStaking = false,
+ disableTron = false,
+ disableBattery = false,
+ disableGasless = false,
+ disableUsde = false,
+ disableNativeSwap = false,
+ disableOnboardingStory = false
)
}
\ No newline at end of file
diff --git a/apps/wallet/api/src/main/java/com/tonapps/wallet/api/entity/OnRampArgsEntity.kt b/apps/wallet/api/src/main/java/com/tonapps/wallet/api/entity/OnRampArgsEntity.kt
index 7b29ca647..48891bdc6 100644
--- a/apps/wallet/api/src/main/java/com/tonapps/wallet/api/entity/OnRampArgsEntity.kt
+++ b/apps/wallet/api/src/main/java/com/tonapps/wallet/api/entity/OnRampArgsEntity.kt
@@ -10,7 +10,6 @@ data class OnRampArgsEntity(
val wallet: String,
val purchaseType: String,
val amount: Coins,
- val country: String,
val paymentMethod: String?
) {
@@ -21,7 +20,6 @@ data class OnRampArgsEntity(
put("wallet", wallet)
put("purchase_type", purchaseType)
put("amount", amount.value.toPlainString())
- put("country", country)
paymentMethod?.let {
put("payment_method", it)
}
diff --git a/apps/wallet/api/src/main/java/com/tonapps/wallet/api/entity/OnRampMerchantEntity.kt b/apps/wallet/api/src/main/java/com/tonapps/wallet/api/entity/OnRampMerchantEntity.kt
index f854b6239..498fbe376 100644
--- a/apps/wallet/api/src/main/java/com/tonapps/wallet/api/entity/OnRampMerchantEntity.kt
+++ b/apps/wallet/api/src/main/java/com/tonapps/wallet/api/entity/OnRampMerchantEntity.kt
@@ -5,12 +5,26 @@ import org.json.JSONObject
data class OnRampMerchantEntity(
val merchant: String,
val amount: Double,
- val widgetUrl: String
+ val widgetUrl: String,
+ val minAmount: Double,
) {
+ data class Data(
+ val items: List = emptyList(),
+ val suggested: List = emptyList()
+ ) {
+
+ val isEmpty: Boolean
+ get() = items.isEmpty() && suggested.isEmpty()
+
+ val size: Int
+ get() = items.size + suggested.size
+ }
+
constructor(json: JSONObject) : this(
merchant = json.getString("merchant"),
amount = json.getDouble("amount"),
- widgetUrl = json.getString("widget_url")
+ widgetUrl = json.getString("widget_url"),
+ minAmount = json.optDouble("min_amount", 0.0)
)
}
\ No newline at end of file
diff --git a/apps/wallet/api/src/main/java/com/tonapps/wallet/api/entity/QRScannerExtendsEntity.kt b/apps/wallet/api/src/main/java/com/tonapps/wallet/api/entity/QRScannerExtendsEntity.kt
new file mode 100644
index 000000000..3ff6fadcd
--- /dev/null
+++ b/apps/wallet/api/src/main/java/com/tonapps/wallet/api/entity/QRScannerExtendsEntity.kt
@@ -0,0 +1,55 @@
+package com.tonapps.wallet.api.entity
+
+import android.os.Parcelable
+import android.util.Log
+import kotlinx.parcelize.IgnoredOnParcel
+import kotlinx.parcelize.Parcelize
+import org.json.JSONArray
+import org.json.JSONObject
+import java.net.URLEncoder
+import java.nio.charset.StandardCharsets
+
+@Parcelize
+data class QRScannerExtendsEntity(
+ val version: Int,
+ val regexp: String,
+ val url: String
+): Parcelable {
+
+ @IgnoredOnParcel
+ val regex: Regex by lazy {
+ Regex(regexp)
+ }
+
+ constructor(json: JSONObject) : this(
+ version = json.getInt("version"),
+ regexp = json.getString("regexp"),
+ url = json.getString("url")
+ )
+
+ fun isMatch(input: String): Boolean {
+ return regex.containsMatchIn(input)
+ }
+
+ fun buildUrl(input: String): String? {
+ if (!isMatch(input)) {
+ return null
+ }
+ val encoded = URLEncoder.encode(input, StandardCharsets.UTF_8.name())
+ return url.replace("{{QR_CODE}}", encoded)
+ }
+
+ companion object {
+
+ fun of(array: JSONArray): List {
+ return (0 until array.length()).mapNotNull {
+ val json = array.getJSONObject(it)
+ if (json.getInt("version") == 1) {
+ QRScannerExtendsEntity(array.getJSONObject(it))
+ } else {
+ null
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/apps/wallet/api/src/main/java/com/tonapps/wallet/api/entity/SwapEntity.kt b/apps/wallet/api/src/main/java/com/tonapps/wallet/api/entity/SwapEntity.kt
new file mode 100644
index 000000000..dc04e6d3e
--- /dev/null
+++ b/apps/wallet/api/src/main/java/com/tonapps/wallet/api/entity/SwapEntity.kt
@@ -0,0 +1,51 @@
+package com.tonapps.wallet.api.entity
+
+import io.Serializer
+import kotlinx.serialization.Serializable
+
+object SwapEntity {
+
+ @Serializable
+ data class Message(
+ val targetAddress: String,
+ val sendAmount: String,
+ val payload: String?,
+ )
+
+ @Serializable
+ data class Messages(
+ val messages: List,
+ val quoteId: String,
+ val resolverName: String,
+ val askUnits: String,
+ val bidUnits: String,
+ val protocolFeeUnits: String,
+ val tradeStartDeadline: String,
+ val gasBudget: String,
+ val estimatedGasConsumption: String,
+ val slippage: Int
+ ) {
+
+ val isEmpty: Boolean
+ get() = messages.isEmpty()
+ }
+
+ val empty = Messages(
+ messages = emptyList(),
+ quoteId = "",
+ resolverName = "",
+ askUnits = "",
+ bidUnits = "",
+ protocolFeeUnits = "",
+ tradeStartDeadline = "",
+ gasBudget = "",
+ estimatedGasConsumption = "",
+ slippage = 100
+ )
+
+ fun parse(data: String) = try {
+ Serializer.fromJSON(data)
+ } catch (ignored: Throwable) {
+ null
+ }
+}
\ No newline at end of file
diff --git a/apps/wallet/api/src/main/java/com/tonapps/wallet/api/entity/TokenEntity.kt b/apps/wallet/api/src/main/java/com/tonapps/wallet/api/entity/TokenEntity.kt
index 4e01f4633..0f5b40854 100644
--- a/apps/wallet/api/src/main/java/com/tonapps/wallet/api/entity/TokenEntity.kt
+++ b/apps/wallet/api/src/main/java/com/tonapps/wallet/api/entity/TokenEntity.kt
@@ -90,9 +90,14 @@ data class TokenEntity(
val TON_ICON_URI = Uri.Builder().scheme("res").path(R.drawable.ic_ton_with_bg.toString()).build()
val USDT_ICON_URI = Uri.Builder().scheme("res").path(R.drawable.ic_usdt_with_bg.toString()).build()
+ val USDE_ICON_URI = Uri.Builder().scheme("res").path(R.drawable.ic_udse_ethena_with_bg.toString()).build()
+ val TS_USDE_ICON_URI = Uri.Builder().scheme("res").path(R.drawable.ic_tsusde_with_bg.toString()).build()
const val TRC20_USDT = "TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t"
const val TON_USDT = "0:b113a994b5024a16719f69139328eb759596c38a25f59028b146fecdc3621dfe"
+ const val TON_USDE = "0:086fa2a675f74347b08dd4606a549b8fdb98829cb282bc1949d3b12fbaed9dcc"
+
+ const val TON_TS_USDE = "0:d0e545323c7acb7102653c073377f7e3c67f122eb94d430a250739f109d4a57d"
val TON = TokenEntity(
blockchain = Blockchain.TON,
@@ -133,6 +138,32 @@ data class TokenEntity(
customPayloadApiUri = null
)
+ val USDE = TokenEntity(
+ blockchain = Blockchain.TON,
+ address = TON_USDE,
+ name = "Ethena USDe",
+ symbol = "USDe",
+ imageUri = USDE_ICON_URI,
+ decimals = 6,
+ verification = Verification.whitelist,
+ isRequestMinting = false,
+ isTransferable = true,
+ customPayloadApiUri = null
+ )
+
+ val TS_USDE = TokenEntity(
+ blockchain = Blockchain.TON,
+ address = TON_TS_USDE,
+ name = "Ethena tsUSDe",
+ symbol = "tsUSDe",
+ imageUri = TS_USDE_ICON_URI,
+ decimals = 6,
+ verification = Verification.whitelist,
+ isRequestMinting = false,
+ isTransferable = true,
+ customPayloadApiUri = null
+ )
+
private fun convertVerification(verification: JettonVerificationType): Verification {
return when (verification) {
JettonVerificationType.whitelist -> Verification.whitelist
@@ -194,6 +225,6 @@ data class TokenEntity(
isRequestMinting = extensions?.contains(Extension.CustomPayload.value) == true,
isTransferable = extensions?.contains(Extension.NonTransferable.value) != true,
lock = lock?.let { Lock(it) },
- customPayloadApiUri = jetton.customPayloadApiUri
+ customPayloadApiUri = jetton.metadata.customPayloadApiUri
)
}
\ No newline at end of file
diff --git a/apps/wallet/api/src/main/java/com/tonapps/wallet/api/internal/ConfigRepository.kt b/apps/wallet/api/src/main/java/com/tonapps/wallet/api/internal/ConfigRepository.kt
index 7e0bed499..56d5fa014 100644
--- a/apps/wallet/api/src/main/java/com/tonapps/wallet/api/internal/ConfigRepository.kt
+++ b/apps/wallet/api/src/main/java/com/tonapps/wallet/api/internal/ConfigRepository.kt
@@ -1,7 +1,6 @@
package com.tonapps.wallet.api.internal
import android.content.Context
-import android.util.Log
import com.tonapps.extensions.file
import com.tonapps.extensions.toByteArray
import com.tonapps.extensions.toParcel
@@ -10,7 +9,6 @@ import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asStateFlow
-import kotlinx.coroutines.flow.filterNotNull
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
@@ -33,11 +31,11 @@ internal class ConfigRepository(
init {
scope.launch(Dispatchers.IO) {
- readCache()?.let {
- setConfig(it)
- }
- remote(false)?.let {
- setConfig(it)
+ val cached = readCache()
+ if (cached != null) {
+ setConfig(cached)
+ } else {
+ initConfig()
}
}
}
@@ -59,4 +57,15 @@ internal class ConfigRepository(
config
}
+ suspend fun refresh(testnet: Boolean) {
+ val config = remote(testnet) ?: return
+ setConfig(config)
+ }
+
+ suspend fun initConfig() {
+ remote(false)?.let {
+ setConfig(it)
+ }
+ }
+
}
\ No newline at end of file
diff --git a/apps/wallet/api/src/main/java/com/tonapps/wallet/api/internal/InternalApi.kt b/apps/wallet/api/src/main/java/com/tonapps/wallet/api/internal/InternalApi.kt
index ee70b799f..b34dd98f3 100644
--- a/apps/wallet/api/src/main/java/com/tonapps/wallet/api/internal/InternalApi.kt
+++ b/apps/wallet/api/src/main/java/com/tonapps/wallet/api/internal/InternalApi.kt
@@ -4,19 +4,21 @@ import android.content.Context
import android.net.Uri
import android.util.ArrayMap
import android.util.Log
+import androidx.core.net.toUri
import com.google.firebase.crashlytics.FirebaseCrashlytics
-import com.tonapps.extensions.deviceCountry
-import com.tonapps.extensions.getStoreCountry
import com.tonapps.extensions.isDebug
import com.tonapps.extensions.locale
import com.tonapps.extensions.map
import com.tonapps.network.get
import com.tonapps.network.postJSON
import com.tonapps.wallet.api.entity.ConfigEntity
+import com.tonapps.wallet.api.entity.EthenaEntity
import com.tonapps.wallet.api.entity.NotificationEntity
import com.tonapps.wallet.api.entity.OnRampArgsEntity
import com.tonapps.wallet.api.entity.StoryEntity
+import com.tonapps.wallet.api.entity.SwapEntity
import com.tonapps.wallet.api.withRetry
+import io.Serializer
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.withContext
@@ -31,12 +33,24 @@ internal class InternalApi(
private val appVersionName: String
) {
+ private var _deviceCountry: String? = null
+ private var _storeCountry: String? = null
+
+ val country: String
+ get() = _storeCountry ?: _deviceCountry ?: Locale.getDefault().country.uppercase()
+
+ fun setCountry(deviceCountry: String, storeCountry: String?) {
+ _deviceCountry = deviceCountry.uppercase()
+ _storeCountry = storeCountry?.uppercase()
+ }
+
private fun endpoint(
path: String,
testnet: Boolean,
platform: String,
build: String,
boot: Boolean = false,
+ queryParams: Map = emptyMap(),
): String = runBlocking {
val builder = Uri.Builder()
builder.scheme("https")
@@ -48,11 +62,16 @@ internal class InternalApi(
.appendQueryParameter("chainName", if (testnet) "testnet" else "mainnet")
.appendQueryParameter("bundle_id", context.packageName)
- val storeCountry = context.getStoreCountry()
- storeCountry?.let {
- builder.appendQueryParameter("store_country_code", storeCountry)
+ _storeCountry?.let {
+ builder.appendQueryParameter("store_country_code", it)
+ }
+ _deviceCountry?.let {
+ builder.appendQueryParameter("device_country_code", it)
+ }
+
+ queryParams.forEach {
+ builder.appendQueryParameter(it.key, it.value)
}
- builder.appendQueryParameter("device_country_code", context.deviceCountry)
builder.build().toString()
}
@@ -64,8 +83,9 @@ internal class InternalApi(
build: String = appVersionName,
locale: Locale,
boot: Boolean = false,
+ queryParams: Map = emptyMap(),
): JSONObject {
- val url = endpoint(path, testnet, platform, build, boot)
+ val url = endpoint(path, testnet, platform, build, boot, queryParams)
val headers = ArrayMap()
headers["Accept-Language"] = locale.toString()
val body = withRetry {
@@ -74,18 +94,45 @@ internal class InternalApi(
return JSONObject(body)
}
- fun getOnRampData(country: String) = withRetry {
- okHttpClient.get("https://swap.tonkeeper.com/v2/onramp/currencies?country=${country.uppercase()}")
+ private fun swapEndpoint(path: String): String {
+ val builder = "https://swap.tonkeeper.com".toUri().buildUpon()
+ .appendEncodedPath(path)
+ _deviceCountry?.let {
+ builder.appendQueryParameter("device_country_code", _deviceCountry)
+ builder.appendQueryParameter("country", _storeCountry ?: _deviceCountry)
+ }
+ _storeCountry?.let {
+ builder.appendQueryParameter("store_country_code", _storeCountry)
+ }
+
+ return builder.build().toString()
+ }
+
+ fun getSwapAssets() = withRetry {
+ okHttpClient.get(swapEndpoint("v2/swap/assets"))
+ }
+
+ fun getOnRampData() = withRetry {
+ okHttpClient.get(swapEndpoint("v2/onramp/currencies"))
}
- fun getEthenaStakingAPY(address: String): BigDecimal = withRetry {
- val json = request("ethena/staking?address=$address", false, locale = context.locale)
- BigDecimal.valueOf(json.getDouble("value"))
- } ?: BigDecimal.ZERO
+ fun getOnRampPaymentMethods() = withRetry {
+ okHttpClient.get(swapEndpoint("v2/onramp/payment_methods"))
+ }
- fun calculateOnRamp(args: OnRampArgsEntity) = withRetry {
- val url = "https://swap.tonkeeper.com/v2/onramp/calculate"
- okHttpClient.postJSON(url, args.toJSON().toString()).body?.string()
+ fun getOnRampMerchants() = withRetry {
+ okHttpClient.get(swapEndpoint("v2/onramp/merchants"))
+ }
+
+ fun calculateOnRamp(args: OnRampArgsEntity): String? {
+ val json = args.toJSON()
+ _deviceCountry?.let { json.put("country", _deviceCountry) }
+ return withRetry {
+ okHttpClient.postJSON(
+ swapEndpoint("v2/onramp/calculate"),
+ json.toString()
+ ).body.string()
+ }
}
fun getNotifications(): List {
@@ -113,7 +160,11 @@ internal class InternalApi(
val telegramBots = domains.filter { it.startsWith("@") }.map { "t.me/${it.substring(1)}" }
val maskDomains = domains.filter { it.startsWith("*.") }
val cleanDomains = domains.filter { domain ->
- !domain.startsWith("@") && !domain.startsWith("*.") && maskDomains.none { mask -> domain.endsWith(".$mask") }
+ !domain.startsWith("@") && !domain.startsWith("*.") && maskDomains.none { mask ->
+ domain.endsWith(
+ ".$mask"
+ )
+ }
}
return (maskDomains + cleanDomains + telegramBots).toTypedArray()
@@ -173,4 +224,14 @@ internal class InternalApi(
}
}
+ fun getEthena(accountId: String): EthenaEntity? = withRetry {
+ val json = request(
+ "staking/ethena",
+ false,
+ locale = context.locale,
+ queryParams = mapOf("address" to accountId)
+ )
+ EthenaEntity(json)
+ }
+
}
\ No newline at end of file
diff --git a/apps/wallet/api/src/main/java/com/tonapps/wallet/api/internal/SwapApi.kt b/apps/wallet/api/src/main/java/com/tonapps/wallet/api/internal/SwapApi.kt
new file mode 100644
index 000000000..8bb42e6e3
--- /dev/null
+++ b/apps/wallet/api/src/main/java/com/tonapps/wallet/api/internal/SwapApi.kt
@@ -0,0 +1,36 @@
+package com.tonapps.wallet.api.internal
+
+import androidx.core.net.toUri
+import com.tonapps.network.get
+import com.tonapps.network.sse
+import com.tonapps.wallet.api.SwapAssetParam
+import com.tonapps.wallet.api.entity.SwapEntity
+import com.tonapps.wallet.api.withRetry
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.emptyFlow
+import kotlinx.coroutines.flow.map
+import okhttp3.OkHttpClient
+
+internal class SwapApi(
+ private val okHttpClient: OkHttpClient
+) {
+
+ fun getSwapAssets() = withRetry {
+ okHttpClient.get("https://swap.tonkeeper.com/v2/swap/assets")
+ }
+
+ fun stream(from: SwapAssetParam, to: SwapAssetParam, userAddress: String): Flow {
+ if (from.isEmpty && to.isEmpty) {
+ return emptyFlow()
+ }
+ val builder = "https://swap.tonkeeper.com/v2/swap/omniston/stream".toUri().buildUpon()
+ from.apply("from", builder)
+ to.apply("to", builder)
+ builder.appendQueryParameter("userAddress", userAddress)
+ val url = builder.build().toString()
+ return okHttpClient.sse(url) { }.map {
+ SwapEntity.parse(it.data)
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/apps/wallet/api/src/main/java/com/tonapps/wallet/api/omniston/Omniston.kt b/apps/wallet/api/src/main/java/com/tonapps/wallet/api/omniston/Omniston.kt
new file mode 100644
index 000000000..28010a31f
--- /dev/null
+++ b/apps/wallet/api/src/main/java/com/tonapps/wallet/api/omniston/Omniston.kt
@@ -0,0 +1,155 @@
+package com.tonapps.wallet.api.omniston
+
+import kotlinx.serialization.Serializable
+
+object Omniston {
+
+ fun fixAddress(address: String): String {
+ if (address.equals("ton", true)) {
+ return "EQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAM9c"
+ }
+ return address
+ }
+
+ @Serializable
+ data class AssetAddress(
+ val blockchain: Int = 607,
+ val address: String
+ )
+
+ @Serializable
+ data class Amount(
+ val offer_units: String? = null,
+ val ask_units: String? = null
+ )
+
+ @Serializable
+ data class SettlementParams(
+ val max_price_slippage_bps: Int = 500,
+ val max_outgoing_messages: Int = 4
+ )
+
+ @Serializable
+ data class QuoteParams(
+ val offer_asset_address: AssetAddress,
+ val ask_asset_address: AssetAddress,
+ val amount: Amount,
+ val referrer_fee_bps: Int = 0,
+ val settlement_methods: List = listOf(0),
+ val settlement_params: SettlementParams
+ )
+
+ @Serializable
+ data class QuoteResult(
+ val quote_id: String,
+ val offer_asset_address: AssetAddress,
+ val ask_asset_address: AssetAddress,
+ val offer_amount: Amount,
+ val ask_amount: Amount,
+ val rate: String,
+ val expires_at: Long,
+ val settlement_methods: List
+ )
+
+ @Serializable
+ data class EventWrapper(
+ val jsonrpc: String,
+ val method: String,
+ val params: EventParams
+ )
+
+ @Serializable
+ data class EventParams(
+ val subscription: Long,
+ val result: EventResult
+ )
+
+ @Serializable
+ data class EventResult(
+ val event: Event
+ )
+
+ @Serializable
+ data class Event(
+ val quote_updated: QuoteUpdated? = null,
+ )
+
+ @Serializable
+ data class QuoteUpdated(
+ val quote_id: String,
+ val resolver_id: String,
+ val resolver_name: String,
+ val offer_asset_address: AssetAddress,
+ val ask_asset_address: AssetAddress,
+ val offer_units: String,
+ val ask_units: String,
+ val referrer_address: String?,
+ val referrer_fee_units: String,
+ val protocol_fee_units: String,
+ val quote_timestamp: Long,
+ val trade_start_deadline: Long,
+ val gas_budget: String,
+ val estimated_gas_consumption: String,
+ val referrer_fee_asset: AssetAddress,
+ val protocol_fee_asset: AssetAddress,
+ val params: SwapParams
+ )
+
+ @Serializable
+ data class SwapParams(
+ val swap: SwapDetails
+ )
+
+ @Serializable
+ data class SwapDetails(
+ val routes: List
+ )
+
+ @Serializable
+ data class Route(
+ val steps: List,
+ val gas_budget: String
+ )
+
+ @Serializable
+ data class Step(
+ val offer_asset_address: AssetAddress,
+ val ask_asset_address: AssetAddress,
+ val chunks: List
+ )
+
+ @Serializable
+ data class Chunk(
+ val protocol: String,
+ val offer_amount: String,
+ val ask_amount: String,
+ val extra_version: Int,
+ val extra: List
+ )
+
+ @Serializable
+ data class TonEventResult(
+ val ton: TonPayload
+ )
+
+ @Serializable
+ data class TonPayload(
+ val messages: List
+ )
+
+ @Serializable
+ data class TonMessage(
+ val target_address: String,
+ val send_amount: String,
+ val payload: String
+ )
+
+ @Serializable
+ data class TransactionBuildTransfer(
+ val destination_address: AssetAddress,
+ val gas_excess_address: AssetAddress,
+ val source_address: AssetAddress,
+ val quote: QuoteUpdated,
+ )
+
+}
\ No newline at end of file
diff --git a/apps/wallet/api/src/main/java/com/tonapps/wallet/api/tron/TronApi.kt b/apps/wallet/api/src/main/java/com/tonapps/wallet/api/tron/TronApi.kt
index 407d8d9fe..53fd2f052 100644
--- a/apps/wallet/api/src/main/java/com/tonapps/wallet/api/tron/TronApi.kt
+++ b/apps/wallet/api/src/main/java/com/tonapps/wallet/api/tron/TronApi.kt
@@ -16,7 +16,7 @@ import com.tonapps.wallet.api.tron.entity.TronEstimationEntity
import com.tonapps.wallet.api.tron.entity.TronEventEntity
import com.tonapps.wallet.api.tron.entity.TronResourcesEntity
import com.tonapps.wallet.api.withRetry
-import io.batteryapi.apis.BatteryApi
+import io.batteryapi.apis.DefaultApi
import io.batteryapi.models.TronSendRequest
import io.ktor.util.encodeBase64
import kotlinx.serialization.json.buildJsonObject
@@ -28,7 +28,7 @@ import java.math.BigInteger
class TronApi(
private val config: ConfigEntity,
private val okHttpClient: OkHttpClient,
- private val batteryApi: BatteryApi
+ private val batteryApi: DefaultApi
) {
companion object {
diff --git a/apps/wallet/api/src/main/res/drawable/ic_tsusde_with_bg.png b/apps/wallet/api/src/main/res/drawable/ic_tsusde_with_bg.png
new file mode 100644
index 000000000..da88b1238
Binary files /dev/null and b/apps/wallet/api/src/main/res/drawable/ic_tsusde_with_bg.png differ
diff --git a/apps/wallet/data/account/build.gradle.kts b/apps/wallet/data/account/build.gradle.kts
index dd77e2ecf..38b476884 100644
--- a/apps/wallet/data/account/build.gradle.kts
+++ b/apps/wallet/data/account/build.gradle.kts
@@ -9,24 +9,24 @@ android {
}
dependencies {
- implementation(Dependence.KotlinX.serializationJSON)
- implementation(Dependence.KotlinX.coroutines)
- implementation(Dependence.Koin.core)
- implementation(Dependence.TON.tvm)
- implementation(Dependence.TON.crypto)
- implementation(Dependence.TON.tlb)
- implementation(Dependence.TON.blockTlb)
- implementation(Dependence.TON.tonapiTl)
- implementation(Dependence.TON.contract)
- implementation(project(Dependence.Module.tonApi))
- implementation(project(Dependence.Wallet.Data.core))
- implementation(project(Dependence.Wallet.Data.rn))
- implementation(project(Dependence.Wallet.Data.rates))
- implementation(project(Dependence.Wallet.api))
- implementation(project(Dependence.Lib.security))
- implementation(project(Dependence.Lib.network))
- implementation(project(Dependence.Lib.extensions))
- implementation(project(Dependence.Lib.blockchain))
- implementation(project(Dependence.Lib.sqlite))
- implementation(project(Dependence.Lib.ledger))
+ implementation(libs.kotlinX.serialization.json)
+ implementation(libs.kotlinX.coroutines.android)
+ implementation(libs.koin.core)
+ implementation(libs.ton.tvm)
+ implementation(libs.ton.crypto)
+ implementation(libs.ton.tlb)
+ implementation(libs.ton.blockTlb)
+ implementation(libs.ton.tonapiTl)
+ implementation(libs.ton.contract)
+ implementation(project(ProjectModules.Module.tonApi))
+ implementation(project(ProjectModules.Wallet.Data.core))
+ implementation(project(ProjectModules.Wallet.Data.rn))
+ implementation(project(ProjectModules.Wallet.Data.rates))
+ implementation(project(ProjectModules.Wallet.api))
+ implementation(project(ProjectModules.Lib.security))
+ implementation(project(ProjectModules.Lib.network))
+ implementation(project(ProjectModules.Lib.extensions))
+ implementation(project(ProjectModules.Lib.blockchain))
+ implementation(project(ProjectModules.Lib.sqlite))
+ implementation(project(ProjectModules.Lib.ledger))
}
diff --git a/apps/wallet/data/backup/build.gradle.kts b/apps/wallet/data/backup/build.gradle.kts
index fa81eb7ab..f9429764e 100644
--- a/apps/wallet/data/backup/build.gradle.kts
+++ b/apps/wallet/data/backup/build.gradle.kts
@@ -8,8 +8,8 @@ android {
}
dependencies {
- implementation(project(Dependence.Lib.sqlite))
- implementation(project(Dependence.Lib.extensions))
- implementation(project(Dependence.Wallet.Data.rn))
+ implementation(project(ProjectModules.Lib.sqlite))
+ implementation(project(ProjectModules.Lib.extensions))
+ implementation(project(ProjectModules.Wallet.Data.rn))
}
diff --git a/apps/wallet/data/battery/build.gradle.kts b/apps/wallet/data/battery/build.gradle.kts
index 56d40c87c..631851974 100644
--- a/apps/wallet/data/battery/build.gradle.kts
+++ b/apps/wallet/data/battery/build.gradle.kts
@@ -8,16 +8,14 @@ android {
}
dependencies {
- implementation(Dependence.Squareup.moshi)
- implementation(Dependence.Squareup.moshiAdapters)
- implementation(Dependence.Squareup.okhttp)
+ implementation(libs.okhttp)
- implementation(project(Dependence.Module.tonApi))
- implementation(project(Dependence.Wallet.Data.core))
- implementation(project(Dependence.Wallet.api))
- implementation(project(Dependence.Lib.blockchain))
- implementation(project(Dependence.Lib.extensions))
- implementation(project(Dependence.Lib.network))
- implementation(project(Dependence.Lib.icu))
- implementation(project(Dependence.Lib.security))
+ implementation(project(ProjectModules.Module.tonApi))
+ implementation(project(ProjectModules.Wallet.Data.core))
+ implementation(project(ProjectModules.Wallet.api))
+ implementation(project(ProjectModules.Lib.blockchain))
+ implementation(project(ProjectModules.Lib.extensions))
+ implementation(project(ProjectModules.Lib.network))
+ implementation(project(ProjectModules.Lib.icu))
+ implementation(project(ProjectModules.Lib.security))
}
diff --git a/apps/wallet/data/browser/build.gradle.kts b/apps/wallet/data/browser/build.gradle.kts
index 403a84b4d..d4430de92 100644
--- a/apps/wallet/data/browser/build.gradle.kts
+++ b/apps/wallet/data/browser/build.gradle.kts
@@ -8,13 +8,13 @@ android {
}
dependencies {
- implementation(Dependence.Squareup.okhttp)
+ implementation(libs.okhttp)
- implementation(project(Dependence.Wallet.api))
- implementation(project(Dependence.Wallet.Data.core))
- implementation(project(Dependence.Wallet.Data.account))
+ implementation(project(ProjectModules.Wallet.api))
+ implementation(project(ProjectModules.Wallet.Data.core))
+ implementation(project(ProjectModules.Wallet.Data.account))
- implementation(project(Dependence.Lib.network))
- implementation(project(Dependence.Lib.extensions))
+ implementation(project(ProjectModules.Lib.network))
+ implementation(project(ProjectModules.Lib.extensions))
}
diff --git a/apps/wallet/data/browser/src/main/java/com/tonapps/wallet/data/browser/BrowserRepository.kt b/apps/wallet/data/browser/src/main/java/com/tonapps/wallet/data/browser/BrowserRepository.kt
index e191773bd..861c54f8a 100644
--- a/apps/wallet/data/browser/src/main/java/com/tonapps/wallet/data/browser/BrowserRepository.kt
+++ b/apps/wallet/data/browser/src/main/java/com/tonapps/wallet/data/browser/BrowserRepository.kt
@@ -41,6 +41,9 @@ class BrowserRepository(context: Context, api: API) {
}
suspend fun isTrustedApp(country: String, testnet: Boolean, locale: Locale, deeplink: Uri): Boolean {
+ if (deeplink.host == "dapp.aeon.xyz" || deeplink.host == "tonkeeper.com" || deeplink.host?.endsWith(".tonkeeper.com") == true) {
+ return true
+ }
val host = deeplink.host ?: return false
val apps = getApps(country, testnet, locale)
for (app in apps) {
diff --git a/apps/wallet/data/collectibles/build.gradle.kts b/apps/wallet/data/collectibles/build.gradle.kts
index 158b9210b..aa2a5a682 100644
--- a/apps/wallet/data/collectibles/build.gradle.kts
+++ b/apps/wallet/data/collectibles/build.gradle.kts
@@ -8,10 +8,10 @@ android {
}
dependencies {
- implementation(project(Dependence.Module.tonApi))
- implementation(project(Dependence.Wallet.Data.core))
- implementation(project(Dependence.Wallet.api))
- implementation(project(Dependence.Lib.blockchain))
- implementation(project(Dependence.Lib.extensions))
- implementation(project(Dependence.Lib.sqlite))
+ implementation(project(ProjectModules.Module.tonApi))
+ implementation(project(ProjectModules.Wallet.Data.core))
+ implementation(project(ProjectModules.Wallet.api))
+ implementation(project(ProjectModules.Lib.blockchain))
+ implementation(project(ProjectModules.Lib.extensions))
+ implementation(project(ProjectModules.Lib.sqlite))
}
diff --git a/apps/wallet/data/collectibles/src/main/java/com/tonapps/wallet/data/collectibles/CollectiblesRepository.kt b/apps/wallet/data/collectibles/src/main/java/com/tonapps/wallet/data/collectibles/CollectiblesRepository.kt
index ef5be2757..bb9c01368 100644
--- a/apps/wallet/data/collectibles/src/main/java/com/tonapps/wallet/data/collectibles/CollectiblesRepository.kt
+++ b/apps/wallet/data/collectibles/src/main/java/com/tonapps/wallet/data/collectibles/CollectiblesRepository.kt
@@ -3,12 +3,19 @@ package com.tonapps.wallet.data.collectibles
import android.content.Context
import android.util.Log
import com.google.firebase.crashlytics.FirebaseCrashlytics
+import com.tonapps.blockchain.ton.extensions.equalsAddress
import com.tonapps.wallet.api.API
+import com.tonapps.wallet.api.withRetry
+import com.tonapps.wallet.data.collectibles.entities.DnsExpiringEntity
import com.tonapps.wallet.data.collectibles.entities.NftEntity
import com.tonapps.wallet.data.collectibles.entities.NftListResult
import com.tonapps.wallet.data.collectibles.source.LocalDataSource
+import io.extensions.renderType
+import io.tonapi.models.TrustType
+import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.cancellable
import kotlinx.coroutines.flow.flow
+import kotlinx.coroutines.withContext
class CollectiblesRepository(
private val context: Context,
@@ -19,6 +26,24 @@ class CollectiblesRepository(
LocalDataSource(context)
}
+ suspend fun getDnsExpiring(accountId: String, testnet: Boolean, period: Int) = api.getDnsExpiring(accountId, testnet, period).map { model ->
+ DnsExpiringEntity(
+ expiringAt = model.expiringAt,
+ name = model.name,
+ dnsItem = model.dnsItem?.let { NftEntity(it, testnet) }
+ )
+ }.sortedBy { it.daysUntilExpiration }
+
+ suspend fun getDnsSoonExpiring(accountId: String, testnet: Boolean, period: Int = 30) = getDnsExpiring(accountId, testnet, period)
+
+ suspend fun getDnsNftExpiring(
+ accountId: String,
+ testnet: Boolean,
+ nftAddress: String
+ ) = getDnsExpiring(accountId, testnet, 366).firstOrNull {
+ it.dnsItem?.address?.equalsAddress(nftAddress) == true
+ }
+
fun getNft(accountId: String, testnet: Boolean, address: String): NftEntity? {
val nft = localDataSource.getSingle(accountId, testnet, address)
if (nft != null) {
@@ -64,7 +89,7 @@ class CollectiblesRepository(
): List? {
val nftItems = api.getNftItems(address, testnet) ?: return null
val items = nftItems.filter {
- it.trust != "blacklist" && it.metadata["render_type"] != "hidden"
+ it.trust != TrustType.blacklist && it.renderType != "hidden"
}.map { NftEntity(it, testnet) }
localDataSource.save(address, testnet, items.toList())
diff --git a/apps/wallet/data/collectibles/src/main/java/com/tonapps/wallet/data/collectibles/entities/DnsExpiringEntity.kt b/apps/wallet/data/collectibles/src/main/java/com/tonapps/wallet/data/collectibles/entities/DnsExpiringEntity.kt
new file mode 100644
index 000000000..afdc90908
--- /dev/null
+++ b/apps/wallet/data/collectibles/src/main/java/com/tonapps/wallet/data/collectibles/entities/DnsExpiringEntity.kt
@@ -0,0 +1,37 @@
+package com.tonapps.wallet.data.collectibles.entities
+
+import android.os.Parcelable
+import com.tonapps.blockchain.ton.extensions.toRawAddress
+import kotlinx.parcelize.IgnoredOnParcel
+import kotlinx.parcelize.Parcelize
+
+@Parcelize
+data class DnsExpiringEntity(
+ val expiringAt: Long,
+ val name: String,
+ val dnsItem: NftEntity? = null
+): Parcelable {
+
+ @IgnoredOnParcel
+ val addressRaw: String by lazy {
+ dnsItem?.address?.toRawAddress() ?: ""
+ }
+
+ @IgnoredOnParcel
+ val inSale: Boolean by lazy {
+ dnsItem?.inSale ?: false
+ }
+
+ @IgnoredOnParcel
+ val daysUntilExpiration: Int by lazy {
+ val currentTime = System.currentTimeMillis() / 1000
+ val remainingSeconds = expiringAt - currentTime
+
+ if (remainingSeconds <= 0) {
+ 0
+ } else {
+ (remainingSeconds / (24 * 60 * 60)).toInt()
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/apps/wallet/data/collectibles/src/main/java/com/tonapps/wallet/data/collectibles/entities/NftEntity.kt b/apps/wallet/data/collectibles/src/main/java/com/tonapps/wallet/data/collectibles/entities/NftEntity.kt
index eaeb39552..6e3362af0 100644
--- a/apps/wallet/data/collectibles/src/main/java/com/tonapps/wallet/data/collectibles/entities/NftEntity.kt
+++ b/apps/wallet/data/collectibles/src/main/java/com/tonapps/wallet/data/collectibles/entities/NftEntity.kt
@@ -119,6 +119,6 @@ data class NftEntity(
verified = item.approvedBy.isNotEmpty(),
inSale = item.sale != null,
dns = item.dns,
- trust = Trust(item.trust),
+ trust = Trust(item.trust.value),
)
}
\ No newline at end of file
diff --git a/apps/wallet/data/collectibles/src/main/java/com/tonapps/wallet/data/collectibles/entities/NftMetadataEntity.kt b/apps/wallet/data/collectibles/src/main/java/com/tonapps/wallet/data/collectibles/entities/NftMetadataEntity.kt
index c7e52f642..6e3eaff71 100644
--- a/apps/wallet/data/collectibles/src/main/java/com/tonapps/wallet/data/collectibles/entities/NftMetadataEntity.kt
+++ b/apps/wallet/data/collectibles/src/main/java/com/tonapps/wallet/data/collectibles/entities/NftMetadataEntity.kt
@@ -3,6 +3,7 @@ package com.tonapps.wallet.data.collectibles.entities
import android.os.Parcelable
import android.util.Base64
import android.util.Log
+import io.JsonAny
import kotlinx.parcelize.IgnoredOnParcel
import kotlinx.parcelize.Parcelize
@@ -43,12 +44,18 @@ data class NftMetadataEntity(
"https://c.tonapi.io/json?url=$encoded"
}
- constructor(map: Map) : this(
- strings = map.filter { it.value is String }.mapValues { it.value as String } as HashMap,
- buttons = map["buttons"]?.let { buttons ->
- (buttons as List