Skip to content

Investigate amount argument must be of type String, represent a positive number and have at most 7 digits after the decimal error #2622

@piyalbasu

Description

@piyalbasu

We've been seeing an uptick of the following error in Sentry:

amount argument must be of type String, represent a positive number and have at most 7 digits after the decimal

Sentry reports that this error is being handled. Claude has generated the below explanation of the error. It appears to be a relatively benign error that the app is swallowing but is still being reported to Sentry. Let's quickly confirm that:

  1. Confirm that this is actually a benign error that is not adversely affecting users
  2. If benign, figure out how to suppress this reports. If not benign, fix the issue

The error originates from **`stellar/js-stellar-base`** — specifically `Operation.isValidAmount()` in [`src/operation.js`](https://github.com/stellar/js-stellar-base/blob/master/src/operation.js#L420-L449). It's **not** a Freighter bug — it's the Stellar SDK rejecting an invalid amount before the transaction is even built.

## The Call Chain in Freighter

When you hit "Send" on the `/account/sendPayment` route:

1. **`Send` component** (`extension/src/popup/views/Send/index.tsx`) renders `SendAmount`
2. `SendAmount` triggers `fetchData` from **`useSimulateTxData`** hook
3. This calls **`getBuiltTx()`** → **`getOperation()`**
4. `getOperation()` calls one of:
   - `Operation.payment({ destination, asset, amount })` — regular payment
   - `Operation.createAccount({ destination, startingBalance: amount })` — unfunded account
   - `Operation.pathPaymentStrictSend({ sendAmount: amount, ... })` — path payment/swap
5. The SDK's `isValidAmount(amount)` **rejects** the value and throws the error

## What `isValidAmount` Checks

```javascript
static isValidAmount(value, allowZero = false) {
  if (typeof value !== 'string') return false;        // Must be a string
  amount = new BigNumber(value);
  if (
    (!allowZero && amount.isZero()) ||                 // No zero (for most ops)
    amount.isNegative() ||                             // No negatives
    amount.times(10000000).gt(MAX_INT64) ||            // No overflow
    amount.decimalPlaces() > 7 ||                      // Max 7 decimal places
    amount.isNaN() || !amount.isFinite()               // Must be a valid number
  ) return false;
}```

## Most Likely Cause in Freighter's Flow

The amount passed to `getOperation()` comes from Redux state via `cleanAmount(amount)`:

```javascript
export const cleanAmount = (s: string) => s.replace(/[^0-9.]/g, "");```

This strips commas and non-numeric chars, but the `formatAmountPreserveCursor` function in the input UI **truncates decimals to `decimals` places** (default 7). The problem likely happens when:

1. **The amount has commas** — `cleanAmount("1,000.5")` → `"1000.5"` :white_check_mark: (this works fine)
2. **The amount is `"0"` or empty** — `cleanAmount("")` → `""` which is **not a valid number string** → :x:
3. **Floating point from calculations** — if any intermediate JS number produces something like `"1e-8"` (scientific notation) or `"0.123456789"` (>7 decimals)
4. **The `amount` field in Redux state is `"0"` (the initial value)** and the user triggers simulation before entering an amount — `isValidAmount("0", allowZero=false)` → :x:

**The most common trigger**: the `amount` field defaults to `"0"` in Redux initial state, and if the simulation runs before the user changes it, `Operation.payment({ amount: "0" })` fails because zero is not allowed for payment operations.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions