Updated on 2026-08-14
This commit is contained in:
parent
9860ecc4cb
commit
5c11931241
21 changed files with 541 additions and 63 deletions
|
|
@ -12,6 +12,7 @@ android {
|
|||
dependencies {
|
||||
/* Project - Domain */
|
||||
implementation(projects.data.common)
|
||||
implementation(projects.domain.models)
|
||||
implementation(projects.domain.blockaid)
|
||||
implementation(projects.domain.blockaid.models)
|
||||
|
||||
|
|
@ -20,6 +21,7 @@ dependencies {
|
|||
|
||||
/* Project - Core */
|
||||
implementation(projects.core.utils)
|
||||
implementation(projects.libs.blockchainSdk)
|
||||
|
||||
/* DI */
|
||||
implementation(deps.hilt.core)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,31 @@
|
|||
package com.tangem.data.blockaid
|
||||
|
||||
import com.tangem.blockchain.blockchains.ethereum.EthereumTransactionExtras
|
||||
import com.tangem.blockchain.common.Blockchain
|
||||
import com.tangem.datasource.api.common.blockaid.models.request.BlockAidScanOptions
|
||||
import com.tangem.datasource.api.common.blockaid.models.request.Data
|
||||
import com.tangem.datasource.api.common.blockaid.models.request.EvmTransactionBulkScanRequest
|
||||
import com.tangem.datasource.api.common.blockaid.models.response.TransactionMetadata
|
||||
import com.tangem.utils.converter.Converter
|
||||
import com.tangem.blockchain.common.TransactionData as SDKTransactionData
|
||||
|
||||
internal class BlockAidEvmScanTransactionConverter(
|
||||
private val blockchain: Blockchain,
|
||||
) : Converter<List<SDKTransactionData.Uncompiled>, EvmTransactionBulkScanRequest> {
|
||||
|
||||
override fun convert(value: List<SDKTransactionData.Uncompiled>): EvmTransactionBulkScanRequest {
|
||||
return EvmTransactionBulkScanRequest(
|
||||
chain = blockchain.getChainId().toString(),
|
||||
options = listOf(BlockAidScanOptions.GasEstimation.value),
|
||||
metadata = TransactionMetadata(domain = "https://tangem.com"),
|
||||
data = value.map {
|
||||
Data(
|
||||
from = it.sourceAddress,
|
||||
to = it.destinationAddress,
|
||||
data = (it.extras as? EthereumTransactionExtras)?.callData?.dataHex.orEmpty(),
|
||||
)
|
||||
},
|
||||
aggregated = false,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -3,11 +3,19 @@ package com.tangem.data.blockaid
|
|||
import com.domain.blockaid.models.dapp.CheckDAppResult
|
||||
import com.domain.blockaid.models.dapp.DAppData
|
||||
import com.domain.blockaid.models.transaction.CheckTransactionResult
|
||||
import com.domain.blockaid.models.transaction.GasEstimationResult
|
||||
import com.domain.blockaid.models.transaction.TransactionData
|
||||
import com.tangem.domain.models.currency.CryptoCurrency
|
||||
import com.tangem.blockchain.common.TransactionData as SDKTransactionData
|
||||
|
||||
interface BlockAidRepository {
|
||||
|
||||
suspend fun verifyDAppDomain(data: DAppData): CheckDAppResult
|
||||
|
||||
suspend fun verifyTransaction(data: TransactionData): CheckTransactionResult
|
||||
|
||||
suspend fun getGasEstimation(
|
||||
cryptoCurrency: CryptoCurrency,
|
||||
transactionDataList: List<SDKTransactionData.Uncompiled>,
|
||||
): GasEstimationResult
|
||||
}
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
package com.tangem.data.blockaid
|
||||
|
||||
import arrow.core.Either
|
||||
import com.domain.blockaid.models.transaction.GasEstimationResult
|
||||
import com.tangem.blockchain.common.TransactionData
|
||||
import com.tangem.domain.blockaid.BlockAidGasEstimate
|
||||
import com.tangem.domain.models.currency.CryptoCurrency
|
||||
import javax.inject.Inject
|
||||
|
||||
class DefaultBlockAidGasEstimate @Inject constructor(
|
||||
private val repository: BlockAidRepository,
|
||||
) : BlockAidGasEstimate {
|
||||
override suspend fun getGasEstimation(
|
||||
cryptoCurrency: CryptoCurrency,
|
||||
transactionDataList: List<TransactionData.Uncompiled>,
|
||||
): Either<Throwable, GasEstimationResult> = Either.catch {
|
||||
repository.getGasEstimation(cryptoCurrency = cryptoCurrency, transactionDataList = transactionDataList)
|
||||
}
|
||||
}
|
||||
|
|
@ -3,12 +3,18 @@ package com.tangem.data.blockaid
|
|||
import com.domain.blockaid.models.dapp.CheckDAppResult
|
||||
import com.domain.blockaid.models.dapp.DAppData
|
||||
import com.domain.blockaid.models.transaction.CheckTransactionResult
|
||||
import com.domain.blockaid.models.transaction.GasEstimationResult
|
||||
import com.domain.blockaid.models.transaction.TransactionData
|
||||
import com.domain.blockaid.models.transaction.TransactionParams
|
||||
import com.tangem.blockchain.common.Blockchain
|
||||
import com.tangem.blockchainsdk.utils.toBlockchain
|
||||
import com.tangem.data.blockaid.converters.GasEstimationResponseConverter
|
||||
import com.tangem.datasource.api.common.blockaid.BlockAidApi
|
||||
import com.tangem.datasource.api.common.blockaid.models.request.DomainScanRequest
|
||||
import com.tangem.domain.models.currency.CryptoCurrency
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
import kotlinx.coroutines.withContext
|
||||
import com.tangem.blockchain.common.TransactionData as SDKTransactionData
|
||||
|
||||
internal class DefaultBlockAidRepository(
|
||||
private val api: BlockAidApi,
|
||||
|
|
@ -30,6 +36,17 @@ internal class DefaultBlockAidRepository(
|
|||
}
|
||||
}
|
||||
|
||||
override suspend fun getGasEstimation(
|
||||
cryptoCurrency: CryptoCurrency,
|
||||
transactionDataList: List<SDKTransactionData.Uncompiled>,
|
||||
): GasEstimationResult {
|
||||
val blockchain = cryptoCurrency.network.toBlockchain()
|
||||
return when {
|
||||
blockchain.isEvm() -> scanEvmTransactionBulk(blockchain, transactionDataList)
|
||||
else -> error("Gas estimation with BlockAid not supported by ${blockchain.fullName}")
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun scanEvmTransaction(data: TransactionData): CheckTransactionResult =
|
||||
withContext(dispatchers.io) {
|
||||
val response = api.scanJsonRpc(mapper.mapToEvmRequest(data))
|
||||
|
|
@ -41,4 +58,15 @@ internal class DefaultBlockAidRepository(
|
|||
val response = api.scanSolanaMessage(mapper.mapToSolanaRequest(data))
|
||||
mapper.mapToDomain(response)
|
||||
}
|
||||
|
||||
private suspend fun scanEvmTransactionBulk(
|
||||
blockchain: Blockchain,
|
||||
transactionDataList: List<SDKTransactionData.Uncompiled>,
|
||||
): GasEstimationResult = withContext(dispatchers.io) {
|
||||
val response = api.scanEvmTransactionBulk(
|
||||
BlockAidEvmScanTransactionConverter(blockchain).convert(transactionDataList),
|
||||
)
|
||||
|
||||
GasEstimationResponseConverter.convert(response)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
package com.tangem.data.blockaid.converters
|
||||
|
||||
import com.domain.blockaid.models.transaction.GasEstimationResult
|
||||
import com.tangem.blockchain.extensions.hexToBigInteger
|
||||
import com.tangem.datasource.api.common.blockaid.models.response.GasEstimationResponse
|
||||
import com.tangem.utils.converter.Converter
|
||||
|
||||
internal object GasEstimationResponseConverter : Converter<List<GasEstimationResponse>, GasEstimationResult> {
|
||||
override fun convert(value: List<GasEstimationResponse>): GasEstimationResult {
|
||||
return GasEstimationResult(
|
||||
estimatedGasList = value.map { it.gasEstimation.estimate.hexToBigInteger() },
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,6 +1,8 @@
|
|||
package com.tangem.data.blockaid.di
|
||||
|
||||
import com.tangem.data.blockaid.DefaultBlockAidGasEstimate
|
||||
import com.tangem.data.blockaid.DefaultBlockAidVerifier
|
||||
import com.tangem.domain.blockaid.BlockAidGasEstimate
|
||||
import com.tangem.domain.blockaid.BlockAidVerifier
|
||||
import dagger.Binds
|
||||
import dagger.Module
|
||||
|
|
@ -15,4 +17,8 @@ interface BlockAidDataModule {
|
|||
@Binds
|
||||
@Singleton
|
||||
fun bindVerifier(verifier: DefaultBlockAidVerifier): BlockAidVerifier
|
||||
|
||||
@Binds
|
||||
@Singleton
|
||||
fun bindBlockAidGasEstimate(gasEstimate: DefaultBlockAidGasEstimate): BlockAidGasEstimate
|
||||
}
|
||||
|
|
@ -1,7 +1,13 @@
|
|||
package com.tangem.data.transaction
|
||||
|
||||
import com.tangem.blockchain.blockchains.ethereum.EthereumWalletManager
|
||||
import com.tangem.blockchain.blockchains.ethereum.eip1559.isSupportEIP1559
|
||||
import com.tangem.blockchain.blockchains.ethereum.network.EthereumFeeHistory
|
||||
import com.tangem.blockchain.common.Amount
|
||||
import com.tangem.blockchain.common.AmountType
|
||||
import com.tangem.blockchain.common.Blockchain
|
||||
import com.tangem.blockchain.common.TransactionData
|
||||
import com.tangem.blockchain.common.transaction.Fee
|
||||
import com.tangem.blockchain.common.transaction.TransactionFee
|
||||
import com.tangem.blockchain.extensions.Result
|
||||
import com.tangem.blockchainsdk.utils.toBlockchain
|
||||
|
|
@ -12,6 +18,8 @@ import com.tangem.domain.models.network.Network
|
|||
import com.tangem.domain.models.wallet.UserWallet
|
||||
import com.tangem.domain.transaction.FeeRepository
|
||||
import com.tangem.domain.walletmanager.WalletManagersFacade
|
||||
import java.math.BigDecimal
|
||||
import java.math.BigInteger
|
||||
|
||||
internal class DefaultFeeRepository(
|
||||
private val walletManagersFacade: WalletManagersFacade,
|
||||
|
|
@ -22,6 +30,53 @@ internal class DefaultFeeRepository(
|
|||
return networkId.toBlockchain().isFeeApproximate(amountType)
|
||||
}
|
||||
|
||||
override suspend fun getEthereumFeeWithoutGas(
|
||||
userWallet: UserWallet,
|
||||
cryptoCurrency: CryptoCurrency,
|
||||
): Fee.Ethereum {
|
||||
val walletManager = walletManagersFacade.getOrCreateWalletManager(
|
||||
userWalletId = userWallet.walletId,
|
||||
network = cryptoCurrency.network,
|
||||
)
|
||||
|
||||
val blockchain = cryptoCurrency.network.toBlockchain()
|
||||
|
||||
val ethereumWalletManager = walletManager as? EthereumWalletManager
|
||||
?: error("Not supported for ${cryptoCurrency.network}")
|
||||
|
||||
val fee = if (blockchain.isSupportEIP1559) {
|
||||
val gasHistory = when (val gasHistory = ethereumWalletManager.getGasHistory()) {
|
||||
is Result.Failure -> throw gasHistory.error
|
||||
is Result.Success -> gasHistory.data
|
||||
}
|
||||
|
||||
val marketPriorityFee = when (gasHistory) {
|
||||
is EthereumFeeHistory.Common -> gasHistory.marketPriorityFee
|
||||
is EthereumFeeHistory.Fallback -> gasHistory.gasPrice.toBigDecimal() * MULTIPLIER_GAS_PRICE_NORMAL_FEE
|
||||
}
|
||||
|
||||
val maxFeePerGas = gasHistory.baseFee * MULTIPLIER_GAS_PRICE_NORMAL_FEE + marketPriorityFee
|
||||
|
||||
getEthEip1559Fee(
|
||||
maxFeePerGas = maxFeePerGas.toBigInteger(),
|
||||
priorityFee = marketPriorityFee.toBigInteger(),
|
||||
blockchain = blockchain,
|
||||
)
|
||||
} else {
|
||||
val gasPrice = when (val gasPrice = ethereumWalletManager.getGasPrice()) {
|
||||
is Result.Failure -> throw gasPrice.error
|
||||
is Result.Success -> gasPrice.data
|
||||
}
|
||||
|
||||
getEthLegacyFee(
|
||||
gasPrice = gasPrice,
|
||||
blockchain = blockchain,
|
||||
)
|
||||
}
|
||||
|
||||
return fee
|
||||
}
|
||||
|
||||
override suspend fun calculateFee(
|
||||
userWallet: UserWallet,
|
||||
cryptoCurrency: CryptoCurrency,
|
||||
|
|
@ -54,4 +109,37 @@ internal class DefaultFeeRepository(
|
|||
?: error("WalletManager is null"),
|
||||
)
|
||||
}
|
||||
|
||||
private fun getEthLegacyFee(gasPrice: BigInteger, blockchain: Blockchain): Fee.Ethereum.Legacy {
|
||||
val amount = Amount(
|
||||
value = BigDecimal.ZERO,
|
||||
blockchain = blockchain,
|
||||
)
|
||||
return Fee.Ethereum.Legacy(
|
||||
amount = amount,
|
||||
gasLimit = BigInteger.ZERO,
|
||||
gasPrice = gasPrice,
|
||||
)
|
||||
}
|
||||
|
||||
private fun getEthEip1559Fee(
|
||||
maxFeePerGas: BigInteger,
|
||||
priorityFee: BigInteger,
|
||||
blockchain: Blockchain,
|
||||
): Fee.Ethereum.EIP1559 {
|
||||
val amount = Amount(
|
||||
value = BigDecimal.ZERO,
|
||||
blockchain = blockchain,
|
||||
)
|
||||
return Fee.Ethereum.EIP1559(
|
||||
amount = amount,
|
||||
gasLimit = BigInteger.ZERO,
|
||||
maxFeePerGas = maxFeePerGas,
|
||||
priorityFee = priorityFee,
|
||||
)
|
||||
}
|
||||
|
||||
private companion object {
|
||||
val MULTIPLIER_GAS_PRICE_NORMAL_FEE = "1.2".toBigDecimal() // 120%
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue