Skip to content

Commit

Permalink
CP-8641: show insufficient balance error in Send when maxAmount is 0 (#…
Browse files Browse the repository at this point in the history
  • Loading branch information
onghwan authored and atn4z7 committed May 17, 2024
1 parent 759c793 commit c10dbb0
Show file tree
Hide file tree
Showing 5 changed files with 241 additions and 203 deletions.
19 changes: 16 additions & 3 deletions packages/core-mobile/app/contexts/SendTokenContext.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,10 @@ import React, {
useState
} from 'react'
import { InteractionManager } from 'react-native'
import { TokenWithBalance } from 'store/balance'
import {
selectNativeTokenBalanceForNetworkAndAccount,
TokenWithBalance
} from 'store/balance'
import { useSelector } from 'react-redux'
import { selectActiveAccount } from 'store/account'
import sendService from 'services/send/SendService'
Expand All @@ -24,6 +27,7 @@ import { NetworkTokenUnit, Amount } from 'types'
import { useNetworks } from 'hooks/networks/useNetworks'
import { useNetworkFee } from 'hooks/useNetworkFee'
import { useInAppRequest } from 'hooks/useInAppRequest'
import { RootState } from 'store'

export type SendStatus = 'Idle' | 'Sending' | 'Success' | 'Fail'

Expand Down Expand Up @@ -57,6 +61,13 @@ export const SendTokenContextProvider = ({
const { activeNetwork } = useNetworks()
const activeAccount = useSelector(selectActiveAccount)
const selectedCurrency = useSelector(selectSelectedCurrency)
const nativeTokenBalance = useSelector((state: RootState) =>
selectNativeTokenBalanceForNetworkAndAccount(
state,
activeNetwork.chainId,
activeAccount?.index
)
)

const [sendToken, setSendToken] = useState<TokenWithBalance | undefined>()
const [maxAmount, setMaxAmount] = useState<Amount>(ZERO_AMOUNT)
Expand Down Expand Up @@ -93,7 +104,8 @@ export const SendTokenContextProvider = ({
sendAmount,
sendToAddress,
sendToken,
defaultMaxFeePerGas
defaultMaxFeePerGas,
nativeTokenBalance
])

const setSendTokenAndResetAmount = useCallback(
Expand Down Expand Up @@ -189,7 +201,8 @@ export const SendTokenContextProvider = ({
sendState,
activeNetwork,
activeAccount,
selectedCurrency
selectedCurrency,
nativeTokenBalance
)
.then(state => {
setGasLimit(state.gasLimit ?? 0)
Expand Down
142 changes: 76 additions & 66 deletions packages/core-mobile/app/services/send/SendServiceAVM.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,75 +24,85 @@ export class SendServiceAVM {
params: ValidateStateAndCalculateFeesParams
): Promise<SendState> {
const { sendState, nativeTokenBalance, sentryTrx } = params
return SentryWrapper.createSpanFor(sentryTrx)
.setContext('svc.send.avm.validate_and_calc_fees')
.executeAsync(async () => {
const { amount, address, defaultMaxFeePerGas, token } = sendState

// Set canSubmit to false if token is not set
if (!token) return SendServiceAVM.getErrorState(sendState, '')

const gasLimit = GAS_LIMIT_FOR_XP_CHAIN
const sendFee = defaultMaxFeePerGas
? new BN(gasLimit).mul(new BN(defaultMaxFeePerGas.toString()))
: undefined
let maxAmount = token.balance.sub(sendFee || new BN(0))
maxAmount = BN.max(maxAmount, new BN(0))

const newState: SendState = {
...sendState,
canSubmit: true,
error: undefined,
gasLimit,
maxAmount,
sendFee
}

if (!address)
return SendServiceAVM.getErrorState(
newState,
SendErrorMessage.ADDRESS_REQUIRED
return (
SentryWrapper.createSpanFor(sentryTrx)
.setContext('svc.send.avm.validate_and_calc_fees')
// eslint-disable-next-line sonarjs/cognitive-complexity
.executeAsync(async () => {
const { amount, address, defaultMaxFeePerGas, token } = sendState

// Set canSubmit to false if token is not set
if (!token) return SendServiceAVM.getErrorState(sendState, '')

const gasLimit = GAS_LIMIT_FOR_XP_CHAIN
const sendFee = defaultMaxFeePerGas
? new BN(gasLimit).mul(new BN(defaultMaxFeePerGas.toString()))
: undefined
let maxAmount = token.balance.sub(sendFee || new BN(0))
maxAmount = BN.max(maxAmount, new BN(0))

const newState: SendState = {
...sendState,
canSubmit: true,
error: undefined,
gasLimit,
maxAmount,
sendFee
}

if (!address)
return SendServiceAVM.getErrorState(
newState,
SendErrorMessage.ADDRESS_REQUIRED
)

if (
!Avalanche.isBech32Address(address, false) &&
!Avalanche.isBech32Address(address, true)
)

if (
!Avalanche.isBech32Address(address, false) &&
!Avalanche.isBech32Address(address, true)
)
return SendServiceAVM.getErrorState(
newState,
SendErrorMessage.INVALID_ADDRESS
)

if (!defaultMaxFeePerGas || defaultMaxFeePerGas === 0n)
return SendServiceAVM.getErrorState(
newState,
SendErrorMessage.INVALID_NETWORK_FEE
return SendServiceAVM.getErrorState(
newState,
SendErrorMessage.INVALID_ADDRESS
)

if (!defaultMaxFeePerGas || defaultMaxFeePerGas === 0n)
return SendServiceAVM.getErrorState(
newState,
SendErrorMessage.INVALID_NETWORK_FEE
)

if (maxAmount.isZero())
return SendServiceAVM.getErrorState(
newState,
SendErrorMessage.INSUFFICIENT_BALANCE
)

if (!amount || amount.isZero())
return SendServiceAVM.getErrorState(
newState,
SendErrorMessage.AMOUNT_REQUIRED
)

if (amount?.gt(maxAmount))
return SendServiceAVM.getErrorState(
newState,
SendErrorMessage.INSUFFICIENT_BALANCE
)

if (
sendFee &&
((token.type !== TokenType.NATIVE &&
nativeTokenBalance?.lt(sendFee)) ||
(token.type === TokenType.NATIVE && token.balance.lt(sendFee)))
)
return SendServiceAVM.getErrorState(
newState,
SendErrorMessage.INSUFFICIENT_BALANCE_FOR_FEE
)

if (!amount || amount.isZero())
return SendServiceAVM.getErrorState(
newState,
SendErrorMessage.AMOUNT_REQUIRED
)

if (amount?.gt(maxAmount))
return SendServiceAVM.getErrorState(
newState,
SendErrorMessage.INSUFFICIENT_BALANCE
)

if (
token.type !== TokenType.NATIVE &&
sendFee &&
nativeTokenBalance?.lt(sendFee)
)
return SendServiceAVM.getErrorState(
newState,
SendErrorMessage.INSUFFICIENT_BALANCE_FOR_FEE
)

return newState
})
return newState
})
)
}

async getTransactionRequest({
Expand Down
136 changes: 70 additions & 66 deletions packages/core-mobile/app/services/send/SendServiceEVM.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,86 +36,90 @@ export class SendServiceEVM implements SendServiceHelper {
params: ValidateStateAndCalculateFeesParams
): Promise<SendState> {
const { sendState, nativeTokenBalance, sentryTrx } = params
return SentryWrapper.createSpanFor(sentryTrx)
.setContext('svc.send.evm.validate_and_calc_fees')
.executeAsync(async () => {
const { amount, address, defaultMaxFeePerGas, token } = sendState
return (
SentryWrapper.createSpanFor(sentryTrx)
.setContext('svc.send.evm.validate_and_calc_fees')
// eslint-disable-next-line sonarjs/cognitive-complexity
.executeAsync(async () => {
const { amount, address, defaultMaxFeePerGas, token } = sendState

// Set canSubmit to false if token is not set
if (!token) return SendServiceEVM.getErrorState(sendState, '')
// Set canSubmit to false if token is not set
if (!token) return SendServiceEVM.getErrorState(sendState, '')

const gasLimit = await this.getGasLimit(sendState)
const sendFee = defaultMaxFeePerGas
? new BN(gasLimit).mul(new BN(defaultMaxFeePerGas.toString()))
: undefined
let maxAmount =
token.type === TokenType.NATIVE
? token.balance.sub(sendFee || new BN(0))
: token.balance
const gasLimit = await this.getGasLimit(sendState)
const sendFee = defaultMaxFeePerGas
? new BN(gasLimit).mul(new BN(defaultMaxFeePerGas.toString()))
: undefined
let maxAmount =
token.type === TokenType.NATIVE
? token.balance.sub(sendFee || new BN(0))
: token.balance

maxAmount = BN.max(maxAmount, new BN(0))
maxAmount = BN.max(maxAmount, new BN(0))

const newState: SendState = {
...sendState,
canSubmit: true,
error: undefined,
gasLimit,
maxAmount,
sendFee
}
const newState: SendState = {
...sendState,
canSubmit: true,
error: undefined,
gasLimit,
maxAmount,
sendFee
}

if (!address)
return SendServiceEVM.getErrorState(
newState,
SendErrorMessage.ADDRESS_REQUIRED
)
if (!address)
return SendServiceEVM.getErrorState(
newState,
SendErrorMessage.ADDRESS_REQUIRED
)

if (!isAddress(address))
return SendServiceEVM.getErrorState(
newState,
SendErrorMessage.INVALID_ADDRESS
)
if (!isAddress(address))
return SendServiceEVM.getErrorState(
newState,
SendErrorMessage.INVALID_ADDRESS
)

if (gasLimit === 0)
return SendServiceEVM.getErrorState(
newState,
SendErrorMessage.INVALID_GAS_LIMIT
)
if (gasLimit === 0)
return SendServiceEVM.getErrorState(
newState,
SendErrorMessage.INVALID_GAS_LIMIT
)

if (!defaultMaxFeePerGas || defaultMaxFeePerGas === 0n)
return SendServiceEVM.getErrorState(
newState,
SendErrorMessage.INVALID_NETWORK_FEE
)
if (!defaultMaxFeePerGas || defaultMaxFeePerGas === 0n)
return SendServiceEVM.getErrorState(
newState,
SendErrorMessage.INVALID_NETWORK_FEE
)

if (
token.type !== TokenType.ERC721 &&
token.type !== TokenType.ERC1155 &&
(!amount || amount.isZero())
)
return SendServiceEVM.getErrorState(
newState,
SendErrorMessage.AMOUNT_REQUIRED
if (
token.type !== TokenType.ERC721 &&
token.type !== TokenType.ERC1155 &&
(!amount || amount.isZero())
)
return SendServiceEVM.getErrorState(
newState,
SendErrorMessage.AMOUNT_REQUIRED
)

if (amount?.gt(maxAmount))
return SendServiceEVM.getErrorState(
newState,
SendErrorMessage.INSUFFICIENT_BALANCE
)
if (amount?.gt(maxAmount))
return SendServiceEVM.getErrorState(
newState,
SendErrorMessage.INSUFFICIENT_BALANCE
)

if (
token.type !== TokenType.NATIVE &&
sendFee &&
nativeTokenBalance?.lt(sendFee)
)
return SendServiceEVM.getErrorState(
newState,
SendErrorMessage.INSUFFICIENT_BALANCE_FOR_FEE
if (
sendFee &&
((token.type !== TokenType.NATIVE &&
nativeTokenBalance?.lt(sendFee)) ||
(token.type === TokenType.NATIVE && token.balance.lt(sendFee)))
)
return SendServiceEVM.getErrorState(
newState,
SendErrorMessage.INSUFFICIENT_BALANCE_FOR_FEE
)

return newState
})
return newState
})
)
}

async getTransactionRequest(
Expand Down
Loading

0 comments on commit c10dbb0

Please sign in to comment.