Updated on 2026-08-14
This commit is contained in:
parent
438f502a4c
commit
0fb88d97c9
6 changed files with 103 additions and 62 deletions
|
|
@ -129,5 +129,6 @@ interface SwapInteractor {
|
|||
amount: SwapAmount,
|
||||
swapData: SwapDataModel?,
|
||||
selectedFeeToken: CryptoCurrencyStatus?,
|
||||
isGasless: Boolean,
|
||||
): Either<GetFeeError, SwapFee>
|
||||
}
|
||||
|
|
@ -966,6 +966,7 @@ internal class SwapInteractorImpl @Inject constructor(
|
|||
amount: SwapAmount,
|
||||
swapData: SwapDataModel?,
|
||||
selectedFeeToken: CryptoCurrencyStatus?,
|
||||
isGasless: Boolean,
|
||||
): Either<GetFeeError, SwapFee> = either {
|
||||
if (amount.value.signum() == 0) {
|
||||
raise(GetFeeError.UnknownError)
|
||||
|
|
@ -982,6 +983,7 @@ internal class SwapInteractorImpl @Inject constructor(
|
|||
fromStatus = fromStatus,
|
||||
amount = amount,
|
||||
selectedFeeToken = selectedFeeToken,
|
||||
isGasless = isGasless,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -1031,12 +1033,14 @@ internal class SwapInteractorImpl @Inject constructor(
|
|||
fromStatus: SwapCurrencyStatus,
|
||||
amount: SwapAmount,
|
||||
selectedFeeToken: CryptoCurrencyStatus?,
|
||||
isGasless: Boolean,
|
||||
): Either<GetFeeError, SwapFee> {
|
||||
return cexSwapFeeCalculator.calculate(
|
||||
userWallet = fromStatus.userWallet,
|
||||
fromSwapCurrencyStatus = fromStatus,
|
||||
amount = amount.value,
|
||||
selectedFeeToken = selectedFeeToken,
|
||||
isGasless = isGasless,
|
||||
).fold(
|
||||
ifLeft = { it.left() },
|
||||
ifRight = { cexFeeResult ->
|
||||
|
|
|
|||
|
|
@ -45,40 +45,51 @@ class CexSwapFeeCalculator(
|
|||
fromSwapCurrencyStatus: SwapCurrencyStatus,
|
||||
amount: BigDecimal,
|
||||
selectedFeeToken: CryptoCurrencyStatus?,
|
||||
isGasless: Boolean,
|
||||
): Either<GetFeeError, CexFeeResult> = either {
|
||||
if (amount.signum() == 0) {
|
||||
raise(GetFeeError.UnknownError)
|
||||
}
|
||||
|
||||
val transactionFeeResult: TransactionFeeResult = when {
|
||||
selectedFeeToken == null -> {
|
||||
// Gasless path — overload 1 in SwapInteractorImpl. No gas-limit bump.
|
||||
val feeExtended = estimateFeeForGaslessTxUseCase(
|
||||
amount = amount,
|
||||
userWallet = userWallet,
|
||||
sendingTokenCurrencyStatus = fromSwapCurrencyStatus.status,
|
||||
).bind()
|
||||
TransactionFeeResult.LoadedExtended(feeExtended)
|
||||
}
|
||||
selectedFeeToken.currency is CryptoCurrency.Token -> {
|
||||
// Explicit gasless-token path — overload 1 in SwapInteractorImpl. No gas-limit bump.
|
||||
val feeExtended = estimateFeeForTokenUseCase(
|
||||
userWallet = userWallet,
|
||||
feeTokenCurrencyStatus = selectedFeeToken,
|
||||
sendingTokenCurrencyStatus = fromSwapCurrencyStatus.status,
|
||||
amount = amount,
|
||||
).bind()
|
||||
TransactionFeeResult.LoadedExtended(feeExtended)
|
||||
}
|
||||
else -> {
|
||||
// Explicit native fee path — overload 2 in SwapInteractorImpl. Apply 5% bump.
|
||||
val fee = estimateFeeUseCase(
|
||||
amount = amount,
|
||||
userWallet = userWallet,
|
||||
cryptoCurrencyStatus = fromSwapCurrencyStatus.status,
|
||||
).bind()
|
||||
TransactionFeeResult.Loaded(patchEthGasLimitForSwap(fee))
|
||||
val transactionFeeResult: TransactionFeeResult = if (isGasless) {
|
||||
when {
|
||||
selectedFeeToken == null -> {
|
||||
// Gasless path — overload 1 in SwapInteractorImpl. No gas-limit bump.
|
||||
val feeExtended = estimateFeeForGaslessTxUseCase(
|
||||
amount = amount,
|
||||
userWallet = userWallet,
|
||||
sendingTokenCurrencyStatus = fromSwapCurrencyStatus.status,
|
||||
).bind()
|
||||
TransactionFeeResult.LoadedExtended(feeExtended)
|
||||
}
|
||||
selectedFeeToken.currency is CryptoCurrency.Token -> {
|
||||
// Explicit gasless-token path — overload 1 in SwapInteractorImpl. No gas-limit bump.
|
||||
val feeExtended = estimateFeeForTokenUseCase(
|
||||
userWallet = userWallet,
|
||||
feeTokenCurrencyStatus = selectedFeeToken,
|
||||
sendingTokenCurrencyStatus = fromSwapCurrencyStatus.status,
|
||||
amount = amount,
|
||||
).bind()
|
||||
TransactionFeeResult.LoadedExtended(feeExtended)
|
||||
}
|
||||
else -> {
|
||||
// Explicit native fee path — overload 2 in SwapInteractorImpl. Apply 5% bump.
|
||||
val fee = estimateFeeUseCase(
|
||||
amount = amount,
|
||||
userWallet = userWallet,
|
||||
cryptoCurrencyStatus = fromSwapCurrencyStatus.status,
|
||||
).bind()
|
||||
TransactionFeeResult.Loaded(patchEthGasLimitForSwap(fee))
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Explicit native fee path — overload 2 in SwapInteractorImpl. Apply 5% bump.
|
||||
val fee = estimateFeeUseCase(
|
||||
amount = amount,
|
||||
userWallet = userWallet,
|
||||
cryptoCurrencyStatus = fromSwapCurrencyStatus.status,
|
||||
).bind()
|
||||
TransactionFeeResult.Loaded(patchEthGasLimitForSwap(fee))
|
||||
}
|
||||
|
||||
CexFeeResult(transactionFee = transactionFeeResult)
|
||||
|
|
|
|||
|
|
@ -95,6 +95,7 @@ internal class SwapInteractorImplLoadSwapFeeTest : SwapInteractorImplTestBase()
|
|||
amount = SwapAmount(BigDecimal.ONE, 18),
|
||||
swapData = swapData,
|
||||
selectedFeeToken = null,
|
||||
isGasless = false,
|
||||
)
|
||||
|
||||
assertThat(result.isRight()).isTrue()
|
||||
|
|
@ -138,6 +139,7 @@ internal class SwapInteractorImplLoadSwapFeeTest : SwapInteractorImplTestBase()
|
|||
amount = SwapAmount(BigDecimal.ONE, 9),
|
||||
swapData = swapData,
|
||||
selectedFeeToken = null,
|
||||
isGasless = false,
|
||||
)
|
||||
|
||||
assertThat(result.isRight()).isTrue()
|
||||
|
|
@ -173,6 +175,7 @@ internal class SwapInteractorImplLoadSwapFeeTest : SwapInteractorImplTestBase()
|
|||
amount = SwapAmount(BigDecimal.ONE, 18),
|
||||
swapData = swapData,
|
||||
selectedFeeToken = null,
|
||||
isGasless = false,
|
||||
)
|
||||
|
||||
assertThat(result.isRight()).isTrue()
|
||||
|
|
@ -193,6 +196,7 @@ internal class SwapInteractorImplLoadSwapFeeTest : SwapInteractorImplTestBase()
|
|||
amount = SwapAmount(BigDecimal.ONE, 18),
|
||||
swapData = null,
|
||||
selectedFeeToken = null,
|
||||
isGasless = false,
|
||||
)
|
||||
|
||||
assertThat(result.isLeft()).isTrue()
|
||||
|
|
@ -214,7 +218,9 @@ internal class SwapInteractorImplLoadSwapFeeTest : SwapInteractorImplTestBase()
|
|||
amount = SwapAmount(BigDecimal.ONE, 18),
|
||||
swapData = null,
|
||||
selectedFeeToken = null,
|
||||
)
|
||||
isGasless = false,
|
||||
|
||||
)
|
||||
|
||||
assertThat(result.isLeft()).isTrue()
|
||||
result.onLeft { error ->
|
||||
|
|
@ -240,8 +246,9 @@ internal class SwapInteractorImplLoadSwapFeeTest : SwapInteractorImplTestBase()
|
|||
toStatus = toStatus,
|
||||
amount = SwapAmount(BigDecimal.ONE, 18),
|
||||
swapData = swapData,
|
||||
selectedFeeToken = null,
|
||||
)
|
||||
selectedFeeToken = null, isGasless = false,
|
||||
|
||||
)
|
||||
|
||||
assertThat(result.isLeft()).isTrue()
|
||||
result.onLeft { error ->
|
||||
|
|
@ -265,7 +272,7 @@ internal class SwapInteractorImplLoadSwapFeeTest : SwapInteractorImplTestBase()
|
|||
)
|
||||
}
|
||||
coEvery {
|
||||
cexSwapFeeCalculator.calculate(any(), any(), any(), any())
|
||||
cexSwapFeeCalculator.calculate(any(), any(), any(), any(), any())
|
||||
} returns CexFeeResult(
|
||||
transactionFee = TransactionFeeResult.LoadedExtended(extendedFee),
|
||||
).right()
|
||||
|
|
@ -277,6 +284,7 @@ internal class SwapInteractorImplLoadSwapFeeTest : SwapInteractorImplTestBase()
|
|||
amount = SwapAmount(BigDecimal.ONE, 18),
|
||||
swapData = null,
|
||||
selectedFeeToken = null,
|
||||
isGasless = true,
|
||||
)
|
||||
|
||||
assertThat(result.isRight()).isTrue()
|
||||
|
|
@ -291,7 +299,9 @@ internal class SwapInteractorImplLoadSwapFeeTest : SwapInteractorImplTestBase()
|
|||
fromSwapCurrencyStatus = fromStatus,
|
||||
amount = BigDecimal.ONE,
|
||||
selectedFeeToken = null,
|
||||
)
|
||||
isGasless = true,
|
||||
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -307,7 +317,7 @@ internal class SwapInteractorImplLoadSwapFeeTest : SwapInteractorImplTestBase()
|
|||
val toStatus = buildSwapCurrencyStatus(networkRawId = ethNetwork, isCoin = true)
|
||||
val extendedFee = mockk<TransactionFeeExtended>(relaxed = true)
|
||||
coEvery {
|
||||
cexSwapFeeCalculator.calculate(any(), any(), any(), any())
|
||||
cexSwapFeeCalculator.calculate(any(), any(), any(), any(), any())
|
||||
} returns CexFeeResult(
|
||||
transactionFee = TransactionFeeResult.LoadedExtended(extendedFee),
|
||||
).right()
|
||||
|
|
@ -318,8 +328,9 @@ internal class SwapInteractorImplLoadSwapFeeTest : SwapInteractorImplTestBase()
|
|||
toStatus = toStatus,
|
||||
amount = SwapAmount(BigDecimal.ONE, 18),
|
||||
swapData = null,
|
||||
selectedFeeToken = null,
|
||||
)
|
||||
selectedFeeToken = null, isGasless = true,
|
||||
|
||||
)
|
||||
|
||||
assertThat(result.isRight()).isTrue()
|
||||
result.onRight { swapFee ->
|
||||
|
|
@ -337,7 +348,7 @@ internal class SwapInteractorImplLoadSwapFeeTest : SwapInteractorImplTestBase()
|
|||
}
|
||||
val extendedFee = mockk<TransactionFeeExtended>(relaxed = true)
|
||||
coEvery {
|
||||
cexSwapFeeCalculator.calculate(any(), any(), any(), any())
|
||||
cexSwapFeeCalculator.calculate(any(), any(), any(), any(), any())
|
||||
} returns CexFeeResult(
|
||||
transactionFee = TransactionFeeResult.LoadedExtended(extendedFee),
|
||||
).right()
|
||||
|
|
@ -348,8 +359,9 @@ internal class SwapInteractorImplLoadSwapFeeTest : SwapInteractorImplTestBase()
|
|||
toStatus = toStatus,
|
||||
amount = SwapAmount(BigDecimal.ONE, 18),
|
||||
swapData = null,
|
||||
selectedFeeToken = explicitTokenStatus,
|
||||
)
|
||||
selectedFeeToken = explicitTokenStatus, isGasless = true,
|
||||
|
||||
)
|
||||
|
||||
assertThat(result.isRight()).isTrue()
|
||||
result.onRight { swapFee ->
|
||||
|
|
@ -360,8 +372,9 @@ internal class SwapInteractorImplLoadSwapFeeTest : SwapInteractorImplTestBase()
|
|||
userWallet = fromStatus.userWallet,
|
||||
fromSwapCurrencyStatus = fromStatus,
|
||||
amount = BigDecimal.ONE,
|
||||
selectedFeeToken = explicitTokenStatus,
|
||||
)
|
||||
selectedFeeToken = explicitTokenStatus, isGasless = true,
|
||||
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -374,7 +387,7 @@ internal class SwapInteractorImplLoadSwapFeeTest : SwapInteractorImplTestBase()
|
|||
}
|
||||
val rawFee = TransactionFee.Single(normal = mockk<Fee.Common>(relaxed = true))
|
||||
coEvery {
|
||||
cexSwapFeeCalculator.calculate(any(), any(), any(), any())
|
||||
cexSwapFeeCalculator.calculate(any(), any(), any(), any(), any())
|
||||
} returns CexFeeResult(
|
||||
transactionFee = TransactionFeeResult.Loaded(rawFee),
|
||||
).right()
|
||||
|
|
@ -385,8 +398,9 @@ internal class SwapInteractorImplLoadSwapFeeTest : SwapInteractorImplTestBase()
|
|||
toStatus = toStatus,
|
||||
amount = SwapAmount(BigDecimal.ONE, 18),
|
||||
swapData = null,
|
||||
selectedFeeToken = explicitNativeStatus,
|
||||
)
|
||||
selectedFeeToken = explicitNativeStatus, isGasless = true,
|
||||
|
||||
)
|
||||
|
||||
assertThat(result.isRight()).isTrue()
|
||||
result.onRight { swapFee ->
|
||||
|
|
@ -400,7 +414,7 @@ internal class SwapInteractorImplLoadSwapFeeTest : SwapInteractorImplTestBase()
|
|||
val fromStatus = buildSwapCurrencyStatus(networkRawId = ethNetwork, isCoin = true)
|
||||
val toStatus = buildSwapCurrencyStatus(networkRawId = ethNetwork, isCoin = true)
|
||||
coEvery {
|
||||
cexSwapFeeCalculator.calculate(any(), any(), any(), any())
|
||||
cexSwapFeeCalculator.calculate(any(), any(), any(), any(), any())
|
||||
} returns GetFeeError.UnknownError.left()
|
||||
|
||||
val result = sut.loadSwapFee(
|
||||
|
|
@ -409,8 +423,9 @@ internal class SwapInteractorImplLoadSwapFeeTest : SwapInteractorImplTestBase()
|
|||
toStatus = toStatus,
|
||||
amount = SwapAmount(BigDecimal.ONE, 18),
|
||||
swapData = null,
|
||||
selectedFeeToken = null,
|
||||
)
|
||||
selectedFeeToken = null, isGasless = true,
|
||||
|
||||
)
|
||||
|
||||
assertThat(result.isLeft()).isTrue()
|
||||
result.onLeft { error ->
|
||||
|
|
@ -433,14 +448,15 @@ internal class SwapInteractorImplLoadSwapFeeTest : SwapInteractorImplTestBase()
|
|||
toStatus = toStatus,
|
||||
amount = SwapAmount(BigDecimal.ZERO, 18),
|
||||
swapData = null,
|
||||
selectedFeeToken = null,
|
||||
)
|
||||
selectedFeeToken = null, isGasless = true,
|
||||
|
||||
)
|
||||
|
||||
assertThat(result.isLeft()).isTrue()
|
||||
result.onLeft { error ->
|
||||
assertThat(error).isInstanceOf(GetFeeError.UnknownError::class.java)
|
||||
}
|
||||
coVerify(exactly = 0) { cexSwapFeeCalculator.calculate(any(), any(), any(), any()) }
|
||||
coVerify(exactly = 0) { cexSwapFeeCalculator.calculate(any(), any(), any(), any(), any()) }
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -459,7 +475,9 @@ internal class SwapInteractorImplLoadSwapFeeTest : SwapInteractorImplTestBase()
|
|||
amount = SwapAmount(BigDecimal.ZERO, 18),
|
||||
swapData = swapData,
|
||||
selectedFeeToken = null,
|
||||
)
|
||||
isGasless = false,
|
||||
|
||||
)
|
||||
|
||||
assertThat(result.isLeft()).isTrue()
|
||||
result.onLeft { error ->
|
||||
|
|
@ -501,7 +519,9 @@ internal class SwapInteractorImplLoadSwapFeeTest : SwapInteractorImplTestBase()
|
|||
amount = SwapAmount(BigDecimal.ONE, 18),
|
||||
swapData = swapData,
|
||||
selectedFeeToken = explicitTokenStatus,
|
||||
)
|
||||
isGasless = false,
|
||||
|
||||
)
|
||||
|
||||
assertThat(result.isRight()).isTrue()
|
||||
result.onRight { swapFee ->
|
||||
|
|
@ -571,8 +591,9 @@ internal class SwapInteractorImplLoadSwapFeeTest : SwapInteractorImplTestBase()
|
|||
toStatus = toStatus,
|
||||
amount = SwapAmount(BigDecimal.ONE, 18),
|
||||
swapData = swapData,
|
||||
selectedFeeToken = null,
|
||||
)
|
||||
selectedFeeToken = null, isGasless = false,
|
||||
|
||||
)
|
||||
|
||||
// When resolveNativeFeeTokenStatus returns null → Left(UnknownError)
|
||||
assertThat(result.isLeft()).isTrue()
|
||||
|
|
|
|||
|
|
@ -69,6 +69,7 @@ internal class CexSwapFeeCalculatorTest {
|
|||
fromSwapCurrencyStatus = fromStatus,
|
||||
amount = BigDecimal.ZERO,
|
||||
selectedFeeToken = null,
|
||||
isGasless = true,
|
||||
)
|
||||
|
||||
assertThat(result.isLeft()).isTrue()
|
||||
|
|
@ -97,7 +98,7 @@ internal class CexSwapFeeCalculatorTest {
|
|||
userWallet = fromStatus.userWallet,
|
||||
fromSwapCurrencyStatus = fromStatus,
|
||||
amount = BigDecimal("1.5"),
|
||||
selectedFeeToken = null,
|
||||
selectedFeeToken = null, isGasless = true,
|
||||
)
|
||||
|
||||
assertThat(result.isRight()).isTrue()
|
||||
|
|
@ -130,7 +131,7 @@ internal class CexSwapFeeCalculatorTest {
|
|||
userWallet = fromStatus.userWallet,
|
||||
fromSwapCurrencyStatus = fromStatus,
|
||||
amount = BigDecimal("1.0"),
|
||||
selectedFeeToken = null,
|
||||
selectedFeeToken = null, isGasless = true,
|
||||
)
|
||||
|
||||
assertThat(result.isLeft()).isTrue()
|
||||
|
|
@ -160,7 +161,7 @@ internal class CexSwapFeeCalculatorTest {
|
|||
userWallet = fromStatus.userWallet,
|
||||
fromSwapCurrencyStatus = fromStatus,
|
||||
amount = BigDecimal("2.0"),
|
||||
selectedFeeToken = tokenStatus,
|
||||
selectedFeeToken = tokenStatus, isGasless = true,
|
||||
)
|
||||
|
||||
assertThat(result.isRight()).isTrue()
|
||||
|
|
@ -207,7 +208,7 @@ internal class CexSwapFeeCalculatorTest {
|
|||
userWallet = fromStatus.userWallet,
|
||||
fromSwapCurrencyStatus = fromStatus,
|
||||
amount = BigDecimal("3.0"),
|
||||
selectedFeeToken = coinStatus,
|
||||
selectedFeeToken = coinStatus, isGasless = true,
|
||||
)
|
||||
|
||||
assertThat(result.isRight()).isTrue()
|
||||
|
|
@ -251,7 +252,7 @@ internal class CexSwapFeeCalculatorTest {
|
|||
userWallet = fromStatus.userWallet,
|
||||
fromSwapCurrencyStatus = fromStatus,
|
||||
amount = BigDecimal("1.0"),
|
||||
selectedFeeToken = coinStatus,
|
||||
selectedFeeToken = coinStatus, isGasless = true,
|
||||
)
|
||||
|
||||
result.onRight { cexResult ->
|
||||
|
|
@ -276,7 +277,7 @@ internal class CexSwapFeeCalculatorTest {
|
|||
userWallet = fromStatus.userWallet,
|
||||
fromSwapCurrencyStatus = fromStatus,
|
||||
amount = BigDecimal("1.0"),
|
||||
selectedFeeToken = coinStatus,
|
||||
selectedFeeToken = coinStatus, isGasless = true,
|
||||
)
|
||||
|
||||
assertThat(result.isLeft()).isTrue()
|
||||
|
|
@ -321,7 +322,7 @@ internal class CexSwapFeeCalculatorTest {
|
|||
userWallet = fromStatus.userWallet,
|
||||
fromSwapCurrencyStatus = fromStatus,
|
||||
amount = BigDecimal("1.0"),
|
||||
selectedFeeToken = coinStatus,
|
||||
selectedFeeToken = coinStatus, isGasless = true,
|
||||
)
|
||||
|
||||
result.onRight { cexResult ->
|
||||
|
|
@ -355,6 +356,7 @@ internal class CexSwapFeeCalculatorTest {
|
|||
fromSwapCurrencyStatus = fromStatus,
|
||||
amount = BigDecimal("1.0"),
|
||||
selectedFeeToken = null,
|
||||
isGasless = true,
|
||||
)
|
||||
|
||||
coVerify(exactly = 1) {
|
||||
|
|
|
|||
|
|
@ -2176,7 +2176,8 @@ internal class SwapModel @Inject constructor(
|
|||
toStatus = toSwapCurrencyStatus,
|
||||
amount = swapAmount,
|
||||
swapData = swapDataForCall,
|
||||
selectedFeeToken = dataState.feePaidCryptoCurrency,
|
||||
selectedFeeToken = null,
|
||||
isGasless = false,
|
||||
).map { swapFee ->
|
||||
when (val res = swapFee.transactionFeeResult) {
|
||||
is TransactionFeeResult.LoadedExtended -> res.fee.transactionFee
|
||||
|
|
@ -2230,6 +2231,7 @@ internal class SwapModel @Inject constructor(
|
|||
amount = swapAmount,
|
||||
swapData = swapDataForCall,
|
||||
selectedFeeToken = selectedToken,
|
||||
isGasless = true,
|
||||
).map { swapFee ->
|
||||
// The fee selector block consumes TransactionFeeExtended; build one when
|
||||
// `transactionFeeResult` is LoadedExtended, else wrap the native fee in a
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue