Search code examples
javascriptethereumsolidityweb3jsethers.js

Encoding -1 as a uint256


As per Aave documentation for liquidationCall one must pass uint(-1) for debtToCover parameter in order to liquidate the maximum amount possible for an account with a healthFactor < 1. How is it possible to encode -1 as a uint256 using web3, ethers, etc?

Attempting this using web3, for example yields an error.

> web3.eth.abi.encodeParameter("uint", "-1")
Uncaught:
Error: value out-of-bounds (argument=null, value="-1", code=INVALID_ARGUMENT, version=abi/5.0.7)
    at Logger.makeError (-/node_modules/@ethersproject/logger/lib/index.js:199:21)
    at Logger.throwError (-/node_modules/@ethersproject/logger/lib/index.js:208:20)
    at Logger.throwArgumentError (-/node_modules/@ethersproject/logger/lib/index.js:211:21)
    at NumberCoder.Coder._throwError (-/node_modules/web3-eth-abi/node_modules/@ethersproject/abi/lib/coders/abstract-coder.js:40:16)
    at NumberCoder.encode (-/node_modules/web3-eth-abi/node_modules/@ethersproject/abi/lib/coders/number.js:40:18)
    at -/node_modules/web3-eth-abi/node_modules/@ethersproject/abi/lib/coders/array.js:71:19
    at Array.forEach (<anonymous>)
    at Object.pack (-/node_modules/web3-eth-abi/node_modules/@ethersproject/abi/lib/coders/array.js:57:12)
    at TupleCoder.encode (-/node_modules/web3-eth-abi/node_modules/@ethersproject/abi/lib/coders/tuple.js:36:24)
    at AbiCoder.encode (-/node_modules/web3-eth-abi/node_modules/@ethersproject/abi/lib/abi-coder.js:86:15)
    at ABICoder.encodeParameters (-/node_modules/web3-eth-abi/lib/index.js:120:27)
    at ABICoder.encodeParameter (-/node_modules/web3-eth-abi/lib/index.js:78:17) {
  reason: 'value out-of-bounds',
  code: 'INVALID_ARGUMENT',
  argument: null,
  value: '-1'

Solution

  • uint stands for "unsigned integer", so it doesn't accept -1 as a valid value.

    Solidity converts uint(-1) to the maximal value of uint up to the version 0.7.6, because the value underflows.

    pragma solidity ^0.7;
    
    contract MyContract {
        // returns 115792089237316195423570985008687907853269984665640564039457584007913129639935
        function foo() external pure returns (uint256) {
            return uint(-1);
        }
    }
    

    Version 0.8.0 introduced automatic revert on integer underflow/overflow, and it doesn't even allow casting the -1 literal to uint, but you can test the revert this way:

    pragma solidity ^0.8;
    
    contract MyContract {
        // reverts on underflow
        function foo() external pure returns (uint256) {
            uint256 number = 0;
            number--;
            return number;
        }
    }
    

    Many JS libraries also don't allow passing -1 to the "unsigned integer" simply because it's an invalid value for the datatype. But since the -1 effectively represents the maximal value in older versions of Solidity, you can pass the uint maximal value.

    For uint8, that's (2^8)-1 (or 255)

    const BN = web3.utils.BN;
    const number = (new BN(2)).pow(new BN(8)).sub(new BN(1));
    console.log(number.toString());
    

    For uint256, that's (2^256)-1 (or the large number starting 115...)

    const BN = web3.utils.BN;
    const number = (new BN(2)).pow(new BN(256)).sub(new BN(1));
    console.log(number.toString());