bug: Swap input not scaled by token decimals — sends micro-units instead of tokens #15

Closed
opened 2026-03-13 01:47:47 +00:00 by Brouie · 10 comments
Brouie commented 2026-03-13 01:47:47 +00:00 (Migrated from gitlab.com)

Description

The swap page passes the user's input amount directly to the swap contract without multiplying by 10^decimals. Entering '10000' sends 10000 micro-units (0.01 tokens) instead of 10000 tokens (10000000000 micro-units).

Root Cause

SwapPage.tsx line 102 passes raw inputAmount to swap():
return swap(address, offerLabel, selectedPair.contract_addr, inputAmount, ...)

No conversion like BigInt(parseFloat(inputAmount) * 10**6).toString() is applied.

Evidence

  • Entered 100000 EMBER swap, balance barely changed
  • On-chain balance query confirms: 895770374807 micro-units (started ~895770473897)
  • Actual deduction: ~99090 micro-units = 0.099 tokens instead of 100000 tokens

Expected

User input should be multiplied by 10^decimals (10^6 for CW20 tokens) before sending to the contract.

Impact

Critical — all swaps execute at dust amounts. Users think they swapped 10000 tokens but actually swapped 0.01 tokens.

## Description The swap page passes the user's input amount directly to the swap contract without multiplying by 10^decimals. Entering '10000' sends 10000 micro-units (0.01 tokens) instead of 10000 tokens (10000000000 micro-units). ## Root Cause SwapPage.tsx line 102 passes raw inputAmount to swap(): `return swap(address, offerLabel, selectedPair.contract_addr, inputAmount, ...)` No conversion like `BigInt(parseFloat(inputAmount) * 10**6).toString()` is applied. ## Evidence - Entered 100000 EMBER swap, balance barely changed - On-chain balance query confirms: 895770374807 micro-units (started ~895770473897) - Actual deduction: ~99090 micro-units = 0.099 tokens instead of 100000 tokens ## Expected User input should be multiplied by 10^decimals (10^6 for CW20 tokens) before sending to the contract. ## Impact Critical — all swaps execute at dust amounts. Users think they swapped 10000 tokens but actually swapped 0.01 tokens.
Brouie commented 2026-03-13 02:02:26 +00:00 (Migrated from gitlab.com)

mentioned in issue #1

mentioned in issue #1
Brouie commented 2026-03-13 02:04:44 +00:00 (Migrated from gitlab.com)

Same decimals bug confirmed on Pool page. Provide Liquidity also passes raw user input without scaling by 10^decimals. Entering 1000 EMBER/CORAL added ~0.001 tokens to the pool instead of 1000 tokens.

Affects: SwapPage.tsx, PoolPage.tsx — likely anywhere user input amounts are sent to contracts.

Same decimals bug confirmed on Pool page. Provide Liquidity also passes raw user input without scaling by 10^decimals. Entering 1000 EMBER/CORAL added ~0.001 tokens to the pool instead of 1000 tokens. Affects: SwapPage.tsx, PoolPage.tsx — likely anywhere user input amounts are sent to contracts.
Brouie commented 2026-03-13 04:22:43 +00:00 (Migrated from gitlab.com)

mentioned in issue #18

mentioned in issue #18
PlasticDigits commented 2026-03-13 06:57:11 +00:00 (Migrated from gitlab.com)

Fix pushed on branch fix/issue-15-decimal-scaling (commit e5da3ec).

Changes:

  • Added toRawAmount(humanAmount, decimals) and fromRawAmount(rawAmount, decimals) utility functions to formatAmount.ts
  • SwapPage: simulateSwap() and swap() now receive amounts scaled by 10^decimals; insufficientBalance check compares raw units correctly; Max button sets human-readable amount
  • PoolPage: provideLiquidity() and withdrawLiquidity() now receive amounts scaled by 10^decimals; LP Max button uses fromRawAmount instead of abbreviated display value

All user-facing inputs (swap, provide liquidity, withdraw liquidity) are now properly converted from human-readable token amounts to on-chain micro-units before being sent to contracts.

Requesting review from @Brouie.

Fix pushed on branch `fix/issue-15-decimal-scaling` (commit e5da3ec). **Changes:** - Added `toRawAmount(humanAmount, decimals)` and `fromRawAmount(rawAmount, decimals)` utility functions to `formatAmount.ts` - **SwapPage**: `simulateSwap()` and `swap()` now receive amounts scaled by 10^decimals; `insufficientBalance` check compares raw units correctly; Max button sets human-readable amount - **PoolPage**: `provideLiquidity()` and `withdrawLiquidity()` now receive amounts scaled by 10^decimals; LP Max button uses `fromRawAmount` instead of abbreviated display value All user-facing inputs (swap, provide liquidity, withdraw liquidity) are now properly converted from human-readable token amounts to on-chain micro-units before being sent to contracts. Requesting review from @Brouie.
PlasticDigits commented 2026-03-13 06:57:26 +00:00 (Migrated from gitlab.com)

mentioned in merge request !720

mentioned in merge request !720
PlasticDigits commented 2026-03-13 06:58:24 +00:00 (Migrated from gitlab.com)

mentioned in commit e5da3ec330

mentioned in commit e5da3ec3301e5be676bfe8f2645364f265594faa
PlasticDigits commented 2026-03-13 06:58:54 +00:00 (Migrated from gitlab.com)

mentioned in commit d1a230e1e4

mentioned in commit d1a230e1e432913cebb391f2d4b6b5b94b6f3062
PlasticDigits (Migrated from gitlab.com) closed this issue 2026-03-13 06:58:54 +00:00
Brouie commented 2026-03-13 12:12:22 +00:00 (Migrated from gitlab.com)

Verified fixed — swapping 100 EMBER now deducts exactly 100,000,000 micro-units (100 tokens). Before: 895770474907, After: 895670474907. Decimals scaling working correctly.

Verified fixed — swapping 100 EMBER now deducts exactly 100,000,000 micro-units (100 tokens). Before: 895770474907, After: 895670474907. Decimals scaling working correctly.
Brouie commented 2026-04-04 04:34:13 +00:00 (Migrated from gitlab.com)

mentioned in issue #49

mentioned in issue #49
Brouie commented 2026-04-06 03:36:43 +00:00 (Migrated from gitlab.com)

mentioned in issue #50

mentioned in issue #50
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
code/cl8y-dex-terraclassic#15
No description provided.