From 1ce06627c85b165548d05e0e4f4016251f5eaff6 Mon Sep 17 00:00:00 2001 From: Tangem Date: Tue, 13 Jan 2026 15:13:18 +0300 Subject: [PATCH] Updated on 2026-08-14 --- .../datasource/api/gasless/GaslessTxServiceApi.kt | 8 ++++---- .../api/gasless/models/GaslessFeeRecipient.kt | 9 +++++++++ .../java/com/tangem/datasource/di/NetworkModule.kt | 7 +++++++ .../DefaultGaslessTransactionRepository.kt | 11 +++++++++-- .../MockedGaslessTransactionRepository.kt | 2 +- .../transaction/GaslessTransactionRepository.kt | 2 +- .../usecase/gasless/TokenFeeCalculatorTest.kt | 14 +++++++------- 7 files changed, 38 insertions(+), 15 deletions(-) create mode 100644 core/datasource/src/main/java/com/tangem/datasource/api/gasless/models/GaslessFeeRecipient.kt diff --git a/core/datasource/src/main/java/com/tangem/datasource/api/gasless/GaslessTxServiceApi.kt b/core/datasource/src/main/java/com/tangem/datasource/api/gasless/GaslessTxServiceApi.kt index 72b01f4139..514dc9f20e 100644 --- a/core/datasource/src/main/java/com/tangem/datasource/api/gasless/GaslessTxServiceApi.kt +++ b/core/datasource/src/main/java/com/tangem/datasource/api/gasless/GaslessTxServiceApi.kt @@ -1,10 +1,7 @@ package com.tangem.datasource.api.gasless import com.tangem.datasource.api.common.response.ApiResponse -import com.tangem.datasource.api.gasless.models.GaslessServiceResponse -import com.tangem.datasource.api.gasless.models.GaslessSignedTransactionResultDTO -import com.tangem.datasource.api.gasless.models.GaslessSupportedTokens -import com.tangem.datasource.api.gasless.models.GaslessTransactionRequest +import com.tangem.datasource.api.gasless.models.* import retrofit2.http.Body import retrofit2.http.GET import retrofit2.http.POST @@ -18,4 +15,7 @@ interface GaslessTxServiceApi { suspend fun signGaslessTransaction( @Body transaction: GaslessTransactionRequest, ): ApiResponse> + + @GET("api/v1/config/fee-recipient") + suspend fun getFeeRecipient(): ApiResponse> } \ No newline at end of file diff --git a/core/datasource/src/main/java/com/tangem/datasource/api/gasless/models/GaslessFeeRecipient.kt b/core/datasource/src/main/java/com/tangem/datasource/api/gasless/models/GaslessFeeRecipient.kt new file mode 100644 index 0000000000..f66c161faf --- /dev/null +++ b/core/datasource/src/main/java/com/tangem/datasource/api/gasless/models/GaslessFeeRecipient.kt @@ -0,0 +1,9 @@ +package com.tangem.datasource.api.gasless.models + +import com.squareup.moshi.Json +import com.squareup.moshi.JsonClass + +@JsonClass(generateAdapter = true) +data class GaslessFeeRecipient( + @Json(name = "feeRecipientAddress") val address: String, +) \ No newline at end of file diff --git a/core/datasource/src/main/java/com/tangem/datasource/di/NetworkModule.kt b/core/datasource/src/main/java/com/tangem/datasource/di/NetworkModule.kt index 8624353f33..25833cc27c 100644 --- a/core/datasource/src/main/java/com/tangem/datasource/di/NetworkModule.kt +++ b/core/datasource/src/main/java/com/tangem/datasource/di/NetworkModule.kt @@ -37,6 +37,7 @@ import javax.inject.Singleton internal object NetworkModule { private const val TANGEM_TECH_MARKETS_SERVICE_TIMEOUT_SECONDS = 60L + private const val TANGEM_GASLESS_SERVICE_TIMEOUT_SECONDS = 60L private const val STAKE_KIT_API_TIMEOUT_SECONDS = 60L private const val P2P_ETH_POOL_API_TIMEOUT_SECONDS = 60L @@ -196,6 +197,12 @@ internal object NetworkModule { return retrofitApiBuilder.build( apiConfigId = ApiConfig.ID.GaslessTxService, applyTimeoutAnnotations = false, + timeouts = Timeouts( + callTimeoutSeconds = TANGEM_GASLESS_SERVICE_TIMEOUT_SECONDS, + connectTimeoutSeconds = TANGEM_GASLESS_SERVICE_TIMEOUT_SECONDS, + readTimeoutSeconds = TANGEM_GASLESS_SERVICE_TIMEOUT_SECONDS, + writeTimeoutSeconds = TANGEM_GASLESS_SERVICE_TIMEOUT_SECONDS, + ), ) } } \ No newline at end of file diff --git a/data/transaction/src/main/java/com/tangem/data/transaction/DefaultGaslessTransactionRepository.kt b/data/transaction/src/main/java/com/tangem/data/transaction/DefaultGaslessTransactionRepository.kt index f821a49766..9587374de8 100644 --- a/data/transaction/src/main/java/com/tangem/data/transaction/DefaultGaslessTransactionRepository.kt +++ b/data/transaction/src/main/java/com/tangem/data/transaction/DefaultGaslessTransactionRepository.kt @@ -69,8 +69,15 @@ class DefaultGaslessTransactionRepository( } } - override fun getTokenFeeReceiverAddress(): String { - return TOKEN_RECEIVER_ADDRESS + override suspend fun getTokenFeeReceiverAddress(): String { + return withContext(coroutineDispatcherProvider.io) { + val response = gaslessTxServiceApi.getFeeRecipient().getOrThrow() + if (response.isSuccess) { + response.result.address + } else { + error("Gasless service returned unsuccessful response") + } + } } override suspend fun signGaslessTransaction( diff --git a/data/transaction/src/main/java/com/tangem/data/transaction/MockedGaslessTransactionRepository.kt b/data/transaction/src/main/java/com/tangem/data/transaction/MockedGaslessTransactionRepository.kt index ae642382e9..dd3e5600f5 100644 --- a/data/transaction/src/main/java/com/tangem/data/transaction/MockedGaslessTransactionRepository.kt +++ b/data/transaction/src/main/java/com/tangem/data/transaction/MockedGaslessTransactionRepository.kt @@ -34,7 +34,7 @@ class MockedGaslessTransactionRepository( return networkBlockchain.getChainId() ?: error("ChainId not found for blockchain ${networkBlockchain.name}") } - override fun getTokenFeeReceiverAddress(): String { + override suspend fun getTokenFeeReceiverAddress(): String { return TOKEN_RECEIVER_ADDRESS } diff --git a/domain/transaction/src/main/java/com/tangem/domain/transaction/GaslessTransactionRepository.kt b/domain/transaction/src/main/java/com/tangem/domain/transaction/GaslessTransactionRepository.kt index bbef57a432..29d57171a6 100644 --- a/domain/transaction/src/main/java/com/tangem/domain/transaction/GaslessTransactionRepository.kt +++ b/domain/transaction/src/main/java/com/tangem/domain/transaction/GaslessTransactionRepository.kt @@ -11,7 +11,7 @@ interface GaslessTransactionRepository { suspend fun getSupportedTokens(network: Network): Set - fun getTokenFeeReceiverAddress(): String + suspend fun getTokenFeeReceiverAddress(): String /** * Sends gasless transaction to the gasless service for signing and returns the signed result. diff --git a/domain/transaction/src/test/java/com/tangem/domain/transaction/usecase/gasless/TokenFeeCalculatorTest.kt b/domain/transaction/src/test/java/com/tangem/domain/transaction/usecase/gasless/TokenFeeCalculatorTest.kt index 2cd02cdee6..8e58e9f4eb 100644 --- a/domain/transaction/src/test/java/com/tangem/domain/transaction/usecase/gasless/TokenFeeCalculatorTest.kt +++ b/domain/transaction/src/test/java/com/tangem/domain/transaction/usecase/gasless/TokenFeeCalculatorTest.kt @@ -198,7 +198,7 @@ class TokenFeeCalculatorTest { val initialFee = createMockEIP1559Fee() coEvery { mockWalletManager.getGasLimit(any(), any(), any()) } returns Result.Success(BigInteger("60000")) - every { gaslessTransactionRepository.getTokenFeeReceiverAddress() } returns "0xFeeReceiver" + coEvery { gaslessTransactionRepository.getTokenFeeReceiverAddress() } returns "0xFeeReceiver" every { gaslessTransactionRepository.getBaseGasForTransaction() } returns BigInteger("21000") // When @@ -231,7 +231,7 @@ class TokenFeeCalculatorTest { val initialFee = createMockEIP1559Fee() coEvery { mockWalletManager.getGasLimit(any(), any(), any()) } returns Result.Success(BigInteger("60000")) - every { gaslessTransactionRepository.getTokenFeeReceiverAddress() } returns "0xFeeReceiver" + coEvery { gaslessTransactionRepository.getTokenFeeReceiverAddress() } returns "0xFeeReceiver" every { gaslessTransactionRepository.getBaseGasForTransaction() } returns BigInteger("21000") // When @@ -252,7 +252,7 @@ class TokenFeeCalculatorTest { @Test fun `calculateTokenFee should return error when fiatRate is null`() = runTest { coEvery { mockWalletManager.getGasLimit(any(), any(), any()) } returns Result.Success(BigInteger("60000")) - every { gaslessTransactionRepository.getTokenFeeReceiverAddress() } returns "0xFeeReceiver" + coEvery { gaslessTransactionRepository.getTokenFeeReceiverAddress() } returns "0xFeeReceiver" every { gaslessTransactionRepository.getBaseGasForTransaction() } returns BigInteger("21000") // Given val tokenStatus = createMockTokenStatus( @@ -285,7 +285,7 @@ class TokenFeeCalculatorTest { val failure = Result.Failure(BlockchainSdkError.NPError("Gas limit fetch failed")) coEvery { mockWalletManager.getGasLimit(any(), any(), any()) } returns failure - every { gaslessTransactionRepository.getTokenFeeReceiverAddress() } returns "0xFeeReceiver" + coEvery { gaslessTransactionRepository.getTokenFeeReceiverAddress() } returns "0xFeeReceiver" // When val result = tokenFeeCalculator.calculateTokenFee( @@ -314,7 +314,7 @@ class TokenFeeCalculatorTest { ) coEvery { mockWalletManager.getGasLimit(any(), any(), any()) } returns Result.Success(BigInteger("60000")) - every { gaslessTransactionRepository.getTokenFeeReceiverAddress() } returns "0xFeeReceiver" + coEvery { gaslessTransactionRepository.getTokenFeeReceiverAddress() } returns "0xFeeReceiver" every { gaslessTransactionRepository.getBaseGasForTransaction() } returns BigInteger("21000") // When @@ -332,7 +332,7 @@ class TokenFeeCalculatorTest { @Test fun `calculateTokenFee should reject TokenCurrency as initial fee`() = runTest { coEvery { mockWalletManager.getGasLimit(any(), any(), any()) } returns Result.Success(BigInteger("60000")) - every { gaslessTransactionRepository.getTokenFeeReceiverAddress() } returns "0xFeeReceiver" + coEvery { gaslessTransactionRepository.getTokenFeeReceiverAddress() } returns "0xFeeReceiver" every { gaslessTransactionRepository.getBaseGasForTransaction() } returns BigInteger("21000") // Given @@ -389,7 +389,7 @@ class TokenFeeCalculatorTest { val initialFee = createMockEIP1559Fee() coEvery { mockWalletManager.getGasLimit(any(), any(), any()) } returns Result.Success(BigInteger("60000")) - every { gaslessTransactionRepository.getTokenFeeReceiverAddress() } returns "0xFeeReceiver" + coEvery { gaslessTransactionRepository.getTokenFeeReceiverAddress() } returns "0xFeeReceiver" every { gaslessTransactionRepository.getBaseGasForTransaction() } returns BigInteger("21000") // When