Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/erc20-max-full-balance.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@relayprotocol/relay-kit-ui': patch
---

MAX now uses the full balance for non-native tokens. The execution and gas buffers only apply to native tokens.
24 changes: 6 additions & 18 deletions packages/ui/src/components/common/PercentageButtons.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,7 @@ import { type FC } from 'react'
import { Button, Flex } from '../primitives/index.js'
import type { ChainVM, RelayChain } from '@relayprotocol/relay-sdk'
import type { PublicClient } from 'viem'
import {
MAX_INPUT_BUFFER_BPS,
MIN_INPUT_BUFFER_UNITS
} from '../../constants/maxAmountBuffer.js'
import { getMaxAmount } from '../../utils/maxAmount.js'
import { cn } from '../../utils/cn.js'

type PercentageButtonsProps = {
Expand Down Expand Up @@ -36,16 +33,6 @@ export const PercentageButtons: FC<PercentageButtonsProps> = ({
percentages = [20, 50],
buttonClassName: customButtonClassName
}) => {
const getExecutionBuffer = (amount: bigint) => {
if (amount <= 0n) return 0n

const bpsBuffer = (amount * MAX_INPUT_BUFFER_BPS) / 10000n
const minimumBuffer =
amount > MIN_INPUT_BUFFER_UNITS ? MIN_INPUT_BUFFER_UNITS : amount

return bpsBuffer > minimumBuffer ? bpsBuffer : minimumBuffer
}

const isMobile = variant === 'mobile'

const defaultButtonClassName = cn(
Expand All @@ -70,7 +57,6 @@ export const PercentageButtons: FC<PercentageButtonsProps> = ({
const handleMaxClick = async () => {
if (!balance || !fromChain) return

const executionBufferAmount = getExecutionBuffer(balance)
const supportsNativeGasBuffer =
fromChain.vmType === 'evm' || fromChain.vmType === 'svm'

Expand All @@ -84,9 +70,11 @@ export const PercentageButtons: FC<PercentageButtonsProps> = ({
)
}

const totalBufferAmount = executionBufferAmount + feeBufferAmount
const finalMaxAmount =
balance > totalBufferAmount ? balance - totalBufferAmount : 0n
const finalMaxAmount = getMaxAmount(
balance,
!!isFromNative,
feeBufferAmount
)
Comment thread
greptile-apps[bot] marked this conversation as resolved.

onPercentageClick(
finalMaxAmount,
Expand Down
6 changes: 5 additions & 1 deletion packages/ui/src/components/widgets/SwapWidgetRenderer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -470,7 +470,11 @@ const SwapWidgetRenderer: FC<SwapWidgetRendererProps> = ({
}
)

const isFromNative = fromToken?.address === fromChain?.currency?.address
const isFromNative = addressesEqual(
fromChain?.vmType ?? 'evm',
fromToken?.address,
fromChain?.currency?.address
)

const explicitDeposit = useExplicitDeposit(
wallet,
Expand Down
35 changes: 35 additions & 0 deletions packages/ui/src/utils/maxAmount.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import {
MAX_INPUT_BUFFER_BPS,
MIN_INPUT_BUFFER_UNITS
} from '../constants/maxAmountBuffer.js'

/**
* Headroom subtracted from a native balance on MAX to avoid exact-balance
* execution edge cases.
*/
export const getExecutionBuffer = (amount: bigint): bigint => {
if (amount <= 0n) return 0n

const bpsBuffer = (amount * MAX_INPUT_BUFFER_BPS) / 10000n
const minimumBuffer =
amount > MIN_INPUT_BUFFER_UNITS ? MIN_INPUT_BUFFER_UNITS : amount

return bpsBuffer > minimumBuffer ? bpsBuffer : minimumBuffer
}

/**
* Returns the amount to populate when MAX is clicked. Native tokens keep an
* execution buffer plus the gas fee buffer; other tokens use the full balance
* since gas is paid in the native token.
*/
export const getMaxAmount = (
balance: bigint,
isFromNative: boolean,
feeBufferAmount: bigint = 0n
): bigint => {
if (balance <= 0n) return 0n
if (!isFromNative) return balance

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 MAX change lacks regression coverage

The new full-balance branch has no regression test. Without a test for the Base USDC sale and one confirming that native-token MAX keeps gas headroom, a later change could restore the original ERC-20 bug or break the native behavior without being caught. Greptile automatically discovered a related ticket stating that the Base USDC case should have a regression test, which informed this comment.

Source Used: Linear — MAX leaves gas reserve for ERC-20 sales

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!


const totalBufferAmount = getExecutionBuffer(balance) + feeBufferAmount
return balance > totalBufferAmount ? balance - totalBufferAmount : 0n
}
Loading