Skip to content

Commit

Permalink
Fix load SOL balance (#1197)
Browse files Browse the repository at this point in the history
  • Loading branch information
aminsato authored Oct 18, 2024
1 parent dabe9d1 commit fd1aa76
Show file tree
Hide file tree
Showing 6 changed files with 119 additions and 19 deletions.
22 changes: 22 additions & 0 deletions data/src/main/kotlin/com/vultisig/wallet/data/api/SolanaApi.kt
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,18 @@ import com.vultisig.wallet.data.api.models.SplResponseAccountJson
import com.vultisig.wallet.data.api.models.SplResponseJson
import com.vultisig.wallet.data.api.models.SplTokenJson
import com.vultisig.wallet.data.api.utils.postRpc
import com.vultisig.wallet.data.api.models.SplTokenInfo
import com.vultisig.wallet.data.models.SplTokenDeserialized
import com.vultisig.wallet.data.utils.SplTokenResponseJsonSerializer
import io.ktor.client.HttpClient
import io.ktor.client.call.body
import io.ktor.client.request.get
import io.ktor.client.request.post
import io.ktor.client.request.setBody
import io.ktor.client.statement.bodyAsText
import kotlinx.coroutines.async
import kotlinx.coroutines.awaitAll
import kotlinx.coroutines.coroutineScope
import kotlinx.serialization.json.Json
import kotlinx.serialization.json.add
import kotlinx.serialization.json.addJsonArray
Expand All @@ -38,6 +43,7 @@ interface SolanaApi {
suspend fun broadcastTransaction(tx: String): String?
suspend fun getSPLTokens(walletAddress: String): List<SplResponseAccountJson>?
suspend fun getSPLTokensInfo(tokens: List<String>): List<SplTokenJson>
suspend fun getSPLTokensInfo2(tokens: List<String>): List<SplTokenInfo>
suspend fun getSPLTokenBalance(walletAddress: String, coinAddress: String): String?
}

Expand All @@ -50,8 +56,10 @@ internal class SolanaApiImp @Inject constructor(
private val rpcEndpoint = "https://api.mainnet-beta.solana.com"
private val rpcEndpoint2 = "https://solana-rpc.publicnode.com"
private val splTokensInfoEndpoint = "https://api.solana.fm/v1/tokens"
private val splTokensInfoEndpoint2 = "https://tokens.jup.ag/token"
private val solanaRentExemptionEndpoint = "https://api.devnet.solana.com"
override suspend fun getBalance(address: String): BigInteger {

val payload = RpcPayload(
jsonrpc = "2.0",
method = "getBalance",
Expand Down Expand Up @@ -195,6 +203,20 @@ internal class SolanaApiImp @Inject constructor(
}
}

override suspend fun getSPLTokensInfo2(tokens: List<String>) = coroutineScope {
tokens.map { token ->
async {
try {
httpClient.get("$splTokensInfoEndpoint2/$token").body<SplTokenInfo>()
} catch (e: Exception) {
Timber.tag("SolanaApiImp")
.e("Error getting spl token for $token message : ${e.message}")
null
}
}
}.awaitAll().filterNotNull()
}

override suspend fun getSPLTokens(walletAddress: String): List<SplResponseAccountJson>? {
try {
val payload = RpcPayload(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -79,20 +79,37 @@ data class SplTokenJson(
val mint: String,
)

@Serializable
data class SplTokenInfo(
@SerialName("address")
val address: String,
@SerialName("name")
val name: String,
@SerialName("symbol")
val symbol: String,
@SerialName("decimals")
val decimals: Int,
@SerialName("logoURI")
val logoURI: String,
@SerialName("extensions")
val extensions: SplExtensionsJson?,
)


@Serializable
data class SplTokenListJson(
@SerialName("symbol")
val ticker: String,
@SerialName("image")
val logo: String,
val logo: String?,
@SerialName("extensions")
val extensions: SplExtensionsJson?,
)

@Serializable
data class SplExtensionsJson(
@SerialName("coingeckoId")
val coingeckoId: String,
val coingeckoId: String?,
)


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,5 +45,11 @@ internal interface DataMappersModule {
fun bindKeysignPayloadProtoMapper(
impl: KeysignPayloadProtoMapperImpl
): KeysignPayloadProtoMapper


@Binds
@Singleton
fun bindSplTokenJsonFromSplTokenInfoMapper(
impl: SplTokenJsonFromSplTokenInfoImpl
): SplTokenJsonFromSplTokenInfoMapper

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package com.vultisig.wallet.data.mappers

import com.vultisig.wallet.data.api.models.SplTokenInfo
import com.vultisig.wallet.data.api.models.SplTokenJson
import com.vultisig.wallet.data.api.models.SplTokenListJson
import javax.inject.Inject


interface SplTokenJsonFromSplTokenInfoMapper : SuspendMapperFunc<SplTokenInfo, SplTokenJson>

internal class SplTokenJsonFromSplTokenInfoImpl @Inject constructor(
private val mapKeysignPayload: KeysignPayloadProtoMapper,
) : SplTokenJsonFromSplTokenInfoMapper {

override suspend fun invoke(from: SplTokenInfo): SplTokenJson = SplTokenJson(
decimals = from.decimals,
tokenList = SplTokenListJson(
logo = from.logoURI,
ticker = from.symbol,
extensions = from.extensions
),
mint = from.address
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,11 @@ package com.vultisig.wallet.data.repositories
import com.vultisig.wallet.data.api.SolanaApi
import com.vultisig.wallet.data.api.models.SplResponseAccountJson
import com.vultisig.wallet.data.api.models.SplTokenJson
import com.vultisig.wallet.data.api.models.SplTokenListJson
import com.vultisig.wallet.data.db.dao.TokenValueDao
import com.vultisig.wallet.data.db.models.TokenValueEntity
import com.vultisig.wallet.data.mappers.KeysignMessageFromProtoMapper
import com.vultisig.wallet.data.mappers.SplTokenJsonFromSplTokenInfoMapper
import com.vultisig.wallet.data.models.Chain
import com.vultisig.wallet.data.models.Coin
import com.vultisig.wallet.data.models.Vault
Expand All @@ -27,25 +30,53 @@ internal class SplTokenRepositoryImpl @Inject constructor(
private val solanaApi: SolanaApi,
private val chainAccountAddressRepository: ChainAccountAddressRepository,
private val tokenValueDao: TokenValueDao,
) : SplTokenRepository {
private val mapSplTokenJsonFromSplTokenInfo: SplTokenJsonFromSplTokenInfoMapper,
) : SplTokenRepository {

override suspend fun getTokens(address: String, vault: Vault): List<Coin> {
val rawSPLTokens = solanaApi.getSPLTokens(address) ?: return emptyList()
val splTokenResponse = rawSPLTokens.map { processRawSPLToken(it) }
val result = solanaApi.getSPLTokensInfo(splTokenResponse.map { it.mint })
val splTokens = splTokenResponse.map { key ->
val coin = createCoin(result.first { key.mint == it.mint }, key.mint, vault)
tokenValueDao.insertTokenValue(
TokenValueEntity(
Chain.Solana.id,
coin.address,
coin.ticker,
key.amount.toString()
)
)
coin
var result = solanaApi.getSPLTokensInfo(splTokenResponse.map { it.mint })
if (result.size != splTokenResponse.size) {
//search for missing tokens in splTokenResponse
val missingMints = splTokenResponse.map { it.mint }.filter { mint ->
result.none { it.mint == mint }
}
val mutableResult = result.toMutableList()
solanaApi.getSPLTokensInfo2(missingMints).forEach {
mutableResult.add(mapSplTokenJsonFromSplTokenInfo(it))
}
result = mutableResult
}
return splTokenResponse.mapNotNull { key ->
result.firstOrNull { resultItem -> resultItem.mint == key.mint }
?.let { matchingResult ->
createCoin(
matchingResult,
key.mint,
vault
).apply {
saveToDatabase(
this,
key
)
}
}
}
return splTokens

}
private suspend fun saveToDatabase(
coin: Coin,
splTokenData: SplTokenResponse,
) {
tokenValueDao.insertTokenValue(
TokenValueEntity(
Chain.Solana.id,
coin.address,
coin.ticker,
splTokenData.amount.toString()
)
)
}

override suspend fun getBalance(coin: Coin): BigInteger? {
Expand Down Expand Up @@ -76,7 +107,7 @@ internal class SplTokenRepositoryImpl @Inject constructor(
val coin = Coin(
chain = Chain.Solana,
ticker = tokenResponse.tokenList.ticker,
logo = tokenResponse.tokenList.logo,
logo = tokenResponse.tokenList.logo?: "",
decimal = tokenResponse.decimals,
priceProviderID = tokenResponse.tokenList.extensions?.coingeckoId ?: "0",
contractAddress = contractAddress,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ object BigIntegerSerializer : KSerializer<BigInteger> {
encoder.encodeString(value.toString())

override fun deserialize(decoder: Decoder): BigInteger =
BigInteger(decoder.decodeInt().toString())
BigInteger.valueOf(decoder.decodeLong())
}

@Singleton
Expand Down

0 comments on commit fd1aa76

Please sign in to comment.