Skip to content

Commit

Permalink
PWN-838 - fix min SOL amount again
Browse files Browse the repository at this point in the history
  • Loading branch information
gslevinkov committed Jan 28, 2024
1 parent e1d54be commit 721b5b5
Show file tree
Hide file tree
Showing 7 changed files with 207 additions and 65 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -295,7 +295,7 @@ class SendFeeRelayerManager(
TokenProgram.AccountInfoData.ACCOUNT_INFO_DATA_LENGTH
)

Timber.i("Requesting minRentExemption for token_program: $minRentExemption")
Timber.i("Requesting minRentExemption for spl_token_program: $minRentExemption")

var transactionFee = BigInteger.ZERO

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,11 @@ class CalculationMode(

fun getCurrencyMode(): CurrencyMode = currencyMode

fun getDebugInfo(): String = buildString {
val remainingBalance = token.totalInLamports - getCurrentAmountLamports()
append("Remaining balance: $remainingBalance")
}

fun isMaxButtonVisible(minRentExemption: BigInteger): Boolean {
return if (token.isSOL) {
val maxAllowedAmount = token.totalInLamports - minRentExemption
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ class NewSendButtonState(
private val resources: Resources
) {

private val minSolValidator = NewSendButtonStateMinSolValidator(
private val minSolValidator = SendButtonStateMinSolValidator(
tokenToSend = tokenToSend,
minRentExemption = minRentExemption,
recipient = recipient
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
package org.p2p.wallet.send.model

import timber.log.Timber
import java.math.BigInteger
import org.p2p.core.token.Token
import org.p2p.core.utils.isZero

sealed interface SendSolValidation {
object AmountIsValid : SendSolValidation
object EmptyRecipientMinAmountInvalid : SendSolValidation
object SenderNotZeroed : SendSolValidation
object SenderNoRentExemptAmountLeft : SendSolValidation
}

/**
* One place to keep all the logic regarding min SOL validation
*/
class SendButtonStateMinSolValidator(
private val tokenToSend: Token.Active,
private val minRentExemption: BigInteger,
private val recipient: SearchResult
) {

/**
* This case is only for sending SOL
*
* 1. The recipient should receive at least [minRentExemption] SOL balance if the recipient's current balance is 0
* 2. The recipient should have at least [minRentExemption] after the transaction
*
* 1. The sender is allowed to sent exactly the whole balance.
* 2. It's allowed for the sender to have a SOL balance 0 or at least [minRentExemption]
* */
fun validateAmount(inputAmount: BigInteger): SendSolValidation {
if (!tokenToSend.isSOL) return SendSolValidation.AmountIsValid

val isRecipientEmpty = recipient is SearchResult.AddressFound && recipient.isEmptyBalance
val balanceRemaining = tokenToSend.totalInLamports - inputAmount
val isSendingAllSol = balanceRemaining.isZero()
val isRentExemptionRemaining = balanceRemaining >= minRentExemption
val sendingMoreThanMinRent = inputAmount >= minRentExemption

Timber.i(
buildString {
appendLine("$minRentExemption")
appendLine("-----")
appendLine("isRecipientEmpty = $isRecipientEmpty")
appendLine("balanceRemaining = $balanceRemaining")
appendLine("isSendingAllSol = $isSendingAllSol")
appendLine("isRentExemptionRemaining = $isRentExemptionRemaining")
appendLine("sendingMoreThanMinRent = $sendingMoreThanMinRent")
}
)

val isSolAmountCanBeSent = isSendingAllSol || isRentExemptionRemaining

if (isRecipientEmpty) {
if (!sendingMoreThanMinRent) {
return SendSolValidation.EmptyRecipientMinAmountInvalid
}
if (isSendingAllSol) {
return SendSolValidation.AmountIsValid
}

if (!isRentExemptionRemaining && !isSendingAllSol) {
return SendSolValidation.SenderNoRentExemptAmountLeft
}
if (!isSendingAllSol && !isRentExemptionRemaining) {
return SendSolValidation.SenderNoRentExemptAmountLeft
}
}

if (isSendingAllSol) {
return SendSolValidation.AmountIsValid
}
// if we are not sending all SOL but leave some dust that less than rent
if (!isRentExemptionRemaining) {
return SendSolValidation.SenderNoRentExemptAmountLeft
}
return SendSolValidation.AmountIsValid
}
}
3 changes: 3 additions & 0 deletions app/src/main/java/org/p2p/wallet/send/ui/NewSendPresenter.kt
Original file line number Diff line number Diff line change
Expand Up @@ -637,6 +637,9 @@ class NewSendPresenter(
private fun buildDebugInfo(solanaFee: SendSolanaFee?) {
launch {
val debugInfo = sendFeeRelayerManager.buildDebugInfo(solanaFee)
.plus("\n")
.plus(calculationMode.getDebugInfo())

view?.showDebugInfo(debugInfo)
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
package org.p2p.wallet.send.model

import assertk.assertions.isInstanceOf
import io.mockk.every
import io.mockk.mockk
import org.junit.Test
import java.math.BigInteger
import org.p2p.core.token.Token
import org.p2p.core.utils.fromLamports
import org.p2p.wallet.utils.assertThat

class SendButtonStateMinSolValidatorTest {

private lateinit var solValidator: SendButtonStateMinSolValidator

private val emptyRecipient = mockk<SearchResult.AddressFound> {
every { isEmptyBalance }.returns(true)
}

private val nonEmptyRecipient = mockk<SearchResult.AddressFound> {
every { isEmptyBalance }.returns(false)
}

@Test
fun `GIVEN empty recipient THEN validator cases work`() {
// entering amount that less than minRentExemption
val minRentExemption = BigInteger("890000")
// "0.015000000"
val solAmount = BigInteger.valueOf(15_000_000)

solValidator = SendButtonStateMinSolValidator(
tokenToSend = createCustomSol(solAmount),
minRentExemption = minRentExemption,
recipient = emptyRecipient
)

// enter less than min rent
var input = BigInteger.valueOf(880_000)
var result = solValidator.validateAmount(input)
result.assertThat()
.isInstanceOf(SendSolValidation.EmptyRecipientMinAmountInvalid::class)

// enter more == min rent
input = "890000".toBigInteger()
result = solValidator.validateAmount(input)
result.assertThat()
.isInstanceOf(SendSolValidation.AmountIsValid::class)

// enter more than min rent
input = "890001".toBigInteger()
result = solValidator.validateAmount(input)
result.assertThat()
.isInstanceOf(SendSolValidation.AmountIsValid::class)

// enter full amount
input = solAmount
result = solValidator.validateAmount(input)
result.assertThat()
.isInstanceOf(SendSolValidation.AmountIsValid::class)

// enter amount but mint rent is not left
input = BigInteger.valueOf(14_999_000)
result = solValidator.validateAmount(input)
result.assertThat()
.isInstanceOf(SendSolValidation.SenderNoRentExemptAmountLeft::class)
}

@Test
fun `GIVEN non-empty recipient THEN validator cases work`() {
val minRentExemption = BigInteger.valueOf(890_000)
// "0.015000000"
val solAmount = BigInteger.valueOf(15_000_000)

solValidator = SendButtonStateMinSolValidator(
tokenToSend = createCustomSol(solAmount),
minRentExemption = minRentExemption,
recipient = nonEmptyRecipient
)

// enter less than min rent
var input = BigInteger.valueOf(880_000)
var result = solValidator.validateAmount(input)
result.assertThat()
.isInstanceOf(SendSolValidation.AmountIsValid::class)

// enter equals min rent
input = "890000".toBigInteger()
result = solValidator.validateAmount(input)
result.assertThat()
.isInstanceOf(SendSolValidation.AmountIsValid::class)

// enter more than min rent
input = "890001".toBigInteger()
result = solValidator.validateAmount(input)
result.assertThat()
.isInstanceOf(SendSolValidation.AmountIsValid::class)

// enter full amount
input = solAmount
result = solValidator.validateAmount(input)
result.assertThat()
.isInstanceOf(SendSolValidation.AmountIsValid::class)

// enter amount but mint rent is not left
input = BigInteger.valueOf(14_999_000)
result = solValidator.validateAmount(input)
result.assertThat()
.isInstanceOf(SendSolValidation.SenderNoRentExemptAmountLeft::class)
}

private fun createCustomSol(solAmount: BigInteger): Token.Active = mockk {
every { isSOL }.returns(true)
every { totalInLamports }.returns(solAmount)
every { total }.returns(solAmount.fromLamports(9))
}
}

0 comments on commit 721b5b5

Please sign in to comment.