Skip to content

Commit

Permalink
feat: battery (#65)
Browse files Browse the repository at this point in the history
  • Loading branch information
sorokin0andrey committed Sep 2, 2024
1 parent ed07566 commit 8282498
Show file tree
Hide file tree
Showing 223 changed files with 9,154 additions and 294 deletions.
1 change: 1 addition & 0 deletions .idea/gradle.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

105 changes: 102 additions & 3 deletions apps/wallet/api/src/main/java/com/tonapps/wallet/api/API.kt
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package com.tonapps.wallet.api
import android.content.Context
import android.util.ArrayMap
import android.util.Log
import com.squareup.moshi.JsonAdapter
import com.tonapps.blockchain.ton.extensions.EmptyPrivateKeyEd25519
import com.tonapps.blockchain.ton.extensions.base64
import com.tonapps.blockchain.ton.extensions.isValidTonAddress
Expand All @@ -17,13 +18,20 @@ import com.tonapps.network.interceptor.AuthorizationInterceptor
import com.tonapps.network.post
import com.tonapps.network.postJSON
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.BalanceEntity
import com.tonapps.wallet.api.entity.ChartEntity
import com.tonapps.wallet.api.entity.ConfigEntity
import com.tonapps.wallet.api.entity.TokenEntity
import com.tonapps.wallet.api.internal.ConfigRepository
import com.tonapps.wallet.api.internal.InternalApi
import io.batteryapi.apis.BatteryApi
import io.batteryapi.apis.BatteryApi.UnitsGetBalance
import io.batteryapi.models.Balance
import io.batteryapi.models.Config
import io.batteryapi.models.RechargeMethods
import io.tonapi.infrastructure.Serializer
import io.tonapi.models.Account
import io.tonapi.models.AccountAddress
import io.tonapi.models.AccountEvent
Expand Down Expand Up @@ -67,6 +75,10 @@ class API(
)
}

private val batteryHttpClient: OkHttpClient by lazy {
createBatteryAPIHttpClient(context)
}

private val internalApi = InternalApi(context, defaultHttpClient)
private val configRepository = ConfigRepository(context, scope, internalApi)

Expand All @@ -88,6 +100,14 @@ class API(
Provider(config.tonapiMainnetHost, config.tonapiTestnetHost, tonAPIHttpClient)
}

private val batteryApi by lazy {
SourceAPI(BatteryApi(config.batteryHost, batteryHttpClient), BatteryApi(config.batteryTestnetHost, batteryHttpClient))
}

private val emulationJSONAdapter: JsonAdapter<MessageConsequences> by lazy {
Serializer.moshi.adapter(MessageConsequences::class.java)
}

fun accounts(testnet: Boolean) = provider.accounts.get(testnet)

fun jettons(testnet: Boolean) = provider.jettons.get(testnet)
Expand All @@ -108,11 +128,29 @@ class API(

fun rates() = provider.rates.get(false)

fun getAlertNotifications() = withRetry {
fun battery(testnet: Boolean) = batteryApi.get(testnet)

suspend fun getBatteryConfig(testnet: Boolean): Config? {
return withRetry { battery(testnet).getConfig() }
}

suspend fun getBatteryRechargeMethods(testnet: Boolean): RechargeMethods? {
return withRetry { battery(testnet).getRechargeMethods(false) }
}

suspend fun getBatteryBalance(
tonProofToken: String,
testnet: Boolean,
units: UnitsGetBalance = UnitsGetBalance.ton
): Balance? {
return withRetry { battery(testnet).getBalance(tonProofToken, units) }
}

suspend fun getAlertNotifications() = withRetry {
internalApi.getNotifications()
} ?: emptyList()

private fun isOkStatus(testnet: Boolean): Boolean {
suspend fun isOkStatus(testnet: Boolean): Boolean {
try {
val status = withRetry {
provider.blockchain.get(testnet).status()
Expand Down Expand Up @@ -165,7 +203,6 @@ class API(
return listOf(accountEvent)
}


fun getTokenEvents(
tokenAddress: String,
accountId: String,
Expand Down Expand Up @@ -355,6 +392,36 @@ class API(
}
}

suspend fun emulateWithBattery(
tonProofToken: String,
cell: Cell,
testnet: Boolean
) = emulateWithBattery(tonProofToken, cell.base64(), testnet)

suspend fun emulateWithBattery(
tonProofToken: String,
boc: String,
testnet: Boolean
): Pair<MessageConsequences, Boolean>? {
val host = if (testnet) config.batteryTestnetHost else config.batteryHost
val url = "$host/wallet/emulate"
val data = "{\"boc\":\"$boc\"}"

val response = withRetry {
tonAPIHttpClient.postJSON(url, data, ArrayMap<String, String>().apply {
set("X-TonConnect-Auth", tonProofToken)
})
} ?: return null

val supportedByBattery = response.headers["supported-by-battery"] == "true"
val allowedByBattery = response.headers["allowed-by-battery"] == "true"
val withBattery = supportedByBattery && allowedByBattery

val string = response.body?.string() ?: return null
val consequences = emulationJSONAdapter.fromJson(string) ?: return null
return Pair(consequences, withBattery)
}

suspend fun emulate(
boc: String,
testnet: Boolean,
Expand All @@ -380,13 +447,37 @@ class API(
return emulate(cell.base64(), testnet, address, balance)
}

suspend fun sendToBlockchainWithBattery(
boc: Cell,
tonProofToken: String,
testnet: Boolean,
) = sendToBlockchainWithBattery(boc.base64(), tonProofToken, testnet)

suspend fun sendToBlockchainWithBattery(
boc: String,
tonProofToken: String,
testnet: Boolean,
): Boolean = withContext(Dispatchers.IO) {
if (!isOkStatus(testnet)) {
return@withContext false
}

val request = io.batteryapi.models.EmulateMessageToWalletRequest(boc)

withRetry {
battery(testnet).sendMessage(tonProofToken, request)
true
} ?: false
}

suspend fun sendToBlockchain(
boc: String,
testnet: Boolean
): Boolean = withContext(Dispatchers.IO) {
if (!isOkStatus(testnet)) {
return@withContext false
}

val request = SendBlockchainMessageRequest(boc)
withRetry {
blockchain(testnet).sendBlockchainMessage(request)
Expand Down Expand Up @@ -649,5 +740,13 @@ class API(
)
)).build()
}

private fun createBatteryAPIHttpClient(
context: Context,
): OkHttpClient {
return baseOkHttpClientBuilder()
.addInterceptor(AcceptLanguageInterceptor(context.locale))
.build()
}
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
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
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package com.tonapps.wallet.api.entity

import android.os.Parcelable
import android.util.Log
import com.tonapps.icu.Coins
import io.tonapi.models.JettonBalance
import io.tonapi.models.TokenRates
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,12 +26,29 @@ data class ConfigEntity(
val faqUrl: String,
val aptabaseEndpoint: String,
val aptabaseAppKey: String,
val scamEndpoint: String
val scamEndpoint: String,
val batteryHost: String,
val batteryTestnetHost: String,
val batteryBeta: Boolean,
val batteryDisabled: Boolean,
val batterySendDisabled: Boolean,
val batteryMeanFees: String,
val batteryMeanPriceNft: String,
val batteryMeanPriceSwap: String,
val batteryMeanPriceJetton: String,
val disableBatteryCryptoRecharge: Boolean,
val batteryReservedAmount: String,
val batteryMaxInputAmount: String,
val batteryRefundEndpoint: String,
val batteryPromoDisabled: Boolean,
): Parcelable {

val swapUri: Uri
get() = Uri.parse(stonfiUrl)

val isBatteryDisabled: Boolean
get() = batteryDisabled || batterySendDisabled

constructor(json: JSONObject, debug: Boolean) : this(
supportLink = json.getString("supportLink"),
nftExplorer = json.getString("NFTOnExplorerUrl"),
Expand All @@ -55,10 +72,22 @@ data class ConfigEntity(
faqUrl = json.getString("faq_url"),
aptabaseEndpoint = json.getString("aptabaseEndpoint"),
aptabaseAppKey = json.getString("aptabaseAppKey"),
scamEndpoint = json.optString("scamEndpoint", "https://scam.tonkeeper.com")
) {
Log.d("ConfigLog", "ConfigEntity: $json")
}
scamEndpoint = json.optString("scamEndpoint", "https://scam.tonkeeper.com"),
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"),
disableBatteryCryptoRecharge = json.optBoolean("disable_battery_crypto_recharge_module", false),
batteryMeanPriceNft = json.optString("batteryMeanPrice_nft", "0.03"),
batteryMeanPriceSwap = json.optString("batteryMeanPrice_swap", "0.22"),
batteryMeanPriceJetton = json.optString("batteryMeanPrice_jetton", "0.06"),
batteryReservedAmount = json.optString("batteryReservedAmount", "0.3"),
batteryMaxInputAmount = json.optString("batteryMaxInputAmount", "3"),
batteryRefundEndpoint = json.optString("batteryRefundEndpoint", "https://battery-refund-app.vercel.app"),
batteryPromoDisabled = json.optBoolean("disable_battery_promo_module", true),
)

constructor() : this(
supportLink = "mailto:support@tonkeeper.com",
Expand All @@ -80,6 +109,20 @@ data class ConfigEntity(
aptabaseEndpoint = "https://anonymous-analytics.tonkeeper.com",
aptabaseAppKey = "A-SH-4314447490",
scamEndpoint = "https://scam.tonkeeper.com",
batteryHost = "https://battery.tonkeeper.com",
batteryTestnetHost = "https://testnet-battery.tonkeeper.com",
batteryBeta = true,
batteryDisabled = false,
batterySendDisabled = false,
batteryMeanFees = "0.0055",
disableBatteryCryptoRecharge = false,
batteryMeanPriceNft = "0.03",
batteryMeanPriceSwap = "0.22",
batteryMeanPriceJetton = "0.06",
batteryReservedAmount = "0.3",
batteryMaxInputAmount = "3",
batteryRefundEndpoint = "https://battery-refund-app.vercel.app",
batteryPromoDisabled = false,
)

companion object {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,15 +1,13 @@
package com.tonapps.wallet.data.account

import android.content.Context
import android.util.Log
import com.tonapps.blockchain.ton.contract.BaseWalletContract
import com.tonapps.blockchain.ton.contract.WalletVersion
import com.tonapps.blockchain.ton.contract.walletVersion
import com.tonapps.blockchain.ton.extensions.EmptyPrivateKeyEd25519
import com.tonapps.blockchain.ton.extensions.base64
import com.tonapps.blockchain.ton.extensions.hex
import com.tonapps.blockchain.ton.extensions.toAccountId
import com.tonapps.extensions.isMainVersion
import com.tonapps.ledger.ton.LedgerAccount
import com.tonapps.wallet.api.API
import com.tonapps.wallet.data.account.entities.MessageBodyEntity
Expand Down Expand Up @@ -37,14 +35,10 @@ import kotlinx.coroutines.flow.shareIn
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import kotlinx.parcelize.RawValue
import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json
import org.ton.api.pk.PrivateKeyEd25519
import org.ton.api.pub.PublicKeyEd25519
import org.ton.cell.Cell
import org.ton.contract.wallet.WalletTransfer
import org.ton.mnemonic.Mnemonic
import java.util.UUID

class AccountRepository(
Expand Down Expand Up @@ -427,9 +421,10 @@ class AccountRepository(
wallet: WalletEntity,
seqno: Int,
validUntil: Long,
transfers: List<WalletTransfer>
transfers: List<WalletTransfer>,
internalMessage: Boolean = false,
): MessageBodyEntity {
val body = wallet.createBody(seqno, validUntil, transfers)
val body = wallet.createBody(seqno, validUntil, transfers, internalMessage)
return MessageBodyEntity(seqno, body, validUntil)
}

Expand All @@ -438,9 +433,10 @@ class AccountRepository(
seqno: Int,
privateKeyEd25519: PrivateKeyEd25519,
validUntil: Long,
transfers: List<WalletTransfer>
transfers: List<WalletTransfer>,
internalMessage: Boolean = false,
): Cell {
val data = messageBody(wallet, seqno, validUntil, transfers)
val data = messageBody(wallet, seqno, validUntil, transfers, internalMessage)
return wallet.sign(privateKeyEd25519, data.seqno, data.body)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import android.os.Parcelable
import android.util.Log
import com.tonapps.blockchain.ton.TonNetwork
import com.tonapps.blockchain.ton.contract.BaseWalletContract
import com.tonapps.blockchain.ton.contract.WalletFeature
import com.tonapps.blockchain.ton.contract.WalletVersion
import com.tonapps.blockchain.ton.extensions.toAccountId
import com.tonapps.blockchain.ton.extensions.toRawAddress
Expand Down Expand Up @@ -32,7 +33,7 @@ data class WalletEntity(
data class Ledger(
val deviceId: String,
val accountIndex: Int
): Parcelable
) : Parcelable

val contract: BaseWalletContract by lazy {
val network = if (testnet) TonNetwork.TESTNET.value else TonNetwork.MAINNET.value
Expand Down Expand Up @@ -69,15 +70,21 @@ data class WalletEntity(
return address.toRawAddress().equals(accountId, ignoreCase = true)
}

fun isSupportedFeature(feature: WalletFeature): Boolean {
return contract.isSupportedFeature(feature)
}

fun createBody(
seqno: Int,
validUntil: Long,
gifts: List<WalletTransfer>
gifts: List<WalletTransfer>,
internalMessage: Boolean = false,
): Cell {
return contract.createTransferUnsignedBody(
validUntil = validUntil,
seqno = seqno,
gifts = gifts.toTypedArray()
gifts = gifts.toTypedArray(),
internalMessage = internalMessage,
)
}

Expand Down
1 change: 1 addition & 0 deletions apps/wallet/data/battery/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/build
Loading

0 comments on commit 8282498

Please sign in to comment.