# Bridge over-balance input sends the full balance while the UI shows a different amount

**URL:** https://ghostforum.org/t/bridge-over-balance-input-sends-the-full-balance-while-the-ui-shows-a-different-amount/84
**Category:** Submitted
**Tags:** ghost-dao-interface
**Created:** [October 2, 2026, 6:34am UTC](https://ghostforum.org/t/bridge-over-balance-input-sends-the-full-balance-while-the-ui-shows-a-different-amount/84 "2026-10-02T06:34:58Z")
**Posts on this page:** 3
**Page:** 1

<div class="post-metadata">

### Author: ![callmefull](https://ghostforum.org/letter_avatar_proxy/v4/letter/c/cdc98d/32.png) [@callmefull](https://ghostforum.org/u/callmefull)
#### Post date: [October 2, 2026, 6:34am UTC](https://ghostforum.org/t/bridge-over-balance-input-sends-the-full-balance-while-the-ui-shows-a-different-amount/84/1 "2026-10-02T06:34:58Z")

</div>

**Severity:** Medium  
**Component:** `ghost-dao-interface / src/containers/Bridge/BridgeCard.jsx`  
**Commit tested:** `a911859`

## **Summary**

When the bridge amount is larger than the user’s GHST balance, `BridgeCard.jsx` clamps the value to the user’s full balance.

The problem is that the UI does not use that same value consistently.

The bridge transaction uses the clamped `preparedAmount`, while the `"You will get"` amount is calculated from the amount typed by the user.

For example, with a balance of 10 GHST and an input of 100 GHST:

```auto
Input: 100 GHST
Button disabled: false
"You will get": 99 GHST
Transaction amount: 10 GHST

```

The amount shown by the UI therefore does not match the amount passed to the bridge transaction.

## **Root cause**

In `src/containers/Bridge/BridgeCard.jsx`:

```auto
const preparedAmount = useMemo(() => {
  try {
    const result = BigInt(parseFloat(amount) * Math.pow(10, 18));

    if (result > ghstBalance._value) {
      return ghstBalance._value;
    }

    return result;
  } catch {
    return 0n;
  }
}, [amount]);

```

An amount greater than the user’s balance is replaced with the entire balance.

That value is then passed to the bridge call:

```auto
const txHash = await ghost(
  chainId,
  address,
  convertedReceiver,
  preparedAmount
);

```

However, amountAfterFee is calculated from the typed input rather than the clamped amount.

The balance check also compares the balance against preparedAmount. After the clamp, the value is equal to the balance, so an over-balance input is not rejected by that check.

## **Proof of Concept**

I reproduced the component logic in a Node.js driver using the same BigInt wei representation. The wallet broadcast was captured instead of being sent.

```auto
Wallet balance: 10 GHST

Test 1
Typed amount: 100 GHST
Button disabled: false
"You will get": 99 GHST
Transaction sends: 10 GHST

Test 2
Typed amount: 1000 GHST
Button disabled: false
"You will get": 990 GHST
Transaction sends: 10 GHST

Control
Typed amount: 5 GHST
Button disabled: false
"You will get": 4.95 GHST
Transaction sends: 5 GHST

```

The behavior reproduced consistently across repeated runs.

## **Impact**

An extra zero in the bridge amount is enough to cause the transaction to use the user’s entire GHST balance.

The main issue is the mismatch between the amount presented by the interface and the amount actually passed to the transaction. The user can see a result calculated from the amount they typed while the transaction uses a different amount.

## **Expected behavior**

An amount greater than the available balance should be rejected, or the interface should update all displayed amounts to the exact value that will be submitted.

The amount shown in the confirmation flow should always correspond to the amount passed to the bridge transaction.

## **Suggested fix**

The simplest fix is to remove the silent clamp and let the balance check reject over-balance input:

```auto
const preparedAmount = useMemo(() => {
  try {
    return BigInt(parseFloat(amount) * Math.pow(10, 18));
  } catch {
    return 0n;
  }
}, [amount]);

```

Then the existing balance check can operate on the actual requested amount.

At minimum, if the clamp is intentional, the confirmation UI and fee calculation should use the same clamped amount that is actually sent.

---

<div class="post-metadata">

### Author: ![VoidDragon](https://ghostforum.org/user_avatar/ghostforum.org/voiddragon/32/16_2.png) [@VoidDragon](https://ghostforum.org/u/VoidDragon)
#### Post date: [October 3, 2026, 11:47pm UTC](https://ghostforum.org/t/bridge-over-balance-input-sends-the-full-balance-while-the-ui-shows-a-different-amount/84/2 "2026-10-03T23:47:12Z")

</div>

Thank you for your report!

You are right - the values are desynced between what the UI shows and what is actually calculated. I don’t think we should let the user type any value they want and then execute a different one; that would be worse. So I believe your second option is much better.

You can continue this thread - could you provide a more in-depth fix for synchronizing the UI and the calculated value?

---

<div class="post-metadata">

### Author: ![callmefull](https://ghostforum.org/letter_avatar_proxy/v4/letter/c/cdc98d/32.png) [@callmefull](https://ghostforum.org/u/callmefull)
#### Post date: [October 4, 2026, 1:43am UTC](https://ghostforum.org/t/bridge-over-balance-input-sends-the-full-balance-while-the-ui-shows-a-different-amount/84/3 "2026-10-04T01:43:07Z")

</div>

Thanks. I agree that the clamped amount should be the single source of truth for the rest of the bridge flow.

I would structure it so the typed value is parsed once, then normalized against the wallet balance. Everything shown to the user and everything sent on-chain should use that same normalized value.

For example:

```auto
const requestedAmount = parseAmount(amount);

const effectiveAmount =
  requestedAmount > ghstBalance._value
    ? ghstBalance._value
    : requestedAmount;

```

Then `effectiveAmount` should be used for the fee/result calculation:

```auto
const amountAfterFee = calculateAmountAfterFee(effectiveAmount);

```

and for the actual transaction:

```auto
const txHash = await ghost(
  chainId,
  address,
  convertedReceiver,
  effectiveAmount
);

```

The confirmation UI should also display `effectiveAmount`, not the raw typed amount.

For example, with a 10 GHST balance and the user typing 100 GHST:

```auto
Entered: 100 GHST
Amount sent: 10 GHST
You will get: 9.90 GHST

```

I would also keep an explicit over-balance state so the user is told that the entered amount exceeded the balance and was capped. That way the clamp is visible instead of silently changing the transaction amount.

There is another point I would change at the same time: the amount should not be converted with `parseFloat(amount) * 1e18` for the value used by the transaction. Parsing the decimal input exactly avoids cases where the displayed amount and the actual integer amount differ because of floating-point rounding.

The invariant I would enforce is:

```auto
displayed amount == amount passed to ghost()

```

and the fee / “You will get” calculation should be derived from that same value.
