Truffle – How to Fix ‘execution reverted: ds-math-sub-underflow’ in Ether Swap Processing

ethergasjavascripttruffleuniswap

I get this error or another error err: insufficient funds for gas * price + value, which has been exhaustively discussed, but no real solution has worked for me here.

I saw the suggestion made here, but I don't see where I'm using the same method

I am running a bot that operates a swap between crytocurrencies on two exchanges. Here is the relevant code:

The bot is running against a live contract on etherscan here.

reserve0 = reserves.reserve0;
            reserve0toNumber = BigInt(reserve0)
            let maxUint256 = BigInt("115792089237316195423570985008687907853269984665640564039457584007913129639935")
            if (reserve0toNumber < maxUint256) {
                reserve0Final = reserve0toNumber
            } else {
                reserve0Final = maxUint256
            }
            reserve1 = reserves.reserve1
            console.log(`Reserves on ${_routerPath[1]._address}`)
            console.log(`Token 1: ${reserve0}`)
            console.log(`Token 0: ${reserve1}\n`)

            try {
                
***             //This is where the ds-math-sub-underflow error occurs
                let result = await _routerPath[0].methods.getAmountsIn(reserve0Final, [_token0.address, _token1.address]).call()

                const token0In = result[0] 
                const token1In = result[1] 

                result = await _routerPath[1].methods.getAmountsOut(token1In, [_token1.address, _token0.address]).call()

                console.log(`Estimated amount of Token 0 needed to buy enough Token 1 on ${exchangeToBuy}\t\t| ${web3.utils.fromWei(token0In, 'ether')}`)
                console.log(`Estimated amount of Token 0 returned after swapping Token 1 on ${exchangeToSell}\t| ${web3.utils.fromWei(result[1], 'ether')}\n`)

                const { amountIn, amountOut } = await getEstimatedReturn(token0In, _routerPath, _token0, _token1)

                console.log([amountIn,amountOut])

                // const block = web3.eth.getBlock("latest")
                // const gas = web3.eth.getBlock("latest").gasLimit
                
***             //This is where the insufficient funds for gas * price + value error occurs
                const estimatedGasCost = await _routerPath[0].methods.swapExactTokensForTokens(result[1], web3.utils.toWei('1', 'ether'), [_token1.address, _token0.address],account, Math.floor(Date.now() / 1000) + 60*60).estimateGas({from: account, value: web3.utils.toWei(result[1], 'ether')});

Here is the error stack for ds-math-sub-underflow

Error: Returned error: execution reverted: ds-math-sub-underflow
    at Object.ErrorResponse (/Users/TrentKennelly/trading_bot_V2/node_modules/web3-core-helpers/lib/errors.js:28:19)
    at Object.callback (/Users/TrentKennelly/trading_bot_V2/node_modules/web3-core-requestmanager/lib/index.js:300:36)
    at /Users/TrentKennelly/trading_bot_V2/node_modules/web3-providers-ws/lib/index.js:114:45
    at Array.forEach (<anonymous>)
    at WebsocketProvider._onMessage (/Users/TrentKennelly/trading_bot_V2/node_modules/web3-providers-ws/lib/index.js:102:69)
    at W3CWebSocket._dispatchEvent [as dispatchEvent] (/Users/TrentKennelly/trading_bot_V2/node_modules/yaeti/lib/EventTarget.js:115:12)
    at W3CWebSocket.onMessage (/Users/TrentKennelly/trading_bot_V2/node_modules/websocket/lib/W3CWebSocket.js:234:14)
    at WebSocketConnection.<anonymous> (/Users/TrentKennelly/trading_bot_V2/node_modules/websocket/lib/W3CWebSocket.js:205:19)
    at WebSocketConnection.emit (node:events:513:28)
    at WebSocketConnection.processFrame (/Users/TrentKennelly/trading_bot_V2/node_modules/websocket/lib/WebSocketConnection.js:554:26)
    at /Users/TrentKennelly/trading_bot_V2/node_modules/websocket/lib/WebSocketConnection.js:323:40
    at process.processTicksAndRejections (node:internal/process/task_queues:77:11) {
  data: //this is a hash - is it safe to provide?
}

Here is the error stack for err: insufficient funds for gas * price + value

Estimated amount of Token 0 needed to buy enough Token 1 on Sushiswap       | 0.000000000000000022
Estimated amount of Token 0 returned after swapping Token 1 on Uniswap  | 0.000000000000000012

Error: Returned error: err: insufficient funds for gas * price + value: address 0xdf58cA0a02bf9b43CE13b8AB5969E4b60f22c0a3 have 11059137636702474 want 12000000000000000000 (supplied gas 15010499)
    at Object.ErrorResponse (/Users/TrentKennelly/trading_bot_V2/node_modules/web3-core-helpers/lib/errors.js:28:19)
    at Object.callback (/Users/TrentKennelly/trading_bot_V2/node_modules/web3-core-requestmanager/lib/index.js:300:36)
    at /Users/TrentKennelly/trading_bot_V2/node_modules/web3-providers-ws/lib/index.js:114:45
    at Array.forEach (<anonymous>)
    at WebsocketProvider._onMessage (/Users/TrentKennelly/trading_bot_V2/node_modules/web3-providers-ws/lib/index.js:102:69)
    at W3CWebSocket._dispatchEvent [as dispatchEvent] (/Users/TrentKennelly/trading_bot_V2/node_modules/yaeti/lib/EventTarget.js:115:12)
    at W3CWebSocket.onMessage (/Users/TrentKennelly/trading_bot_V2/node_modules/websocket/lib/W3CWebSocket.js:234:14)
    at WebSocketConnection.<anonymous> (/Users/TrentKennelly/trading_bot_V2/node_modules/websocket/lib/W3CWebSocket.js:205:19)
    at WebSocketConnection.emit (node:events:513:28)
    at WebSocketConnection.processFrame (/Users/TrentKennelly/trading_bot_V2/node_modules/websocket/lib/WebSocketConnection.js:554:26)
    at /Users/TrentKennelly/trading_bot_V2/node_modules/websocket/lib/WebSocketConnection.js:323:40
    at process.processTicksAndRejections (node:internal/process/task_queues:77:11) {
  data: null
}

I can't see a reason why this would be failing other than maybe a math problem? Any thoughts?

UPDATE I have been printing out different parts of the code I provided and I'm fairly comfortable that there is indeed a math problem, I'm just not totally sure how to fix it.

Here's the updated code:

reserve0 = reserves.reserve0;
            reserve1 = reserves.reserve1
            console.log(`Reserves on ${_routerPath[1]._address}`)
            console.log(`Token 1: ${reserve0}`)
            console.log(`Token 0: ${reserve1}\n`)

            try {
                //***This is where the ds-math-sub-underflow error occurs
                let result = await _routerPath[0].methods.getAmountsIn(reserve0, [_token0.address, _token1.address]).call()
                console.log(`result: ${result}`)

                const token0In = result[0] 
                const token1In = result[1] 

                result = await _routerPath[1].methods.getAmountsOut(token1In, [_token1.address, _token0.address]).call()
                console.log(`result V2: ${result}`)

                console.log(`Estimated amount of Token 0 needed to buy enough Token 1 on ${exchangeToBuy}\t\t| ${web3.utils.fromWei(token0In, 'ether')}`)
                console.log(`Estimated amount of Token 0 returned after swapping Token 1 on ${exchangeToSell}\t| ${web3.utils.fromWei(result[1], 'ether')}\n`)

                const { amountIn, amountOut } = await getEstimatedReturn(token0In, _routerPath, _token0, _token1)

                console.log(`amountIn: ${amountIn}`)
                console.log(`amountOut: ${amountOut}`)
                
                // ***This is where the insufficient funds for gas * price + value error occurs
                const estimatedGasCost = await _routerPath[0].methods.swapExactTokensForTokens(result[1], web3.utils.toWei('1', 'ether'), [_token1.address, _token0.address],account, Math.floor(Date.now() / 1000) + 60*60).estimateGas({from: account, value: web3.utils.toWei(result[1], 'ether')});

Here's an example with result and resultV2 printed out:

Swap Initiated on Sushiswap, Checking Price...

Current Block: 16490588
-----------------------------------------
UNISWAP   | WETH/SYN     | 1669
SUSHISWAP | SYN/WETH     | 1817

Percentage Difference: -8.15%

Determining Direction...

Potential Arbitrage Direction:

Buy  -->     Sushiswap
Sell     -->     Uniswap

Determining Profitability...

Reserves on 0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D
Token 1: 41732
Token 0: 25

result: 24,41732
result V2: 41732,41706
Estimated amount of Token 0 needed to buy enough Token 1 on Sushiswap       | 0.000000000000000024
Estimated amount of Token 0 returned after swapping Token 1 on Uniswap  | 0.000000000000041706

amountIn: 2.4e-17
amountOut: 1.2e-17
Error: Returned error: err: insufficient funds for gas * price + value: address 0xdf58cA0a02bf9b43CE13b8AB5969E4b60f22c0a3 have 11059137636702474 want 41706000000000000000000 (supplied gas 15010499)
    at Object.ErrorResponse (/Users/TrentKennelly/trading_bot_V2/node_modules/web3-core-helpers/lib/errors.js:28:19)
    at Object.callback (/Users/TrentKennelly/trading_bot_V2/node_modules/web3-core-requestmanager/lib/index.js:300:36)
    at /Users/TrentKennelly/trading_bot_V2/node_modules/web3-providers-ws/lib/index.js:114:45
    at Array.forEach (<anonymous>)
    at WebsocketProvider._onMessage (/Users/TrentKennelly/trading_bot_V2/node_modules/web3-providers-ws/lib/index.js:102:69)
    at W3CWebSocket._dispatchEvent [as dispatchEvent] (/Users/TrentKennelly/trading_bot_V2/node_modules/yaeti/lib/EventTarget.js:115:12)
    at W3CWebSocket.onMessage (/Users/TrentKennelly/trading_bot_V2/node_modules/websocket/lib/W3CWebSocket.js:234:14)
    at WebSocketConnection.<anonymous> (/Users/TrentKennelly/trading_bot_V2/node_modules/websocket/lib/W3CWebSocket.js:205:19)
    at WebSocketConnection.emit (node:events:513:28)
    at WebSocketConnection.processFrame (/Users/TrentKennelly/trading_bot_V2/node_modules/websocket/lib/WebSocketConnection.js:554:26)
    at /Users/TrentKennelly/trading_bot_V2/node_modules/websocket/lib/WebSocketConnection.js:323:40
    at process.processTicksAndRejections (node:internal/process/task_queues:77:11) {
  data: null
}

Seems like the amountsIn result is greater than the amountsOut result every time.

Final note – this update is demonstrating the gas * price + value error, but I THINK the ds-math-sub-underflow error happens in the same spot, it just errors out at the try in its entirety

Update 2 It's possible that the math problem is originating from the estimateGas function call. It seems that function may require that I provide a wallet address with enough ether to cover the entirety of the trade amount, even though the contract this is using has arbitrage components to it (though, at this point in the code, because I haven't called the contract at all it doesn't know that). How can I estimate the gas in a "theoretical" way without it deciding I'm too poor to run the trade and refusing to continue?

Update 3 I've discovered that reserve0 in the line let result = await _routerPath[0].methods.getAmountsIn(reserve0, [_token0.address, _token1.address]).call() is being passed as a string when it should be a uint. However, even with this change the same error is occurring. Could really use some help here if anybody has any to give.

Best Answer

This is a question that was actually, ultimately, two questions. As to the gas * price + value error, it was because the wallet was being analyzed to determine if there were enough funds to complete the transaction, even though the code ultimately took in a flashloan so this wouldn't have been an important consideration. I added this calculation that, while not perfect, at least moves me along for now. Likely will revisit it later:

const gasPrice = await web3.eth.getGasPrice();
const gasCalc = (gasPrice * 21000).toString();
const estimatedGasCost = web3.utils.fromWei(gasCalc, 'ether')  

As to the ds-math-sub-underflow error, I have taken time to review more closely and have determined, as others have said in other threads, that the reason is truly that the amounts are too small to work with. For instance, here's a readout that gave me this error:

Swap Initiated on Sushiswap, Checking Price...

Current Block: 16539823
-----------------------------------------
UNISWAP   | WETH/YGG     | 7761
SUSHISWAP | YGG/WETH     | 5166

Percentage Difference: 50.23%

Determining Direction...

Potential Arbitrage Direction:

Buy  -->     Uniswap
Sell     -->     Sushiswap

Determining Profitability...

Reserves on 0xd9e1cE17f2641f24aE83637ab66a2cca9C378B9F
WETH: 1562933246649717869273477
YGG: 302558036805197349829

Error: Returned error: execution reverted: ds-math-sub-underflow
    at Object.ErrorResponse (/Users/TrentKennelly/trading_bot_V2/node_modules/web3-core-helpers/lib/errors.js:28:19)
    at Object.callback (/Users/TrentKennelly/trading_bot_V2/node_modules/web3-core-requestmanager/lib/index.js:300:36)
    at /Users/TrentKennelly/trading_bot_V2/node_modules/web3-providers-ws/lib/index.js:114:45
    at Array.forEach (<anonymous>)
    at WebsocketProvider._onMessage (/Users/TrentKennelly/trading_bot_V2/node_modules/web3-providers-ws/lib/index.js:102:69)
    at W3CWebSocket._dispatchEvent [as dispatchEvent] (/Users/TrentKennelly/trading_bot_V2/node_modules/yaeti/lib/EventTarget.js:115:12)
    at W3CWebSocket.onMessage (/Users/TrentKennelly/trading_bot_V2/node_modules/websocket/lib/W3CWebSocket.js:234:14)
    at WebSocketConnection.<anonymous> (/Users/TrentKennelly/trading_bot_V2/node_modules/websocket/lib/W3CWebSocket.js:205:19)
    at WebSocketConnection.emit (node:events:513:28)
    at WebSocketConnection.processFrame (/Users/TrentKennelly/trading_bot_V2/node_modules/websocket/lib/WebSocketConnection.js:554:26)
    at /Users/TrentKennelly/trading_bot_V2/node_modules/websocket/lib/WebSocketConnection.js:323:40
    at process.processTicksAndRejections (node:internal/process/task_queues:77:11) {
  data: '0x08c379a00000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000001564732d6d6174682d7375622d756e646572666c6f770000000000000000000000'
}

A 50% difference sounds good until you realize that 1 YGG = 0.000198139 ETH. It truly is just too small to deal with. So, while I'm going to continue to monitor those in case something changes, I've added a filter to my catch statement so I don't have to see the error all the time.

Related Topic