From 1def958063206dcd16149ed5183fbe4973acb6f1 Mon Sep 17 00:00:00 2001 From: aminsato Date: Fri, 25 Oct 2024 06:12:40 +0330 Subject: [PATCH 1/4] Fix `Instruction data not found` error --- .../ui/models/keysign/KeysignFlowViewModel.kt | 1 + .../ui/models/send/SendFormViewModel.kt | 1 + commondata | 2 +- .../com/vultisig/wallet/data/api/SolanaApi.kt | 23 ++++++-- .../wallet/data/api/models/SplTokenJson.kt | 13 +++++ .../data/chains/helpers/SolanaHelper.kt | 54 +++++++++---------- .../data/mappers/KeysignPayloadProtoMapper.kt | 1 + .../data/models/payload/BlockChainSpecfic.kt | 1 + .../BlockChainSpecificRepository.kt | 43 ++++++++------- 9 files changed, 85 insertions(+), 54 deletions(-) diff --git a/app/src/main/java/com/vultisig/wallet/ui/models/keysign/KeysignFlowViewModel.kt b/app/src/main/java/com/vultisig/wallet/ui/models/keysign/KeysignFlowViewModel.kt index 33da1102a..368f4a829 100644 --- a/app/src/main/java/com/vultisig/wallet/ui/models/keysign/KeysignFlowViewModel.kt +++ b/app/src/main/java/com/vultisig/wallet/ui/models/keysign/KeysignFlowViewModel.kt @@ -302,6 +302,7 @@ internal class KeysignFlowViewModel @Inject constructor( priorityFee = specific.priorityFee.toString(), toTokenAssociatedAddress = specific.toAddressPubKey, fromTokenAssociatedAddress = specific.fromAddressPubKey, + tokenAccountExists = specific.tokenAccountExists, ) } else null, polkadotSpecific = if (specific is BlockChainSpecific.Polkadot) { diff --git a/app/src/main/java/com/vultisig/wallet/ui/models/send/SendFormViewModel.kt b/app/src/main/java/com/vultisig/wallet/ui/models/send/SendFormViewModel.kt index 4d03e9cd1..7f8713dcc 100644 --- a/app/src/main/java/com/vultisig/wallet/ui/models/send/SendFormViewModel.kt +++ b/app/src/main/java/com/vultisig/wallet/ui/models/send/SendFormViewModel.kt @@ -407,6 +407,7 @@ internal class SendFormViewModel @Inject constructor( isSwap = false, isMaxAmountEnabled = isMaxAmount, isDeposit = false, + dstAddress = dstAddress ) .let { val ethSettings = ethGasSettings.value diff --git a/commondata b/commondata index 22c6cb4f1..0cf90ae77 160000 --- a/commondata +++ b/commondata @@ -1 +1 @@ -Subproject commit 22c6cb4f1ae46205ad98d114d6c9519b9f4c71cd +Subproject commit 0cf90ae776aedf95ec3f30fdbf58e7748b280e18 diff --git a/data/src/main/kotlin/com/vultisig/wallet/data/api/SolanaApi.kt b/data/src/main/kotlin/com/vultisig/wallet/data/api/SolanaApi.kt index 572c4dda7..053a0882a 100644 --- a/data/src/main/kotlin/com/vultisig/wallet/data/api/SolanaApi.kt +++ b/data/src/main/kotlin/com/vultisig/wallet/data/api/SolanaApi.kt @@ -4,6 +4,7 @@ import com.vultisig.wallet.data.api.models.BroadcastTransactionRespJson import com.vultisig.wallet.data.api.models.RecentBlockHashResponseJson import com.vultisig.wallet.data.api.models.RpcPayload import com.vultisig.wallet.data.api.models.SPLTokenRequestJson +import com.vultisig.wallet.data.api.models.SolanaAccountExistResponseJson import com.vultisig.wallet.data.api.models.SolanaBalanceJson import com.vultisig.wallet.data.api.models.SolanaFeeObjectJson import com.vultisig.wallet.data.api.models.SolanaFeeObjectRespJson @@ -45,6 +46,7 @@ interface SolanaApi { suspend fun getSPLTokensInfo(tokens: List): List suspend fun getSPLTokensInfo2(tokens: List): List suspend fun getSPLTokenBalance(walletAddress: String, coinAddress: String): String? + suspend fun doesTokenAccountExist(address: String, mint: String): Boolean } internal class SolanaApiImp @Inject constructor( @@ -57,7 +59,6 @@ internal class SolanaApiImp @Inject constructor( 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( @@ -84,7 +85,7 @@ internal class SolanaApiImp @Inject constructor( override suspend fun getMinimumBalanceForRentExemption(): BigInteger = try { httpClient.postRpc( - solanaRentExemptionEndpoint, + rpcEndpoint, "getMinimumBalanceForRentExemption", params = buildJsonArray { add(DATA_LENGTH_MINIMUM_BALANCE_FOR_RENT_EXEMPTION) @@ -152,7 +153,7 @@ internal class SolanaApiImp @Inject constructor( return "0" } - override suspend fun broadcastTransaction(tx: String): String? { + override suspend fun broadcastTransaction(tx: String): String { try { val requestBody = RpcPayload( jsonrpc = "2.0", @@ -286,6 +287,22 @@ internal class SolanaApiImp @Inject constructor( } } + override suspend fun doesTokenAccountExist(address: String, mint: String): Boolean = + httpClient.postRpc( + rpcEndpoint, + "getTokenAccountsByOwner", + params = buildJsonArray { + add(address) + addJsonObject { + put("mint", mint) + } + addJsonObject { + put("encoding", "jsonParsed") + } + }, + ).result.values.isNotEmpty() + + companion object { private const val PROGRAM_ID_SPL_REQUEST_PARAM = diff --git a/data/src/main/kotlin/com/vultisig/wallet/data/api/models/SplTokenJson.kt b/data/src/main/kotlin/com/vultisig/wallet/data/api/models/SplTokenJson.kt index 7307c2550..ed6211df4 100644 --- a/data/src/main/kotlin/com/vultisig/wallet/data/api/models/SplTokenJson.kt +++ b/data/src/main/kotlin/com/vultisig/wallet/data/api/models/SplTokenJson.kt @@ -2,6 +2,7 @@ package com.vultisig.wallet.data.api.models import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable +import kotlinx.serialization.json.JsonArray @Serializable data class SplTokenAmountJson( @@ -167,4 +168,16 @@ data class SplAmountRpcResponseResultJson( data class SPLTokenRequestJson( @SerialName("tokens") val tokens: List, +) + +@Serializable +data class SolanaAccountExistResponseJson( + @SerialName("result") + val result: SolanaAccountExistResultJson, +) + +@Serializable +data class SolanaAccountExistResultJson( + @SerialName("value") + val values: JsonArray ) \ No newline at end of file diff --git a/data/src/main/kotlin/com/vultisig/wallet/data/chains/helpers/SolanaHelper.kt b/data/src/main/kotlin/com/vultisig/wallet/data/chains/helpers/SolanaHelper.kt index 0148dea73..2e4d2a1c4 100644 --- a/data/src/main/kotlin/com/vultisig/wallet/data/chains/helpers/SolanaHelper.kt +++ b/data/src/main/kotlin/com/vultisig/wallet/data/chains/helpers/SolanaHelper.kt @@ -60,41 +60,39 @@ class SolanaHelper( .build() .toByteArray() } else { - if (solanaSpecific.fromAddressPubKey != null && solanaSpecific.toAddressPubKey!= null) { - val transfer = Solana.TokenTransfer.newBuilder() + val fromAddress = AnyAddress(keysignPayload.coin.address, coinType) + val senderAddress = SolanaAddress(fromAddress.description()) + val receiverAddress = SolanaAddress(toAddress.description()) + val generatedSenderAssociatedAddress = senderAddress.defaultTokenAddress( + keysignPayload.coin.contractAddress + ) + val generatedRecipientAssociatedAddress = receiverAddress.defaultTokenAddress( + keysignPayload.coin.contractAddress + ) + val tokenAccountExists = keysignPayload.blockChainSpecific.tokenAccountExists + return if (tokenAccountExists == true) { + val transferTokenMessage = Solana.TokenTransfer.newBuilder() .setTokenMintAddress(keysignPayload.coin.contractAddress) - .setSenderTokenAddress(solanaSpecific.fromAddressPubKey) - .setRecipientTokenAddress(solanaSpecific.toAddressPubKey) + .setRecipientTokenAddress(generatedRecipientAssociatedAddress) + .setSenderTokenAddress(generatedSenderAssociatedAddress) .setAmount(keysignPayload.toAmount.toLong()) .setDecimals(keysignPayload.coin.decimal) - return input - .setTokenTransferTransaction(transfer.build()) - .build() - .toByteArray() + input.setTokenTransferTransaction(transferTokenMessage.build()) } else { - val fromAddress = AnyAddress(keysignPayload.coin.address, coinType) - val senderAddress = SolanaAddress(fromAddress.description()) - val receiverAddress = SolanaAddress(toAddress.description()) - val generatedSenderAssociatedAddress = senderAddress.defaultTokenAddress( - keysignPayload.coin.contractAddress - ) - val generatedRecipientAssociatedAddress = receiverAddress.defaultTokenAddress( - keysignPayload.coin.contractAddress - ) - val transferTokenMessage = - Solana.TokenTransfer.newBuilder() - .setTokenMintAddress(keysignPayload.coin.contractAddress) - .setRecipientTokenAddress(generatedRecipientAssociatedAddress) - .setSenderTokenAddress(generatedSenderAssociatedAddress) - .setAmount(keysignPayload.toAmount.toLong()) - .setDecimals(keysignPayload.coin.decimal) + val createAndTransferTokenMessage = Solana.CreateAndTransferToken.newBuilder() + .setTokenMintAddress(keysignPayload.coin.contractAddress) + .setRecipientTokenAddress(generatedRecipientAssociatedAddress) + .setSenderTokenAddress(generatedSenderAssociatedAddress) + .setAmount(keysignPayload.toAmount.toLong()) + .setDecimals(keysignPayload.coin.decimal) + .setTokenProgramId(Solana.TokenProgramId.TokenProgram) + .setRecipientMainAddress(toAddress.description()) - return input - .setTokenTransferTransaction(transferTokenMessage.build()) - .build() - .toByteArray() + input.setCreateAndTransferTokenTransaction(createAndTransferTokenMessage.build()) } + .build() + .toByteArray() } } diff --git a/data/src/main/kotlin/com/vultisig/wallet/data/mappers/KeysignPayloadProtoMapper.kt b/data/src/main/kotlin/com/vultisig/wallet/data/mappers/KeysignPayloadProtoMapper.kt index e24660bb6..9dd556c9f 100644 --- a/data/src/main/kotlin/com/vultisig/wallet/data/mappers/KeysignPayloadProtoMapper.kt +++ b/data/src/main/kotlin/com/vultisig/wallet/data/mappers/KeysignPayloadProtoMapper.kt @@ -138,6 +138,7 @@ internal class KeysignPayloadProtoMapperImpl @Inject constructor() : KeysignPayl priorityFee = BigInteger(it.priorityFee), fromAddressPubKey = it.fromTokenAssociatedAddress, toAddressPubKey = it.toTokenAssociatedAddress, + tokenAccountExists = it.tokenAccountExists, ) } diff --git a/data/src/main/kotlin/com/vultisig/wallet/data/models/payload/BlockChainSpecfic.kt b/data/src/main/kotlin/com/vultisig/wallet/data/models/payload/BlockChainSpecfic.kt index d3a2600c5..2d824dfa0 100644 --- a/data/src/main/kotlin/com/vultisig/wallet/data/models/payload/BlockChainSpecfic.kt +++ b/data/src/main/kotlin/com/vultisig/wallet/data/models/payload/BlockChainSpecfic.kt @@ -40,6 +40,7 @@ sealed class BlockChainSpecific { val priorityFee: BigInteger, val fromAddressPubKey: String? = null, val toAddressPubKey: String? = null, + val tokenAccountExists: Boolean? = null, ) : BlockChainSpecific() data class Sui( diff --git a/data/src/main/kotlin/com/vultisig/wallet/data/repositories/BlockChainSpecificRepository.kt b/data/src/main/kotlin/com/vultisig/wallet/data/repositories/BlockChainSpecificRepository.kt index fd73a1edd..5a65244ee 100644 --- a/data/src/main/kotlin/com/vultisig/wallet/data/repositories/BlockChainSpecificRepository.kt +++ b/data/src/main/kotlin/com/vultisig/wallet/data/repositories/BlockChainSpecificRepository.kt @@ -8,15 +8,14 @@ import com.vultisig.wallet.data.api.PolkadotApi import com.vultisig.wallet.data.api.SolanaApi import com.vultisig.wallet.data.api.ThorChainApi import com.vultisig.wallet.data.api.chains.SuiApi -import com.vultisig.wallet.data.api.chains.TonApi import com.vultisig.wallet.data.models.Chain import com.vultisig.wallet.data.models.Coin import com.vultisig.wallet.data.models.TokenStandard import com.vultisig.wallet.data.models.TokenValue import com.vultisig.wallet.data.models.payload.BlockChainSpecific import com.vultisig.wallet.data.models.payload.UtxoInfo -import kotlinx.datetime.Clock -import timber.log.Timber +import kotlinx.coroutines.async +import kotlinx.coroutines.coroutineScope import java.math.BigInteger import javax.inject.Inject @@ -36,6 +35,7 @@ interface BlockChainSpecificRepository { isMaxAmountEnabled: Boolean, isDeposit: Boolean, gasLimit: BigInteger? = null, + dstAddress: String? = null, ): BlockChainSpecificAndUtxo } @@ -49,7 +49,6 @@ internal class BlockChainSpecificRepositoryImpl @Inject constructor( private val blockChairApi: BlockChairApi, private val polkadotApi: PolkadotApi, private val suiApi: SuiApi, - private val tonApi: TonApi, ) : BlockChainSpecificRepository { override suspend fun getSpecific( @@ -61,6 +60,7 @@ internal class BlockChainSpecificRepositoryImpl @Inject constructor( isMaxAmountEnabled: Boolean, isDeposit: Boolean, gasLimit: BigInteger?, + dstAddress: String?, ): BlockChainSpecificAndUtxo = when (chain.standard) { TokenStandard.THORCHAIN -> { val account = if (chain == Chain.MayaChain) { @@ -165,13 +165,22 @@ internal class BlockChainSpecificRepositoryImpl @Inject constructor( ) } - TokenStandard.SOL -> { - val blockHash = solanaApi.getRecentBlockHash() - Timber.d("solana blockhash: $blockHash") + TokenStandard.SOL -> coroutineScope { + val blockHash = async { + solanaApi.getRecentBlockHash() + } + + val tokenAccountExists = async { + dstAddress?.let { + solanaApi.doesTokenAccountExist(dstAddress, token.contractAddress) + } + } + BlockChainSpecificAndUtxo( BlockChainSpecific.Solana( - recentBlockHash = blockHash, - priorityFee = gasFee.value + recentBlockHash = blockHash.await(), + tokenAccountExists = tokenAccountExists.await(), + priorityFee = gasFee.value, ) ) } @@ -211,26 +220,16 @@ internal class BlockChainSpecificRepositoryImpl @Inject constructor( } TokenStandard.SUI -> { + suiApi.getAllCoins(address) + BlockChainSpecificAndUtxo( BlockChainSpecific.Sui( - referenceGasPrice = suiApi.getReferenceGasPrice(), + referenceGasPrice = gasFee.value, coins = suiApi.getAllCoins(address), ), utxos = emptyList(), ) } - - TokenStandard.TON -> { - BlockChainSpecificAndUtxo( - blockChainSpecific = BlockChainSpecific.Ton( - sequenceNumber = tonApi.getSpecificTransactionInfo(address) - .toString().toULong(), - expireAt = (Clock.System.now() - .epochSeconds + 600L).toULong(), - bounceable = false, - ), - ) - } } private fun ensureOneGweiPriorityFee(priorityFee: BigInteger): BigInteger { From 629e5069436ea6039f0cf0df7610e767cebd75f5 Mon Sep 17 00:00:00 2001 From: aminsato Date: Sat, 2 Nov 2024 18:33:37 +0330 Subject: [PATCH 2/4] Send SPL toke according to iOS version --- .../ui/models/keysign/KeysignFlowViewModel.kt | 1 - commondata | 2 +- .../com/vultisig/wallet/data/api/SolanaApi.kt | 50 ++++++++++++------- .../wallet/data/api/models/SplTokenJson.kt | 15 +----- .../data/chains/helpers/SolanaHelper.kt | 50 +++++++++---------- .../data/mappers/KeysignPayloadProtoMapper.kt | 1 - .../data/models/payload/BlockChainSpecfic.kt | 1 - .../BlockChainSpecificRepository.kt | 44 ++++++++++++---- 8 files changed, 94 insertions(+), 70 deletions(-) diff --git a/app/src/main/java/com/vultisig/wallet/ui/models/keysign/KeysignFlowViewModel.kt b/app/src/main/java/com/vultisig/wallet/ui/models/keysign/KeysignFlowViewModel.kt index 368f4a829..33da1102a 100644 --- a/app/src/main/java/com/vultisig/wallet/ui/models/keysign/KeysignFlowViewModel.kt +++ b/app/src/main/java/com/vultisig/wallet/ui/models/keysign/KeysignFlowViewModel.kt @@ -302,7 +302,6 @@ internal class KeysignFlowViewModel @Inject constructor( priorityFee = specific.priorityFee.toString(), toTokenAssociatedAddress = specific.toAddressPubKey, fromTokenAssociatedAddress = specific.fromAddressPubKey, - tokenAccountExists = specific.tokenAccountExists, ) } else null, polkadotSpecific = if (specific is BlockChainSpecific.Polkadot) { diff --git a/commondata b/commondata index 0cf90ae77..22c6cb4f1 160000 --- a/commondata +++ b/commondata @@ -1 +1 @@ -Subproject commit 0cf90ae776aedf95ec3f30fdbf58e7748b280e18 +Subproject commit 22c6cb4f1ae46205ad98d114d6c9519b9f4c71cd diff --git a/data/src/main/kotlin/com/vultisig/wallet/data/api/SolanaApi.kt b/data/src/main/kotlin/com/vultisig/wallet/data/api/SolanaApi.kt index 053a0882a..f94584ebc 100644 --- a/data/src/main/kotlin/com/vultisig/wallet/data/api/SolanaApi.kt +++ b/data/src/main/kotlin/com/vultisig/wallet/data/api/SolanaApi.kt @@ -4,7 +4,6 @@ import com.vultisig.wallet.data.api.models.BroadcastTransactionRespJson import com.vultisig.wallet.data.api.models.RecentBlockHashResponseJson import com.vultisig.wallet.data.api.models.RpcPayload import com.vultisig.wallet.data.api.models.SPLTokenRequestJson -import com.vultisig.wallet.data.api.models.SolanaAccountExistResponseJson import com.vultisig.wallet.data.api.models.SolanaBalanceJson import com.vultisig.wallet.data.api.models.SolanaFeeObjectJson import com.vultisig.wallet.data.api.models.SolanaFeeObjectRespJson @@ -46,7 +45,7 @@ interface SolanaApi { suspend fun getSPLTokensInfo(tokens: List): List suspend fun getSPLTokensInfo2(tokens: List): List suspend fun getSPLTokenBalance(walletAddress: String, coinAddress: String): String? - suspend fun doesTokenAccountExist(address: String, mint: String): Boolean + suspend fun getTokenAssociatedAccountByOwner(walletAddress: String, mintAddress: String): String? } internal class SolanaApiImp @Inject constructor( @@ -59,6 +58,7 @@ internal class SolanaApiImp @Inject constructor( 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( @@ -85,7 +85,7 @@ internal class SolanaApiImp @Inject constructor( override suspend fun getMinimumBalanceForRentExemption(): BigInteger = try { httpClient.postRpc( - rpcEndpoint, + solanaRentExemptionEndpoint, "getMinimumBalanceForRentExemption", params = buildJsonArray { add(DATA_LENGTH_MINIMUM_BALANCE_FOR_RENT_EXEMPTION) @@ -153,7 +153,7 @@ internal class SolanaApiImp @Inject constructor( return "0" } - override suspend fun broadcastTransaction(tx: String): String { + override suspend fun broadcastTransaction(tx: String): String? { try { val requestBody = RpcPayload( jsonrpc = "2.0", @@ -287,21 +287,35 @@ internal class SolanaApiImp @Inject constructor( } } - override suspend fun doesTokenAccountExist(address: String, mint: String): Boolean = - httpClient.postRpc( - rpcEndpoint, - "getTokenAccountsByOwner", - params = buildJsonArray { - add(address) - addJsonObject { - put("mint", mint) - } - addJsonObject { - put("encoding", "jsonParsed") + override suspend fun getTokenAssociatedAccountByOwner( + walletAddress: String, + mintAddress: String, + ): String? { + try { + val response = httpClient.postRpc( + url = rpcEndpoint2, + method = "getTokenAccountsByOwner", + params = buildJsonArray { + add(walletAddress) + addJsonObject { + put("mint", mintAddress) + } + addJsonObject { + put("encoding", ENCODING_SPL_REQUEST_PARAM) + } } - }, - ).result.values.isNotEmpty() - + ) + if (response.error != null) { + Timber.d("getTokenAssociatedAccountByOwner error: ${response.error}") + return null + } + val value = response.value ?: error("getTokenAssociatedAccountByOwner error") + return value.value[0].pubKey + } catch (e: Exception) { + Timber.e(e) + return null + } + } companion object { diff --git a/data/src/main/kotlin/com/vultisig/wallet/data/api/models/SplTokenJson.kt b/data/src/main/kotlin/com/vultisig/wallet/data/api/models/SplTokenJson.kt index ed6211df4..f25b17b0b 100644 --- a/data/src/main/kotlin/com/vultisig/wallet/data/api/models/SplTokenJson.kt +++ b/data/src/main/kotlin/com/vultisig/wallet/data/api/models/SplTokenJson.kt @@ -2,7 +2,6 @@ package com.vultisig.wallet.data.api.models import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable -import kotlinx.serialization.json.JsonArray @Serializable data class SplTokenAmountJson( @@ -148,6 +147,8 @@ data class SplAmountAccountJson( data class SplAmountValueJson( @SerialName("account") val account: SplAmountAccountJson, + @SerialName("pubkey") + val pubKey: String, ) @Serializable @@ -168,16 +169,4 @@ data class SplAmountRpcResponseResultJson( data class SPLTokenRequestJson( @SerialName("tokens") val tokens: List, -) - -@Serializable -data class SolanaAccountExistResponseJson( - @SerialName("result") - val result: SolanaAccountExistResultJson, -) - -@Serializable -data class SolanaAccountExistResultJson( - @SerialName("value") - val values: JsonArray ) \ No newline at end of file diff --git a/data/src/main/kotlin/com/vultisig/wallet/data/chains/helpers/SolanaHelper.kt b/data/src/main/kotlin/com/vultisig/wallet/data/chains/helpers/SolanaHelper.kt index 2e4d2a1c4..67a572596 100644 --- a/data/src/main/kotlin/com/vultisig/wallet/data/chains/helpers/SolanaHelper.kt +++ b/data/src/main/kotlin/com/vultisig/wallet/data/chains/helpers/SolanaHelper.kt @@ -60,39 +60,37 @@ class SolanaHelper( .build() .toByteArray() } else { - val fromAddress = AnyAddress(keysignPayload.coin.address, coinType) - val senderAddress = SolanaAddress(fromAddress.description()) - val receiverAddress = SolanaAddress(toAddress.description()) - val generatedSenderAssociatedAddress = senderAddress.defaultTokenAddress( - keysignPayload.coin.contractAddress - ) - val generatedRecipientAssociatedAddress = receiverAddress.defaultTokenAddress( - keysignPayload.coin.contractAddress - ) - val tokenAccountExists = keysignPayload.blockChainSpecific.tokenAccountExists - return if (tokenAccountExists == true) { - val transferTokenMessage = Solana.TokenTransfer.newBuilder() + if (solanaSpecific.fromAddressPubKey != null && solanaSpecific.toAddressPubKey!= null) { + val transfer = Solana.TokenTransfer.newBuilder() .setTokenMintAddress(keysignPayload.coin.contractAddress) - .setRecipientTokenAddress(generatedRecipientAssociatedAddress) - .setSenderTokenAddress(generatedSenderAssociatedAddress) + .setSenderTokenAddress(solanaSpecific.fromAddressPubKey) + .setRecipientTokenAddress(solanaSpecific.toAddressPubKey) .setAmount(keysignPayload.toAmount.toLong()) .setDecimals(keysignPayload.coin.decimal) - input.setTokenTransferTransaction(transferTokenMessage.build()) + return input + .setTokenTransferTransaction(transfer.build()) + .build() + .toByteArray() } else { - val createAndTransferTokenMessage = Solana.CreateAndTransferToken.newBuilder() - .setTokenMintAddress(keysignPayload.coin.contractAddress) - .setRecipientTokenAddress(generatedRecipientAssociatedAddress) - .setSenderTokenAddress(generatedSenderAssociatedAddress) - .setAmount(keysignPayload.toAmount.toLong()) - .setDecimals(keysignPayload.coin.decimal) - .setTokenProgramId(Solana.TokenProgramId.TokenProgram) - .setRecipientMainAddress(toAddress.description()) + val receiverAddress = SolanaAddress(toAddress.description()) + val generatedRecipientAssociatedAddress = receiverAddress.defaultTokenAddress( + keysignPayload.coin.contractAddress + ) + val transferTokenMessage = + Solana.CreateAndTransferToken.newBuilder() + .setRecipientMainAddress(toAddress.description()) + .setTokenMintAddress(keysignPayload.coin.contractAddress) + .setRecipientTokenAddress(generatedRecipientAssociatedAddress) + .setSenderTokenAddress(solanaSpecific.fromAddressPubKey) + .setAmount(keysignPayload.toAmount.toLong()) + .setDecimals(keysignPayload.coin.decimal) - input.setCreateAndTransferTokenTransaction(createAndTransferTokenMessage.build()) + return input + .setCreateAndTransferTokenTransaction(transferTokenMessage.build()) + .build() + .toByteArray() } - .build() - .toByteArray() } } diff --git a/data/src/main/kotlin/com/vultisig/wallet/data/mappers/KeysignPayloadProtoMapper.kt b/data/src/main/kotlin/com/vultisig/wallet/data/mappers/KeysignPayloadProtoMapper.kt index 9dd556c9f..e24660bb6 100644 --- a/data/src/main/kotlin/com/vultisig/wallet/data/mappers/KeysignPayloadProtoMapper.kt +++ b/data/src/main/kotlin/com/vultisig/wallet/data/mappers/KeysignPayloadProtoMapper.kt @@ -138,7 +138,6 @@ internal class KeysignPayloadProtoMapperImpl @Inject constructor() : KeysignPayl priorityFee = BigInteger(it.priorityFee), fromAddressPubKey = it.fromTokenAssociatedAddress, toAddressPubKey = it.toTokenAssociatedAddress, - tokenAccountExists = it.tokenAccountExists, ) } diff --git a/data/src/main/kotlin/com/vultisig/wallet/data/models/payload/BlockChainSpecfic.kt b/data/src/main/kotlin/com/vultisig/wallet/data/models/payload/BlockChainSpecfic.kt index 2d824dfa0..d3a2600c5 100644 --- a/data/src/main/kotlin/com/vultisig/wallet/data/models/payload/BlockChainSpecfic.kt +++ b/data/src/main/kotlin/com/vultisig/wallet/data/models/payload/BlockChainSpecfic.kt @@ -40,7 +40,6 @@ sealed class BlockChainSpecific { val priorityFee: BigInteger, val fromAddressPubKey: String? = null, val toAddressPubKey: String? = null, - val tokenAccountExists: Boolean? = null, ) : BlockChainSpecific() data class Sui( diff --git a/data/src/main/kotlin/com/vultisig/wallet/data/repositories/BlockChainSpecificRepository.kt b/data/src/main/kotlin/com/vultisig/wallet/data/repositories/BlockChainSpecificRepository.kt index 5a65244ee..60eb6e686 100644 --- a/data/src/main/kotlin/com/vultisig/wallet/data/repositories/BlockChainSpecificRepository.kt +++ b/data/src/main/kotlin/com/vultisig/wallet/data/repositories/BlockChainSpecificRepository.kt @@ -8,6 +8,7 @@ import com.vultisig.wallet.data.api.PolkadotApi import com.vultisig.wallet.data.api.SolanaApi import com.vultisig.wallet.data.api.ThorChainApi import com.vultisig.wallet.data.api.chains.SuiApi +import com.vultisig.wallet.data.api.chains.TonApi import com.vultisig.wallet.data.models.Chain import com.vultisig.wallet.data.models.Coin import com.vultisig.wallet.data.models.TokenStandard @@ -16,6 +17,8 @@ import com.vultisig.wallet.data.models.payload.BlockChainSpecific import com.vultisig.wallet.data.models.payload.UtxoInfo import kotlinx.coroutines.async import kotlinx.coroutines.coroutineScope +import kotlinx.datetime.Clock +import timber.log.Timber import java.math.BigInteger import javax.inject.Inject @@ -49,6 +52,7 @@ internal class BlockChainSpecificRepositoryImpl @Inject constructor( private val blockChairApi: BlockChairApi, private val polkadotApi: PolkadotApi, private val suiApi: SuiApi, + private val tonApi: TonApi, ) : BlockChainSpecificRepository { override suspend fun getSpecific( @@ -169,18 +173,30 @@ internal class BlockChainSpecificRepositoryImpl @Inject constructor( val blockHash = async { solanaApi.getRecentBlockHash() } - - val tokenAccountExists = async { + val fromAddressPubKey = async { + solanaApi.getTokenAssociatedAccountByOwner( + token.address, + token.contractAddress + ).takeIf { !token.isNativeToken } + } + val toAddressPubKey = async { dstAddress?.let { - solanaApi.doesTokenAccountExist(dstAddress, token.contractAddress) + solanaApi.getTokenAssociatedAccountByOwner( + dstAddress, + token.contractAddress + ).takeIf { !token.isNativeToken } } } - + val recentBlockHashResult = blockHash.await() + val fromAddressPubKeyResult = fromAddressPubKey.await() + val toAddressPubKeyResult = toAddressPubKey.await() + Timber.d("solana blockhash: $recentBlockHashResult") BlockChainSpecificAndUtxo( BlockChainSpecific.Solana( - recentBlockHash = blockHash.await(), - tokenAccountExists = tokenAccountExists.await(), + recentBlockHash = recentBlockHashResult, priorityFee = gasFee.value, + fromAddressPubKey = fromAddressPubKeyResult, + toAddressPubKey = toAddressPubKeyResult, ) ) } @@ -220,16 +236,26 @@ internal class BlockChainSpecificRepositoryImpl @Inject constructor( } TokenStandard.SUI -> { - suiApi.getAllCoins(address) - BlockChainSpecificAndUtxo( BlockChainSpecific.Sui( - referenceGasPrice = gasFee.value, + referenceGasPrice = suiApi.getReferenceGasPrice(), coins = suiApi.getAllCoins(address), ), utxos = emptyList(), ) } + + TokenStandard.TON -> { + BlockChainSpecificAndUtxo( + blockChainSpecific = BlockChainSpecific.Ton( + sequenceNumber = tonApi.getSpecificTransactionInfo(address) + .toString().toULong(), + expireAt = (Clock.System.now() + .epochSeconds + 600L).toULong(), + bounceable = false, + ), + ) + } } private fun ensureOneGweiPriorityFee(priorityFee: BigInteger): BigInteger { From 72baa96ba746fbbcc4de4dc1cf65e3aefd78b867 Mon Sep 17 00:00:00 2001 From: aminsato Date: Mon, 4 Nov 2024 17:32:42 +0330 Subject: [PATCH 3/4] Update commondata --- commondata | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/commondata b/commondata index 22c6cb4f1..0cf90ae77 160000 --- a/commondata +++ b/commondata @@ -1 +1 @@ -Subproject commit 22c6cb4f1ae46205ad98d114d6c9519b9f4c71cd +Subproject commit 0cf90ae776aedf95ec3f30fdbf58e7748b280e18 From 972b87d03c255b1db915a9abd148efd55de40941 Mon Sep 17 00:00:00 2001 From: aminsato Date: Mon, 4 Nov 2024 17:36:51 +0330 Subject: [PATCH 4/4] Update commondata --- commondata | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/commondata b/commondata index 0cf90ae77..22c6cb4f1 160000 --- a/commondata +++ b/commondata @@ -1 +1 @@ -Subproject commit 0cf90ae776aedf95ec3f30fdbf58e7748b280e18 +Subproject commit 22c6cb4f1ae46205ad98d114d6c9519b9f4c71cd