Updated on 2026-08-14
This commit is contained in:
parent
9860ecc4cb
commit
5c11931241
21 changed files with 541 additions and 63 deletions
|
|
@ -154,6 +154,7 @@ dependencies {
|
||||||
implementation(projects.domain.walletManager)
|
implementation(projects.domain.walletManager)
|
||||||
implementation(projects.domain.walletManager.models)
|
implementation(projects.domain.walletManager.models)
|
||||||
implementation(projects.domain.yieldSupply)
|
implementation(projects.domain.yieldSupply)
|
||||||
|
implementation(projects.domain.blockaid)
|
||||||
|
|
||||||
implementation(projects.common)
|
implementation(projects.common)
|
||||||
implementation(projects.common.routing)
|
implementation(projects.common.routing)
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
package com.tangem.tap.di.domain
|
package com.tangem.tap.di.domain
|
||||||
|
|
||||||
|
import com.tangem.domain.blockaid.BlockAidGasEstimate
|
||||||
import com.tangem.domain.transaction.FeeRepository
|
import com.tangem.domain.transaction.FeeRepository
|
||||||
import com.tangem.domain.transaction.error.FeeErrorResolver
|
import com.tangem.domain.transaction.error.FeeErrorResolver
|
||||||
import com.tangem.domain.yield.supply.YieldSupplyTransactionRepository
|
import com.tangem.domain.yield.supply.YieldSupplyTransactionRepository
|
||||||
|
|
@ -42,10 +43,12 @@ internal object YieldSupplyDomainModule {
|
||||||
fun provideYieldSupplyEstimateEnterFeeUseCase(
|
fun provideYieldSupplyEstimateEnterFeeUseCase(
|
||||||
feeRepository: FeeRepository,
|
feeRepository: FeeRepository,
|
||||||
feeErrorResolver: FeeErrorResolver,
|
feeErrorResolver: FeeErrorResolver,
|
||||||
|
blockAidGasEstimate: BlockAidGasEstimate,
|
||||||
): YieldSupplyEstimateEnterFeeUseCase {
|
): YieldSupplyEstimateEnterFeeUseCase {
|
||||||
return YieldSupplyEstimateEnterFeeUseCase(
|
return YieldSupplyEstimateEnterFeeUseCase(
|
||||||
feeRepository = feeRepository,
|
feeRepository = feeRepository,
|
||||||
feeErrorResolver = feeErrorResolver,
|
feeErrorResolver = feeErrorResolver,
|
||||||
|
blockAidGasEstimate = blockAidGasEstimate,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,11 @@
|
||||||
package com.tangem.datasource.api.common.blockaid
|
package com.tangem.datasource.api.common.blockaid
|
||||||
|
|
||||||
import com.tangem.datasource.api.common.blockaid.models.request.DomainScanRequest
|
import com.tangem.datasource.api.common.blockaid.models.request.DomainScanRequest
|
||||||
|
import com.tangem.datasource.api.common.blockaid.models.request.EvmTransactionBulkScanRequest
|
||||||
import com.tangem.datasource.api.common.blockaid.models.request.EvmTransactionScanRequest
|
import com.tangem.datasource.api.common.blockaid.models.request.EvmTransactionScanRequest
|
||||||
import com.tangem.datasource.api.common.blockaid.models.request.SolanaTransactionScanRequest
|
import com.tangem.datasource.api.common.blockaid.models.request.SolanaTransactionScanRequest
|
||||||
import com.tangem.datasource.api.common.blockaid.models.response.DomainScanResponse
|
import com.tangem.datasource.api.common.blockaid.models.response.DomainScanResponse
|
||||||
|
import com.tangem.datasource.api.common.blockaid.models.response.GasEstimationResponse
|
||||||
import com.tangem.datasource.api.common.blockaid.models.response.SolanaTransactionResponse
|
import com.tangem.datasource.api.common.blockaid.models.response.SolanaTransactionResponse
|
||||||
import com.tangem.datasource.api.common.blockaid.models.response.TransactionScanResponse
|
import com.tangem.datasource.api.common.blockaid.models.response.TransactionScanResponse
|
||||||
import retrofit2.http.Body
|
import retrofit2.http.Body
|
||||||
|
|
@ -19,4 +21,7 @@ interface BlockAidApi {
|
||||||
|
|
||||||
@POST("solana/message/scan")
|
@POST("solana/message/scan")
|
||||||
suspend fun scanSolanaMessage(@Body request: SolanaTransactionScanRequest): SolanaTransactionResponse
|
suspend fun scanSolanaMessage(@Body request: SolanaTransactionScanRequest): SolanaTransactionResponse
|
||||||
|
|
||||||
|
@POST("evm/transaction-bulk/scan")
|
||||||
|
suspend fun scanEvmTransactionBulk(@Body request: EvmTransactionBulkScanRequest): List<GasEstimationResponse>
|
||||||
}
|
}
|
||||||
|
|
@ -0,0 +1,7 @@
|
||||||
|
package com.tangem.datasource.api.common.blockaid.models.request
|
||||||
|
|
||||||
|
enum class BlockAidScanOptions(val value: String) {
|
||||||
|
Simulation("simulation"),
|
||||||
|
Validation("validation"),
|
||||||
|
GasEstimation("gas_estimation"),
|
||||||
|
}
|
||||||
|
|
@ -14,9 +14,25 @@ data class EvmTransactionScanRequest(
|
||||||
@Json(name = "metadata") val metadata: TransactionMetadata,
|
@Json(name = "metadata") val metadata: TransactionMetadata,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@JsonClass(generateAdapter = true)
|
||||||
|
data class EvmTransactionBulkScanRequest(
|
||||||
|
@Json(name = "chain") val chain: String,
|
||||||
|
@Json(name = "options") val options: List<String>,
|
||||||
|
@Json(name = "metadata") val metadata: TransactionMetadata,
|
||||||
|
@Json(name = "data") val data: List<Data>,
|
||||||
|
@Json(name = "aggregated") val aggregated: Boolean = false,
|
||||||
|
)
|
||||||
|
|
||||||
@JsonClass(generateAdapter = true)
|
@JsonClass(generateAdapter = true)
|
||||||
data class RpcData(
|
data class RpcData(
|
||||||
@Json(name = "jsonrpc") val jsonrpc: String = "2.0",
|
@Json(name = "jsonrpc") val jsonrpc: String = "2.0",
|
||||||
@Json(name = "method") val method: String,
|
@Json(name = "method") val method: String,
|
||||||
@Json(name = "params") val params: List<Map<String, String>>,
|
@Json(name = "params") val params: List<Map<String, String>>,
|
||||||
|
)
|
||||||
|
|
||||||
|
@JsonClass(generateAdapter = true)
|
||||||
|
data class Data(
|
||||||
|
@Json(name = "from") val from: String,
|
||||||
|
@Json(name = "to") val to: String,
|
||||||
|
@Json(name = "data") val data: String,
|
||||||
)
|
)
|
||||||
|
|
@ -0,0 +1,14 @@
|
||||||
|
package com.tangem.datasource.api.common.blockaid.models.response
|
||||||
|
|
||||||
|
import com.squareup.moshi.Json
|
||||||
|
import com.squareup.moshi.JsonClass
|
||||||
|
|
||||||
|
@JsonClass(generateAdapter = true)
|
||||||
|
data class GasEstimationResponse(
|
||||||
|
@Json(name = "gas_estimation") val gasEstimation: GasEstimationItem,
|
||||||
|
)
|
||||||
|
|
||||||
|
@JsonClass(generateAdapter = true)
|
||||||
|
data class GasEstimationItem(
|
||||||
|
@Json(name = "estimate") val estimate: String,
|
||||||
|
)
|
||||||
|
|
@ -12,6 +12,7 @@ android {
|
||||||
dependencies {
|
dependencies {
|
||||||
/* Project - Domain */
|
/* Project - Domain */
|
||||||
implementation(projects.data.common)
|
implementation(projects.data.common)
|
||||||
|
implementation(projects.domain.models)
|
||||||
implementation(projects.domain.blockaid)
|
implementation(projects.domain.blockaid)
|
||||||
implementation(projects.domain.blockaid.models)
|
implementation(projects.domain.blockaid.models)
|
||||||
|
|
||||||
|
|
@ -20,6 +21,7 @@ dependencies {
|
||||||
|
|
||||||
/* Project - Core */
|
/* Project - Core */
|
||||||
implementation(projects.core.utils)
|
implementation(projects.core.utils)
|
||||||
|
implementation(projects.libs.blockchainSdk)
|
||||||
|
|
||||||
/* DI */
|
/* DI */
|
||||||
implementation(deps.hilt.core)
|
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.CheckDAppResult
|
||||||
import com.domain.blockaid.models.dapp.DAppData
|
import com.domain.blockaid.models.dapp.DAppData
|
||||||
import com.domain.blockaid.models.transaction.CheckTransactionResult
|
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.TransactionData
|
||||||
|
import com.tangem.domain.models.currency.CryptoCurrency
|
||||||
|
import com.tangem.blockchain.common.TransactionData as SDKTransactionData
|
||||||
|
|
||||||
interface BlockAidRepository {
|
interface BlockAidRepository {
|
||||||
|
|
||||||
suspend fun verifyDAppDomain(data: DAppData): CheckDAppResult
|
suspend fun verifyDAppDomain(data: DAppData): CheckDAppResult
|
||||||
|
|
||||||
suspend fun verifyTransaction(data: TransactionData): CheckTransactionResult
|
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.CheckDAppResult
|
||||||
import com.domain.blockaid.models.dapp.DAppData
|
import com.domain.blockaid.models.dapp.DAppData
|
||||||
import com.domain.blockaid.models.transaction.CheckTransactionResult
|
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.TransactionData
|
||||||
import com.domain.blockaid.models.transaction.TransactionParams
|
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.BlockAidApi
|
||||||
import com.tangem.datasource.api.common.blockaid.models.request.DomainScanRequest
|
import com.tangem.datasource.api.common.blockaid.models.request.DomainScanRequest
|
||||||
|
import com.tangem.domain.models.currency.CryptoCurrency
|
||||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
|
import com.tangem.blockchain.common.TransactionData as SDKTransactionData
|
||||||
|
|
||||||
internal class DefaultBlockAidRepository(
|
internal class DefaultBlockAidRepository(
|
||||||
private val api: BlockAidApi,
|
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 =
|
private suspend fun scanEvmTransaction(data: TransactionData): CheckTransactionResult =
|
||||||
withContext(dispatchers.io) {
|
withContext(dispatchers.io) {
|
||||||
val response = api.scanJsonRpc(mapper.mapToEvmRequest(data))
|
val response = api.scanJsonRpc(mapper.mapToEvmRequest(data))
|
||||||
|
|
@ -41,4 +58,15 @@ internal class DefaultBlockAidRepository(
|
||||||
val response = api.scanSolanaMessage(mapper.mapToSolanaRequest(data))
|
val response = api.scanSolanaMessage(mapper.mapToSolanaRequest(data))
|
||||||
mapper.mapToDomain(response)
|
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
|
package com.tangem.data.blockaid.di
|
||||||
|
|
||||||
|
import com.tangem.data.blockaid.DefaultBlockAidGasEstimate
|
||||||
import com.tangem.data.blockaid.DefaultBlockAidVerifier
|
import com.tangem.data.blockaid.DefaultBlockAidVerifier
|
||||||
|
import com.tangem.domain.blockaid.BlockAidGasEstimate
|
||||||
import com.tangem.domain.blockaid.BlockAidVerifier
|
import com.tangem.domain.blockaid.BlockAidVerifier
|
||||||
import dagger.Binds
|
import dagger.Binds
|
||||||
import dagger.Module
|
import dagger.Module
|
||||||
|
|
@ -15,4 +17,8 @@ interface BlockAidDataModule {
|
||||||
@Binds
|
@Binds
|
||||||
@Singleton
|
@Singleton
|
||||||
fun bindVerifier(verifier: DefaultBlockAidVerifier): BlockAidVerifier
|
fun bindVerifier(verifier: DefaultBlockAidVerifier): BlockAidVerifier
|
||||||
|
|
||||||
|
@Binds
|
||||||
|
@Singleton
|
||||||
|
fun bindBlockAidGasEstimate(gasEstimate: DefaultBlockAidGasEstimate): BlockAidGasEstimate
|
||||||
}
|
}
|
||||||
|
|
@ -1,7 +1,13 @@
|
||||||
package com.tangem.data.transaction
|
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.AmountType
|
||||||
|
import com.tangem.blockchain.common.Blockchain
|
||||||
import com.tangem.blockchain.common.TransactionData
|
import com.tangem.blockchain.common.TransactionData
|
||||||
|
import com.tangem.blockchain.common.transaction.Fee
|
||||||
import com.tangem.blockchain.common.transaction.TransactionFee
|
import com.tangem.blockchain.common.transaction.TransactionFee
|
||||||
import com.tangem.blockchain.extensions.Result
|
import com.tangem.blockchain.extensions.Result
|
||||||
import com.tangem.blockchainsdk.utils.toBlockchain
|
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.models.wallet.UserWallet
|
||||||
import com.tangem.domain.transaction.FeeRepository
|
import com.tangem.domain.transaction.FeeRepository
|
||||||
import com.tangem.domain.walletmanager.WalletManagersFacade
|
import com.tangem.domain.walletmanager.WalletManagersFacade
|
||||||
|
import java.math.BigDecimal
|
||||||
|
import java.math.BigInteger
|
||||||
|
|
||||||
internal class DefaultFeeRepository(
|
internal class DefaultFeeRepository(
|
||||||
private val walletManagersFacade: WalletManagersFacade,
|
private val walletManagersFacade: WalletManagersFacade,
|
||||||
|
|
@ -22,6 +30,53 @@ internal class DefaultFeeRepository(
|
||||||
return networkId.toBlockchain().isFeeApproximate(amountType)
|
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(
|
override suspend fun calculateFee(
|
||||||
userWallet: UserWallet,
|
userWallet: UserWallet,
|
||||||
cryptoCurrency: CryptoCurrency,
|
cryptoCurrency: CryptoCurrency,
|
||||||
|
|
@ -54,4 +109,37 @@ internal class DefaultFeeRepository(
|
||||||
?: error("WalletManager is null"),
|
?: 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%
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1,13 +1,23 @@
|
||||||
plugins {
|
plugins {
|
||||||
alias(deps.plugins.kotlin.jvm)
|
alias(deps.plugins.android.library)
|
||||||
|
alias(deps.plugins.kotlin.android)
|
||||||
id("configuration")
|
id("configuration")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
android {
|
||||||
|
namespace = "com.tangem.domain.blockaid"
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
/* Project - Domain */
|
/** Project - Domain */
|
||||||
implementation(projects.domain.core)
|
implementation(projects.domain.core)
|
||||||
implementation(projects.domain.blockaid.models)
|
implementation(projects.domain.blockaid.models)
|
||||||
|
|
||||||
/* Other */
|
/** Tangem SDK */
|
||||||
|
implementation(tangemDeps.blockchain)
|
||||||
|
|
||||||
|
|
||||||
|
/** Other */
|
||||||
implementation(deps.moshi.adapters)
|
implementation(deps.moshi.adapters)
|
||||||
}
|
}
|
||||||
|
|
@ -0,0 +1,7 @@
|
||||||
|
package com.domain.blockaid.models.transaction
|
||||||
|
|
||||||
|
import java.math.BigInteger
|
||||||
|
|
||||||
|
data class GasEstimationResult(
|
||||||
|
val estimatedGasList: List<BigInteger>,
|
||||||
|
)
|
||||||
|
|
@ -0,0 +1,14 @@
|
||||||
|
package com.tangem.domain.blockaid
|
||||||
|
|
||||||
|
import arrow.core.Either
|
||||||
|
import com.domain.blockaid.models.transaction.GasEstimationResult
|
||||||
|
import com.tangem.blockchain.common.TransactionData
|
||||||
|
import com.tangem.domain.models.currency.CryptoCurrency
|
||||||
|
|
||||||
|
interface BlockAidGasEstimate {
|
||||||
|
|
||||||
|
suspend fun getGasEstimation(
|
||||||
|
cryptoCurrency: CryptoCurrency,
|
||||||
|
transactionDataList: List<TransactionData.Uncompiled>,
|
||||||
|
): Either<Throwable, GasEstimationResult>
|
||||||
|
}
|
||||||
|
|
@ -2,6 +2,7 @@ package com.tangem.domain.transaction
|
||||||
|
|
||||||
import com.tangem.blockchain.common.AmountType
|
import com.tangem.blockchain.common.AmountType
|
||||||
import com.tangem.blockchain.common.TransactionData
|
import com.tangem.blockchain.common.TransactionData
|
||||||
|
import com.tangem.blockchain.common.transaction.Fee
|
||||||
import com.tangem.blockchain.common.transaction.TransactionFee
|
import com.tangem.blockchain.common.transaction.TransactionFee
|
||||||
import com.tangem.domain.models.currency.CryptoCurrency
|
import com.tangem.domain.models.currency.CryptoCurrency
|
||||||
import com.tangem.domain.models.network.Network
|
import com.tangem.domain.models.network.Network
|
||||||
|
|
@ -18,4 +19,6 @@ interface FeeRepository {
|
||||||
cryptoCurrency: CryptoCurrency,
|
cryptoCurrency: CryptoCurrency,
|
||||||
transactionData: TransactionData,
|
transactionData: TransactionData,
|
||||||
): TransactionFee
|
): TransactionFee
|
||||||
|
|
||||||
|
suspend fun getEthereumFeeWithoutGas(userWallet: UserWallet, cryptoCurrency: CryptoCurrency): Fee.Ethereum
|
||||||
}
|
}
|
||||||
|
|
@ -18,12 +18,15 @@ dependencies {
|
||||||
implementation(projects.domain.transaction.models)
|
implementation(projects.domain.transaction.models)
|
||||||
implementation(projects.domain.transaction)
|
implementation(projects.domain.transaction)
|
||||||
implementation(projects.domain.legacy)
|
implementation(projects.domain.legacy)
|
||||||
|
implementation(projects.domain.blockaid.models)
|
||||||
|
implementation(projects.domain.blockaid)
|
||||||
|
|
||||||
/** Tandem SDK */
|
/** Tandem SDK */
|
||||||
implementation(tangemDeps.blockchain)
|
implementation(tangemDeps.blockchain)
|
||||||
|
|
||||||
/** Other */
|
/** Other */
|
||||||
implementation(deps.arrow.core)
|
implementation(deps.arrow.core)
|
||||||
|
implementation(deps.timber)
|
||||||
|
|
||||||
/** tests */
|
/** tests */
|
||||||
testImplementation(projects.common.test)
|
testImplementation(projects.common.test)
|
||||||
|
|
|
||||||
|
|
@ -5,23 +5,85 @@ import com.tangem.blockchain.blockchains.ethereum.EthereumTransactionExtras
|
||||||
import com.tangem.blockchain.common.TransactionData
|
import com.tangem.blockchain.common.TransactionData
|
||||||
import com.tangem.blockchain.common.transaction.Fee
|
import com.tangem.blockchain.common.transaction.Fee
|
||||||
import com.tangem.blockchain.yieldsupply.providers.ethereum.yield.EthereumYieldSupplyEnterCallData
|
import com.tangem.blockchain.yieldsupply.providers.ethereum.yield.EthereumYieldSupplyEnterCallData
|
||||||
|
import com.tangem.domain.blockaid.BlockAidGasEstimate
|
||||||
import com.tangem.domain.models.currency.CryptoCurrency
|
import com.tangem.domain.models.currency.CryptoCurrency
|
||||||
import com.tangem.domain.models.wallet.UserWallet
|
import com.tangem.domain.models.wallet.UserWallet
|
||||||
import com.tangem.domain.transaction.FeeRepository
|
import com.tangem.domain.transaction.FeeRepository
|
||||||
import com.tangem.domain.transaction.error.FeeErrorResolver
|
import com.tangem.domain.transaction.error.FeeErrorResolver
|
||||||
import com.tangem.domain.transaction.error.GetFeeError
|
import com.tangem.domain.transaction.error.GetFeeError
|
||||||
import com.tangem.utils.extensions.isSingleItem
|
import com.tangem.utils.extensions.isSingleItem
|
||||||
|
import timber.log.Timber
|
||||||
|
import java.math.BigInteger
|
||||||
|
|
||||||
class YieldSupplyEstimateEnterFeeUseCase(
|
class YieldSupplyEstimateEnterFeeUseCase(
|
||||||
private val feeRepository: FeeRepository,
|
private val feeRepository: FeeRepository,
|
||||||
private val feeErrorResolver: FeeErrorResolver,
|
private val feeErrorResolver: FeeErrorResolver,
|
||||||
|
private val blockAidGasEstimate: BlockAidGasEstimate,
|
||||||
) {
|
) {
|
||||||
|
|
||||||
suspend operator fun invoke(
|
suspend operator fun invoke(
|
||||||
userWallet: UserWallet,
|
userWallet: UserWallet,
|
||||||
cryptoCurrency: CryptoCurrency,
|
cryptoCurrency: CryptoCurrency,
|
||||||
transactionDataList: List<TransactionData.Uncompiled>,
|
transactionDataList: List<TransactionData.Uncompiled>,
|
||||||
): Either<GetFeeError, List<TransactionData.Uncompiled>> = Either.catch {
|
): Either<GetFeeError, List<TransactionData.Uncompiled>> = Either.catch {
|
||||||
val withCalculatedFee = transactionDataList.filter {
|
if (transactionDataList.isSingleItem()) {
|
||||||
|
transactionDataList.map { transaction ->
|
||||||
|
transaction.copy(
|
||||||
|
fee = feeRepository.calculateFee(
|
||||||
|
userWallet = userWallet,
|
||||||
|
cryptoCurrency = cryptoCurrency,
|
||||||
|
transactionData = transaction,
|
||||||
|
).normal,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
val estimatedFees = estimateFeeWithBlockAid(
|
||||||
|
userWallet = userWallet,
|
||||||
|
cryptoCurrency = cryptoCurrency,
|
||||||
|
transactionDataList = transactionDataList,
|
||||||
|
)
|
||||||
|
|
||||||
|
if (estimatedFees.isNullOrEmpty()) {
|
||||||
|
// Fallback in case block aid returns error
|
||||||
|
estimateFeeWithStatic(
|
||||||
|
userWallet = userWallet,
|
||||||
|
cryptoCurrency = cryptoCurrency,
|
||||||
|
transactionDataList = transactionDataList,
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
estimatedFees
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}.mapLeft(feeErrorResolver::resolve)
|
||||||
|
|
||||||
|
private suspend fun estimateFeeWithBlockAid(
|
||||||
|
userWallet: UserWallet,
|
||||||
|
cryptoCurrency: CryptoCurrency,
|
||||||
|
transactionDataList: List<TransactionData.Uncompiled>,
|
||||||
|
): List<TransactionData.Uncompiled>? {
|
||||||
|
val estimatedFees = blockAidGasEstimate.getGasEstimation(
|
||||||
|
cryptoCurrency = cryptoCurrency,
|
||||||
|
transactionDataList = transactionDataList,
|
||||||
|
).onLeft(Timber::e).getOrNull() ?: return null
|
||||||
|
|
||||||
|
if (estimatedFees.estimatedGasList.isEmpty()) return null
|
||||||
|
|
||||||
|
val fee = feeRepository.getEthereumFeeWithoutGas(
|
||||||
|
userWallet = userWallet,
|
||||||
|
cryptoCurrency = cryptoCurrency,
|
||||||
|
)
|
||||||
|
|
||||||
|
return transactionDataList.zip(estimatedFees.estimatedGasList) { transaction, estimatedGas ->
|
||||||
|
transaction.copy(fee = fee.fixFee(cryptoCurrency, estimatedGas))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private suspend fun estimateFeeWithStatic(
|
||||||
|
userWallet: UserWallet,
|
||||||
|
cryptoCurrency: CryptoCurrency,
|
||||||
|
transactionDataList: List<TransactionData.Uncompiled>,
|
||||||
|
): List<TransactionData.Uncompiled> {
|
||||||
|
val withCalculatedFees = transactionDataList.filter {
|
||||||
(it.extras as? EthereumTransactionExtras)?.callData !is EthereumYieldSupplyEnterCallData
|
(it.extras as? EthereumTransactionExtras)?.callData !is EthereumYieldSupplyEnterCallData
|
||||||
}.map { transaction ->
|
}.map { transaction ->
|
||||||
transaction.copy(
|
transaction.copy(
|
||||||
|
|
@ -33,38 +95,30 @@ class YieldSupplyEstimateEnterFeeUseCase(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
val withEstimatedCalculatedFee = transactionDataList.filter {
|
val calculatedFee = withCalculatedFees.firstOrNull()?.fee ?: error("Must be any calculated fee")
|
||||||
|
|
||||||
|
val withEstimatedFees = transactionDataList.filter {
|
||||||
(it.extras as? EthereumTransactionExtras)?.callData is EthereumYieldSupplyEnterCallData
|
(it.extras as? EthereumTransactionExtras)?.callData is EthereumYieldSupplyEnterCallData
|
||||||
}.map { transaction ->
|
}.map { transaction ->
|
||||||
if (transactionDataList.isSingleItem()) {
|
transaction.copy(fee = calculatedFee.fixFee(cryptoCurrency, ETHEREUM_CONSTANT_GAS_LIMIT))
|
||||||
transaction.copy(
|
|
||||||
fee = feeRepository.calculateFee(
|
|
||||||
userWallet = userWallet,
|
|
||||||
cryptoCurrency = cryptoCurrency,
|
|
||||||
transactionData = transaction,
|
|
||||||
).normal,
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
transaction.copy(fee = withCalculatedFee.firstOrNull()?.fee?.fixFee(cryptoCurrency))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Transactions order must be preserved
|
// First return calculated fees then estimated
|
||||||
withCalculatedFee + withEstimatedCalculatedFee
|
return withCalculatedFees + withEstimatedFees
|
||||||
}.mapLeft(feeErrorResolver::resolve)
|
}
|
||||||
|
|
||||||
private fun Fee.fixFee(cryptoCurrency: CryptoCurrency) = when (this) {
|
private fun Fee.fixFee(cryptoCurrency: CryptoCurrency, gasLimit: BigInteger) = when (this) {
|
||||||
is Fee.Ethereum.Legacy -> copy(
|
is Fee.Ethereum.Legacy -> copy(
|
||||||
gasLimit = ETHEREUM_CONSTANT_GAS_LIMIT,
|
gasLimit = gasLimit,
|
||||||
amount = amount.copy(
|
amount = amount.copy(
|
||||||
value = gasPrice.multiply(ETHEREUM_CONSTANT_GAS_LIMIT)
|
value = gasPrice.multiply(gasLimit)
|
||||||
.toBigDecimal().movePointLeft(cryptoCurrency.decimals),
|
.toBigDecimal().movePointLeft(cryptoCurrency.decimals),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
is Fee.Ethereum.EIP1559 -> copy(
|
is Fee.Ethereum.EIP1559 -> copy(
|
||||||
gasLimit = ETHEREUM_CONSTANT_GAS_LIMIT,
|
gasLimit = gasLimit,
|
||||||
amount = amount.copy(
|
amount = amount.copy(
|
||||||
value = maxFeePerGas.multiply(ETHEREUM_CONSTANT_GAS_LIMIT)
|
value = maxFeePerGas.multiply(gasLimit)
|
||||||
.toBigDecimal().movePointLeft(cryptoCurrency.decimals),
|
.toBigDecimal().movePointLeft(cryptoCurrency.decimals),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
@ -73,6 +127,6 @@ class YieldSupplyEstimateEnterFeeUseCase(
|
||||||
|
|
||||||
private companion object {
|
private companion object {
|
||||||
// Using constant gas limit to avoid fee calculation errors when contract address is not deployed yet
|
// Using constant gas limit to avoid fee calculation errors when contract address is not deployed yet
|
||||||
val ETHEREUM_CONSTANT_GAS_LIMIT = 350_000.toBigInteger()
|
val ETHEREUM_CONSTANT_GAS_LIMIT = 500_000.toBigInteger()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1,6 +1,8 @@
|
||||||
package com.tangem.domain.yield.supply
|
package com.tangem.domain.yield.supply
|
||||||
|
|
||||||
import arrow.core.Either
|
import arrow.core.Either
|
||||||
|
import arrow.core.right
|
||||||
|
import com.domain.blockaid.models.transaction.GasEstimationResult
|
||||||
import com.google.common.truth.Truth
|
import com.google.common.truth.Truth
|
||||||
import com.tangem.blockchain.blockchains.ethereum.EthereumTransactionExtras
|
import com.tangem.blockchain.blockchains.ethereum.EthereumTransactionExtras
|
||||||
import com.tangem.blockchain.common.Blockchain
|
import com.tangem.blockchain.common.Blockchain
|
||||||
|
|
@ -10,6 +12,7 @@ import com.tangem.blockchain.common.smartcontract.SmartContractCallDataProviderF
|
||||||
import com.tangem.blockchain.common.transaction.Fee
|
import com.tangem.blockchain.common.transaction.Fee
|
||||||
import com.tangem.blockchain.common.transaction.TransactionFee
|
import com.tangem.blockchain.common.transaction.TransactionFee
|
||||||
import com.tangem.blockchain.yieldsupply.YieldSupplyContractCallDataProviderFactory
|
import com.tangem.blockchain.yieldsupply.YieldSupplyContractCallDataProviderFactory
|
||||||
|
import com.tangem.domain.blockaid.BlockAidGasEstimate
|
||||||
import com.tangem.domain.models.currency.CryptoCurrency
|
import com.tangem.domain.models.currency.CryptoCurrency
|
||||||
import com.tangem.domain.models.currency.CryptoCurrencyStatus
|
import com.tangem.domain.models.currency.CryptoCurrencyStatus
|
||||||
import com.tangem.domain.models.wallet.UserWallet
|
import com.tangem.domain.models.wallet.UserWallet
|
||||||
|
|
@ -17,9 +20,7 @@ import com.tangem.domain.transaction.FeeRepository
|
||||||
import com.tangem.domain.transaction.error.FeeErrorResolver
|
import com.tangem.domain.transaction.error.FeeErrorResolver
|
||||||
import com.tangem.domain.utils.convertToSdkAmount
|
import com.tangem.domain.utils.convertToSdkAmount
|
||||||
import com.tangem.domain.yield.supply.usecase.YieldSupplyEstimateEnterFeeUseCase
|
import com.tangem.domain.yield.supply.usecase.YieldSupplyEstimateEnterFeeUseCase
|
||||||
import io.mockk.coEvery
|
import io.mockk.*
|
||||||
import io.mockk.every
|
|
||||||
import io.mockk.mockk
|
|
||||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||||
import kotlinx.coroutines.test.runTest
|
import kotlinx.coroutines.test.runTest
|
||||||
import org.junit.jupiter.api.Test
|
import org.junit.jupiter.api.Test
|
||||||
|
|
@ -30,7 +31,8 @@ import java.math.BigInteger
|
||||||
class YieldSupplyEstimateEnterFeeUseCaseTest {
|
class YieldSupplyEstimateEnterFeeUseCaseTest {
|
||||||
private val feeRepository: FeeRepository = mockk()
|
private val feeRepository: FeeRepository = mockk()
|
||||||
private val feeErrorResolver: FeeErrorResolver = mockk()
|
private val feeErrorResolver: FeeErrorResolver = mockk()
|
||||||
private val useCase = YieldSupplyEstimateEnterFeeUseCase(feeRepository, feeErrorResolver)
|
private val blockAidGasEstimate: BlockAidGasEstimate = mockk()
|
||||||
|
private val useCase = YieldSupplyEstimateEnterFeeUseCase(feeRepository, feeErrorResolver, blockAidGasEstimate)
|
||||||
|
|
||||||
private val userWallet: UserWallet = mockk()
|
private val userWallet: UserWallet = mockk()
|
||||||
private val cryptoCurrency: CryptoCurrency = mockk(relaxed = true) {
|
private val cryptoCurrency: CryptoCurrency = mockk(relaxed = true) {
|
||||||
|
|
@ -72,45 +74,57 @@ class YieldSupplyEstimateEnterFeeUseCaseTest {
|
||||||
@Test
|
@Test
|
||||||
fun `test enter transaction uses constant gas limit Legacy`() = runTest {
|
fun `test enter transaction uses constant gas limit Legacy`() = runTest {
|
||||||
val fee = TransactionFee.Single(ethLegacyFee())
|
val fee = TransactionFee.Single(ethLegacyFee())
|
||||||
|
val deployTx = getDeployTx()
|
||||||
|
val enterTx = getEnterTx()
|
||||||
|
|
||||||
|
coEvery { blockAidGasEstimate.getGasEstimation(any(), any()) } returns GasEstimationResult(emptyList()).right()
|
||||||
coEvery { feeRepository.calculateFee(any(), any(), any()) } returns fee
|
coEvery { feeRepository.calculateFee(any(), any(), any()) } returns fee
|
||||||
|
|
||||||
val result = useCase(
|
val result = useCase(
|
||||||
userWallet = userWallet,
|
userWallet = userWallet,
|
||||||
cryptoCurrency = cryptoCurrency,
|
cryptoCurrency = cryptoCurrency,
|
||||||
transactionDataList = listOf(
|
transactionDataList = listOf(deployTx, enterTx),
|
||||||
getDeployTx(),
|
|
||||||
getEnterTx(),
|
|
||||||
),
|
|
||||||
)
|
)
|
||||||
Truth.assertThat(result.isRight()).isTrue()
|
Truth.assertThat(result.isRight()).isTrue()
|
||||||
|
|
||||||
val txs = (result as Either.Right).value
|
coVerify(ordering = Ordering.ORDERED) {
|
||||||
|
blockAidGasEstimate.getGasEstimation(any(), any())
|
||||||
|
feeRepository.calculateFee(any(), any(), deployTx)
|
||||||
|
}
|
||||||
|
coVerify(inverse = true) {
|
||||||
|
feeRepository.calculateFee(any(), any(), enterTx)
|
||||||
|
}
|
||||||
|
|
||||||
|
val txs = (result as Either.Right).value
|
||||||
val deployFee = txs.first().fee as Fee.Ethereum.Legacy
|
val deployFee = txs.first().fee as Fee.Ethereum.Legacy
|
||||||
val enterFee = txs.last().fee as Fee.Ethereum.Legacy
|
val enterFee = txs.last().fee as Fee.Ethereum.Legacy
|
||||||
Truth.assertThat(deployFee.gasLimit).isEqualTo(BigInteger.valueOf(21_000))
|
Truth.assertThat(deployFee.gasLimit).isEqualTo(BigInteger.valueOf(21_000))
|
||||||
Truth.assertThat(enterFee.gasLimit).isEqualTo(BigInteger.valueOf(350_000))
|
Truth.assertThat(enterFee.gasLimit).isEqualTo(BigInteger.valueOf(500_000))
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `test not enter transaction uses gas limit Legacy`() = runTest {
|
fun `test not enter transaction uses gas limit Legacy`() = runTest {
|
||||||
val fee = TransactionFee.Single(ethLegacyFee())
|
val fee = TransactionFee.Single(ethLegacyFee())
|
||||||
|
val deployTx = getDeployTx()
|
||||||
|
val approveTx = getApproveTx()
|
||||||
|
|
||||||
|
coEvery { blockAidGasEstimate.getGasEstimation(any(), any()) } returns GasEstimationResult(emptyList()).right()
|
||||||
coEvery { feeRepository.calculateFee(any(), any(), any()) } returnsMany listOf(fee, fee)
|
coEvery { feeRepository.calculateFee(any(), any(), any()) } returnsMany listOf(fee, fee)
|
||||||
|
|
||||||
val result = useCase(
|
val result = useCase(
|
||||||
userWallet = userWallet,
|
userWallet = userWallet,
|
||||||
cryptoCurrency = cryptoCurrency,
|
cryptoCurrency = cryptoCurrency,
|
||||||
transactionDataList = listOf(
|
transactionDataList = listOf(deployTx, approveTx),
|
||||||
getDeployTx(),
|
|
||||||
getApproveTx(),
|
|
||||||
),
|
|
||||||
)
|
)
|
||||||
Truth.assertThat(result.isRight()).isTrue()
|
Truth.assertThat(result.isRight()).isTrue()
|
||||||
|
|
||||||
val txs = (result as Either.Right).value
|
coVerify(ordering = Ordering.ORDERED) {
|
||||||
|
blockAidGasEstimate.getGasEstimation(any(), any())
|
||||||
|
feeRepository.calculateFee(any(), any(), deployTx)
|
||||||
|
feeRepository.calculateFee(any(), any(), approveTx)
|
||||||
|
}
|
||||||
|
|
||||||
|
val txs = (result as Either.Right).value
|
||||||
val deployFee = txs.first().fee as Fee.Ethereum.Legacy
|
val deployFee = txs.first().fee as Fee.Ethereum.Legacy
|
||||||
val approveFee = txs.last().fee as Fee.Ethereum.Legacy
|
val approveFee = txs.last().fee as Fee.Ethereum.Legacy
|
||||||
Truth.assertThat(deployFee.gasLimit).isEqualTo(BigInteger.valueOf(21_000))
|
Truth.assertThat(deployFee.gasLimit).isEqualTo(BigInteger.valueOf(21_000))
|
||||||
|
|
@ -120,69 +134,88 @@ class YieldSupplyEstimateEnterFeeUseCaseTest {
|
||||||
@Test
|
@Test
|
||||||
fun `test enter transaction with wrong order uses gas limit Legacy`() = runTest {
|
fun `test enter transaction with wrong order uses gas limit Legacy`() = runTest {
|
||||||
val fee = TransactionFee.Single(ethLegacyFee())
|
val fee = TransactionFee.Single(ethLegacyFee())
|
||||||
|
val enterTx = getEnterTx()
|
||||||
|
val deployTx = getDeployTx()
|
||||||
|
|
||||||
|
coEvery { blockAidGasEstimate.getGasEstimation(any(), any()) } returns GasEstimationResult(emptyList()).right()
|
||||||
coEvery { feeRepository.calculateFee(any(), any(), any()) } returnsMany listOf(fee, fee)
|
coEvery { feeRepository.calculateFee(any(), any(), any()) } returnsMany listOf(fee, fee)
|
||||||
|
|
||||||
val result = useCase(
|
val result = useCase(
|
||||||
userWallet = userWallet,
|
userWallet = userWallet,
|
||||||
cryptoCurrency = cryptoCurrency,
|
cryptoCurrency = cryptoCurrency,
|
||||||
transactionDataList = listOf(
|
transactionDataList = listOf(enterTx, deployTx),
|
||||||
getEnterTx(),
|
|
||||||
getDeployTx(),
|
|
||||||
),
|
|
||||||
)
|
)
|
||||||
Truth.assertThat(result.isRight()).isTrue()
|
Truth.assertThat(result.isRight()).isTrue()
|
||||||
|
|
||||||
val txs = (result as Either.Right).value
|
coVerify(ordering = Ordering.ORDERED) {
|
||||||
|
blockAidGasEstimate.getGasEstimation(any(), any())
|
||||||
|
feeRepository.calculateFee(any(), any(), deployTx)
|
||||||
|
}
|
||||||
|
coVerify(inverse = true) {
|
||||||
|
feeRepository.calculateFee(any(), any(), enterTx)
|
||||||
|
}
|
||||||
|
|
||||||
|
val txs = (result as Either.Right).value
|
||||||
val deployFee = txs.first().fee as Fee.Ethereum.Legacy
|
val deployFee = txs.first().fee as Fee.Ethereum.Legacy
|
||||||
val enterFee = txs.last().fee as Fee.Ethereum.Legacy
|
val enterFee = txs.last().fee as Fee.Ethereum.Legacy
|
||||||
Truth.assertThat(deployFee.gasLimit).isEqualTo(BigInteger.valueOf(21_000))
|
Truth.assertThat(deployFee.gasLimit).isEqualTo(BigInteger.valueOf(21_000))
|
||||||
Truth.assertThat(enterFee.gasLimit).isEqualTo(BigInteger.valueOf(350_000))
|
Truth.assertThat(enterFee.gasLimit).isEqualTo(BigInteger.valueOf(500_000))
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `test enter transaction uses constant gas limit Eip1559`() = runTest {
|
fun `test enter transaction uses constant gas limit Eip1559`() = runTest {
|
||||||
val fee = TransactionFee.Single(ethEip1559Fee())
|
val fee = TransactionFee.Single(ethEip1559Fee())
|
||||||
|
val deployTx = getDeployTx()
|
||||||
|
val enterTx = getEnterTx()
|
||||||
|
|
||||||
|
coEvery { blockAidGasEstimate.getGasEstimation(any(), any()) } returns GasEstimationResult(emptyList()).right()
|
||||||
coEvery { feeRepository.calculateFee(any(), any(), any()) } returns fee
|
coEvery { feeRepository.calculateFee(any(), any(), any()) } returns fee
|
||||||
|
|
||||||
val result = useCase(
|
val result = useCase(
|
||||||
userWallet = userWallet,
|
userWallet = userWallet,
|
||||||
cryptoCurrency = cryptoCurrency,
|
cryptoCurrency = cryptoCurrency,
|
||||||
transactionDataList = listOf(
|
transactionDataList = listOf(deployTx, enterTx),
|
||||||
getDeployTx(),
|
|
||||||
getEnterTx(),
|
|
||||||
),
|
|
||||||
)
|
)
|
||||||
Truth.assertThat(result.isRight()).isTrue()
|
Truth.assertThat(result.isRight()).isTrue()
|
||||||
|
|
||||||
val txs = (result as Either.Right).value
|
coVerify(ordering = Ordering.ORDERED) {
|
||||||
|
blockAidGasEstimate.getGasEstimation(any(), any())
|
||||||
|
feeRepository.calculateFee(any(), any(), deployTx)
|
||||||
|
}
|
||||||
|
coVerify(inverse = true) {
|
||||||
|
feeRepository.calculateFee(any(), any(), enterTx)
|
||||||
|
}
|
||||||
|
|
||||||
|
val txs = (result as Either.Right).value
|
||||||
val deployFee = txs.first().fee as Fee.Ethereum.EIP1559
|
val deployFee = txs.first().fee as Fee.Ethereum.EIP1559
|
||||||
val enterFee = txs.last().fee as Fee.Ethereum.EIP1559
|
val enterFee = txs.last().fee as Fee.Ethereum.EIP1559
|
||||||
Truth.assertThat(deployFee.gasLimit).isEqualTo(BigInteger.valueOf(21_000))
|
Truth.assertThat(deployFee.gasLimit).isEqualTo(BigInteger.valueOf(21_000))
|
||||||
Truth.assertThat(enterFee.gasLimit).isEqualTo(BigInteger.valueOf(350_000))
|
Truth.assertThat(enterFee.gasLimit).isEqualTo(BigInteger.valueOf(500_000))
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `test not enter transaction uses gas limit Eip1559`() = runTest {
|
fun `test not enter transaction uses gas limit Eip1559`() = runTest {
|
||||||
val fee = TransactionFee.Single(ethEip1559Fee())
|
val fee = TransactionFee.Single(ethEip1559Fee())
|
||||||
|
val deployTx = getDeployTx()
|
||||||
|
val approveTx = getApproveTx()
|
||||||
|
|
||||||
|
coEvery { blockAidGasEstimate.getGasEstimation(any(), any()) } returns GasEstimationResult(emptyList()).right()
|
||||||
coEvery { feeRepository.calculateFee(any(), any(), any()) } returnsMany listOf(fee, fee)
|
coEvery { feeRepository.calculateFee(any(), any(), any()) } returnsMany listOf(fee, fee)
|
||||||
|
|
||||||
val result = useCase(
|
val result = useCase(
|
||||||
userWallet = userWallet,
|
userWallet = userWallet,
|
||||||
cryptoCurrency = cryptoCurrency,
|
cryptoCurrency = cryptoCurrency,
|
||||||
transactionDataList = listOf(
|
transactionDataList = listOf(deployTx, approveTx),
|
||||||
getDeployTx(),
|
|
||||||
getApproveTx(),
|
|
||||||
),
|
|
||||||
)
|
)
|
||||||
Truth.assertThat(result.isRight()).isTrue()
|
Truth.assertThat(result.isRight()).isTrue()
|
||||||
|
|
||||||
val txs = (result as Either.Right).value
|
coVerify(ordering = Ordering.ORDERED) {
|
||||||
|
blockAidGasEstimate.getGasEstimation(any(), any())
|
||||||
|
feeRepository.calculateFee(any(), any(), deployTx)
|
||||||
|
feeRepository.calculateFee(any(), any(), approveTx)
|
||||||
|
}
|
||||||
|
|
||||||
|
val txs = (result as Either.Right).value
|
||||||
val deployFee = txs.first().fee as Fee.Ethereum.EIP1559
|
val deployFee = txs.first().fee as Fee.Ethereum.EIP1559
|
||||||
val approveFee = txs.last().fee as Fee.Ethereum.EIP1559
|
val approveFee = txs.last().fee as Fee.Ethereum.EIP1559
|
||||||
Truth.assertThat(deployFee.gasLimit).isEqualTo(BigInteger.valueOf(21_000))
|
Truth.assertThat(deployFee.gasLimit).isEqualTo(BigInteger.valueOf(21_000))
|
||||||
|
|
@ -192,25 +225,137 @@ class YieldSupplyEstimateEnterFeeUseCaseTest {
|
||||||
@Test
|
@Test
|
||||||
fun `test enter transaction with wrong order uses gas limit Eip1559`() = runTest {
|
fun `test enter transaction with wrong order uses gas limit Eip1559`() = runTest {
|
||||||
val fee = TransactionFee.Single(ethEip1559Fee())
|
val fee = TransactionFee.Single(ethEip1559Fee())
|
||||||
|
val enterTx = getEnterTx()
|
||||||
|
val deployTx = getDeployTx()
|
||||||
|
|
||||||
|
coEvery { blockAidGasEstimate.getGasEstimation(any(), any()) } returns GasEstimationResult(emptyList()).right()
|
||||||
coEvery { feeRepository.calculateFee(any(), any(), any()) } returnsMany listOf(fee, fee)
|
coEvery { feeRepository.calculateFee(any(), any(), any()) } returnsMany listOf(fee, fee)
|
||||||
|
|
||||||
val result = useCase(
|
val result = useCase(
|
||||||
userWallet = userWallet,
|
userWallet = userWallet,
|
||||||
cryptoCurrency = cryptoCurrency,
|
cryptoCurrency = cryptoCurrency,
|
||||||
transactionDataList = listOf(
|
transactionDataList = listOf(enterTx, deployTx),
|
||||||
getEnterTx(),
|
|
||||||
getDeployTx(),
|
|
||||||
),
|
|
||||||
)
|
)
|
||||||
Truth.assertThat(result.isRight()).isTrue()
|
Truth.assertThat(result.isRight()).isTrue()
|
||||||
|
|
||||||
val txs = (result as Either.Right).value
|
coVerify(ordering = Ordering.ORDERED) {
|
||||||
|
blockAidGasEstimate.getGasEstimation(any(), any())
|
||||||
|
feeRepository.calculateFee(any(), any(), deployTx)
|
||||||
|
}
|
||||||
|
coVerify(inverse = true) {
|
||||||
|
feeRepository.calculateFee(any(), any(), enterTx)
|
||||||
|
}
|
||||||
|
|
||||||
|
val txs = (result as Either.Right).value
|
||||||
val deployFee = txs.first().fee as Fee.Ethereum.EIP1559
|
val deployFee = txs.first().fee as Fee.Ethereum.EIP1559
|
||||||
val enterFee = txs.last().fee as Fee.Ethereum.EIP1559
|
val enterFee = txs.last().fee as Fee.Ethereum.EIP1559
|
||||||
Truth.assertThat(deployFee.gasLimit).isEqualTo(BigInteger.valueOf(21_000))
|
Truth.assertThat(deployFee.gasLimit).isEqualTo(BigInteger.valueOf(21_000))
|
||||||
Truth.assertThat(enterFee.gasLimit).isEqualTo(BigInteger.valueOf(350_000))
|
Truth.assertThat(enterFee.gasLimit).isEqualTo(BigInteger.valueOf(500_000))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `test single transaction with fee Legacy`() = runTest {
|
||||||
|
val fee = TransactionFee.Single(ethLegacyFee())
|
||||||
|
val enterTx = getEnterTx()
|
||||||
|
|
||||||
|
coEvery { blockAidGasEstimate.getGasEstimation(any(), any()) } returns GasEstimationResult(emptyList()).right()
|
||||||
|
coEvery { feeRepository.calculateFee(any(), any(), any()) } returns fee
|
||||||
|
|
||||||
|
val result = useCase(
|
||||||
|
userWallet = userWallet,
|
||||||
|
cryptoCurrency = cryptoCurrency,
|
||||||
|
transactionDataList = listOf(enterTx),
|
||||||
|
)
|
||||||
|
Truth.assertThat(result.isRight()).isTrue()
|
||||||
|
|
||||||
|
coVerify(ordering = Ordering.ORDERED) {
|
||||||
|
feeRepository.calculateFee(any(), any(), enterTx)
|
||||||
|
}
|
||||||
|
coVerify(inverse = true) {
|
||||||
|
blockAidGasEstimate.getGasEstimation(any(), any())
|
||||||
|
}
|
||||||
|
|
||||||
|
val txs = (result as Either.Right).value
|
||||||
|
val enterFee = txs.first().fee as Fee.Ethereum.Legacy
|
||||||
|
Truth.assertThat(enterFee.gasLimit).isEqualTo(BigInteger.valueOf(21_000))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `test enter transaction uses block aid Eip1559`() = runTest {
|
||||||
|
val fee = ethEip1559Fee()
|
||||||
|
val deployTx = getDeployTx()
|
||||||
|
val approveTx = getApproveTx()
|
||||||
|
val enterTx = getEnterTx()
|
||||||
|
|
||||||
|
coEvery { blockAidGasEstimate.getGasEstimation(any(), any()) } returns GasEstimationResult(
|
||||||
|
estimatedGasList = listOf(1_000.toBigInteger(), 2_000.toBigInteger(), 3_000.toBigInteger()),
|
||||||
|
).right()
|
||||||
|
coEvery { feeRepository.calculateFee(any(), any(), any()) } returns mockk()
|
||||||
|
coEvery { feeRepository.getEthereumFeeWithoutGas(any(), any()) } returns fee
|
||||||
|
|
||||||
|
val result = useCase(
|
||||||
|
userWallet = userWallet,
|
||||||
|
cryptoCurrency = cryptoCurrency,
|
||||||
|
transactionDataList = listOf(deployTx, approveTx, enterTx),
|
||||||
|
)
|
||||||
|
Truth.assertThat(result.isRight()).isTrue()
|
||||||
|
|
||||||
|
coVerify(ordering = Ordering.ORDERED) {
|
||||||
|
blockAidGasEstimate.getGasEstimation(any(), any())
|
||||||
|
feeRepository.getEthereumFeeWithoutGas(any(), any())
|
||||||
|
}
|
||||||
|
coVerify(inverse = true) {
|
||||||
|
feeRepository.calculateFee(any(), any(), deployTx)
|
||||||
|
feeRepository.calculateFee(any(), any(), approveTx)
|
||||||
|
feeRepository.calculateFee(any(), any(), enterTx)
|
||||||
|
}
|
||||||
|
|
||||||
|
val txs = (result as Either.Right).value
|
||||||
|
val deployFee = txs.first().fee as Fee.Ethereum.EIP1559
|
||||||
|
val approveFee = txs[1].fee as Fee.Ethereum.EIP1559
|
||||||
|
val enterFee = txs.last().fee as Fee.Ethereum.EIP1559
|
||||||
|
Truth.assertThat(deployFee.gasLimit).isEqualTo(BigInteger.valueOf(1_000))
|
||||||
|
Truth.assertThat(approveFee.gasLimit).isEqualTo(BigInteger.valueOf(2_000))
|
||||||
|
Truth.assertThat(enterFee.gasLimit).isEqualTo(BigInteger.valueOf(3_000))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `test enter transaction uses block aid Legacy`() = runTest {
|
||||||
|
val fee = ethLegacyFee()
|
||||||
|
val deployTx = getDeployTx()
|
||||||
|
val approveTx = getApproveTx()
|
||||||
|
val enterTx = getEnterTx()
|
||||||
|
|
||||||
|
coEvery { blockAidGasEstimate.getGasEstimation(any(), any()) } returns GasEstimationResult(
|
||||||
|
estimatedGasList = listOf(1_000.toBigInteger(), 2_000.toBigInteger(), 3_000.toBigInteger()),
|
||||||
|
).right()
|
||||||
|
coEvery { feeRepository.calculateFee(any(), any(), any()) } returns mockk()
|
||||||
|
coEvery { feeRepository.getEthereumFeeWithoutGas(any(), any()) } returns fee
|
||||||
|
|
||||||
|
val result = useCase(
|
||||||
|
userWallet = userWallet,
|
||||||
|
cryptoCurrency = cryptoCurrency,
|
||||||
|
transactionDataList = listOf(deployTx, approveTx, enterTx),
|
||||||
|
)
|
||||||
|
Truth.assertThat(result.isRight()).isTrue()
|
||||||
|
|
||||||
|
coVerify(ordering = Ordering.ORDERED) {
|
||||||
|
blockAidGasEstimate.getGasEstimation(any(), any())
|
||||||
|
feeRepository.getEthereumFeeWithoutGas(any(), any())
|
||||||
|
}
|
||||||
|
coVerify(inverse = true) {
|
||||||
|
feeRepository.calculateFee(any(), any(), deployTx)
|
||||||
|
feeRepository.calculateFee(any(), any(), approveTx)
|
||||||
|
feeRepository.calculateFee(any(), any(), enterTx)
|
||||||
|
}
|
||||||
|
|
||||||
|
val txs = (result as Either.Right).value
|
||||||
|
val deployFee = txs.first().fee as Fee.Ethereum.Legacy
|
||||||
|
val approveFee = txs[1].fee as Fee.Ethereum.Legacy
|
||||||
|
val enterFee = txs.last().fee as Fee.Ethereum.Legacy
|
||||||
|
Truth.assertThat(deployFee.gasLimit).isEqualTo(BigInteger.valueOf(1_000))
|
||||||
|
Truth.assertThat(approveFee.gasLimit).isEqualTo(BigInteger.valueOf(2_000))
|
||||||
|
Truth.assertThat(enterFee.gasLimit).isEqualTo(BigInteger.valueOf(3_000))
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getDeployTx() = uncompiled(
|
private fun getDeployTx() = uncompiled(
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue