Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix invalid account data for instruction error #1241

Merged
merged 4 commits into from
Nov 8, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -407,6 +407,7 @@ internal class SendFormViewModel @Inject constructor(
isSwap = false,
isMaxAmountEnabled = isMaxAmount,
isDeposit = false,
dstAddress = dstAddress
)
.let {
val ethSettings = ethGasSettings.value
Expand Down
31 changes: 31 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 @@ -45,6 +45,7 @@ interface SolanaApi {
suspend fun getSPLTokensInfo(tokens: List<String>): List<SplTokenJson>
suspend fun getSPLTokensInfo2(tokens: List<String>): List<SplTokenInfo>
suspend fun getSPLTokenBalance(walletAddress: String, coinAddress: String): String?
suspend fun getTokenAssociatedAccountByOwner(walletAddress: String, mintAddress: String): String?
}

internal class SolanaApiImp @Inject constructor(
Expand Down Expand Up @@ -286,6 +287,36 @@ internal class SolanaApiImp @Inject constructor(
}
}

override suspend fun getTokenAssociatedAccountByOwner(
walletAddress: String,
mintAddress: String,
): String? {
try {
val response = httpClient.postRpc<SplAmountRpcResponseJson>(
url = rpcEndpoint2,
method = "getTokenAccountsByOwner",
params = buildJsonArray {
add(walletAddress)
addJsonObject {
put("mint", mintAddress)
}
addJsonObject {
put("encoding", ENCODING_SPL_REQUEST_PARAM)
}
}
)
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 {
private const val PROGRAM_ID_SPL_REQUEST_PARAM =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,8 @@ data class SplAmountAccountJson(
data class SplAmountValueJson(
@SerialName("account")
val account: SplAmountAccountJson,
@SerialName("pubkey")
val pubKey: String,
)

@Serializable
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,25 +73,21 @@ 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 transferTokenMessage =
Solana.TokenTransfer.newBuilder()
Solana.CreateAndTransferToken.newBuilder()
.setRecipientMainAddress(toAddress.description())
.setTokenMintAddress(keysignPayload.coin.contractAddress)
.setRecipientTokenAddress(generatedRecipientAssociatedAddress)
.setSenderTokenAddress(generatedSenderAssociatedAddress)
.setSenderTokenAddress(solanaSpecific.fromAddressPubKey)
.setAmount(keysignPayload.toAmount.toLong())
.setDecimals(keysignPayload.coin.decimal)

return input
.setTokenTransferTransaction(transferTokenMessage.build())
.setCreateAndTransferTokenTransaction(transferTokenMessage.build())
.build()
.toByteArray()
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ 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.coroutines.async
import kotlinx.coroutines.coroutineScope
import kotlinx.datetime.Clock
import timber.log.Timber
import java.math.BigInteger
Expand All @@ -36,6 +38,7 @@ interface BlockChainSpecificRepository {
isMaxAmountEnabled: Boolean,
isDeposit: Boolean,
gasLimit: BigInteger? = null,
dstAddress: String? = null,
): BlockChainSpecificAndUtxo

}
Expand All @@ -61,6 +64,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) {
Expand Down Expand Up @@ -165,13 +169,34 @@ 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 fromAddressPubKey = async {
solanaApi.getTokenAssociatedAccountByOwner(
token.address,
token.contractAddress
).takeIf { !token.isNativeToken }
}
val toAddressPubKey = async {
dstAddress?.let {
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,
priorityFee = gasFee.value
recentBlockHash = recentBlockHashResult,
priorityFee = gasFee.value,
fromAddressPubKey = fromAddressPubKeyResult,
toAddressPubKey = toAddressPubKeyResult,
)
)
}
Expand Down