Repository navigation
Conversation
Math.floor(amount * 10 ** decimals).toString() prints "1e+21" from a thousand tokens with 18 decimals, and JSBI.BigInt and BigNumber.from both refuse that, so a Uniswap CLMM open-position or add-liquidity of that size failed before reaching the chain. The float multiplication had also lost exact digits from 2^53 raw units. One helper, toRawAmount, replaces the 27 sites of that expression across uniswap, pancakeswap, dflow, okx and titan: the typed digits are kept as text, expanded from exponent form on either side, cut to the token's decimals as the floor was, and parseUnits does exact arithmetic on the string. pancakeswap's CLMM add-liquidity had already moved to parseUnits; its siblings had not. Fixes hummingbot#622.
|
|
|
||
| if (side === 'SELL') { | ||
| const amountRaw = Math.floor(amount * Math.pow(10, baseTokenInfo.decimals)).toString(); | ||
| const amountRaw = toRawAmount(amount, baseTokenInfo.decimals); |
There was a problem hiding this comment.
This conversion only covers the SELL branch. DFlow BUY requests use approximateBuyViaSellLeg, which still converts the amount with Math.floor(baseAmount * 10 ** decimals).toString(). A BUY of 1000 units of an 18-decimal token therefore produces "1e+21" instead of a raw integer string and can fail before returning a route. Titan BUY requests have the same problem, as does OKX when ExactOut falls back to approximation.
Knowledge Base Used:
…ount path approximateBuyViaSellLeg, which every router BUY without ExactOut goes through, and Jupiter's quote built the raw amount with the same expression, written with ** instead of Math.pow, so the first sweep did not find them.
Before submitting this PR, please make sure:
A description of the changes proposed in the pull request:
Fixes #622.
Math.floor(amount * Math.pow(10, decimals)).toString()prints"1e+21"once the raw amount reaches 1e21 — a thousand tokens with 18 decimals — andJSBI.BigIntandBigNumber.fromboth refuse that string, so a Uniswap CLMM open-position or add-liquidity of that size failed before it reached the chain, as the report describes. The float multiplication had also stopped being exact long before, from 2^53 raw units (about 9 tokens at 18 decimals).The expression appeared 27 times across
uniswap,pancakeswap,dflow,okxandtitan, plus twice written with**instead ofMath.pow: inapproximateBuyViaSellLeg, which every router BUY without ExactOut support goes through, and in Jupiter's quote.pancakeswap's CLMM add-liquidity had already moved toparseUnits, its siblings had not. All of them now go through one helper,toRawAmount(amount, decimals)insrc/services/raw-amount.ts:String(n), the shortest round-trip form —toFixedwas not used because it prints the binary expansion,(0.1).toFixed(18)ends in…006),1e-8,1.5e21),parseUnits(amount.toString())throw on extra digits,parseUnitsdoes exact arithmetic on the string.Behaviour for amounts the old code handled correctly is unchanged: the result is the same integer string.
Tests performed by the developer:
test/services/raw-amount.test.ts: 1000 at 18 decimals is1000000000000000000000and both big-int constructors accept it (the replaced expression yields1e+21, asserted alongside);0.1and1.1at 18 decimals are exact; extra digits are cut, not refused;1e-8and1.5e21are expanded.test/connectors/router-utils.test.ts: a BUY of 1000 of an 18-decimal token hands the sell leg1000000000000000000000, not1e+21.test/connectors/{uniswap,pancakeswap,dflow,okx,titan,jupiter}androuter-utils: 165 passed.pnpm typecheck, eslint and prettier clean on the changed files.Tips for QA testing:
Repeat the report:
gateway lp uniswap/clmm add-liquidity(orPOST /trading/clmm/open-position) with an amount whose raw value exceeds 1e21, e.g. 1000 of an 18-decimal token. Before:Cannot convert 1e+21 to a BigInt. After: the position opens with the intended amount. A small swap quote through any of the five connectors should return the same raw amount as before.