Using the maximum integer in Solidity

There’s a variety of reasons you might want to know the maximum possible integer in solidity.

One common use case is to approve a contract to transfer your tokens on your behalf:
tokenContract.approve(exchangeContract, MAX_INT, { from: me })
Here I tell the token contract that the exchange contract is allowed to transfer all my tokens.

Another common use case is to use the maximum number to flag a certain state - though in cases like this you should be careful of ‘semantic overloading’.

Whatever the situation you’re trying, how do you actually get the maximum integer in solidity? I can think of 4 main ways:

1) Just type in the number!

uint256 MAX_INT = 115792089237316195423570985008687907853269984665640564039457584007913129639935

I think we can all agree doing that looks somewhat ridiculous and comes with significant risk that you’ll get the number wrong. As someone reading the contract it would be quite hard to understand what the number really is either.

2) Use the hex version of the number

uint256 MAX_INT = 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff

This method has the advantage that its pretty clear what you’re achieving - it’s commonly known that a string of Fs tends to be the maximum possible number in some number of bits. However it’s hard to be sure with this method you’ve actually got all 64 Fs, and is fairly common to see people using a string with too few.

3) Casting -1

uint256 MAX_INT = uint256(-1)

This method is short to write, and unlike the previous 2 methods its easy to check you’ve got it correct. However the fundamental reason this method works is due to underflowing a calculation, which is generally looked upon as a negative thing to do and to me feels a bit hacky.

One thing to beware of with this method is a new proposal to build SafeMath into Ethereum by default, meaning that underflows and overflows would become impossible. Although this isn’t fully designed yet, it’s likely that uint256(-1) could throw an error.

4) Using exponentiation

uint256 MAX_INT = 2**256 - 1

This is my personal favourite due to the clarity of what it is you’re calculating. It’s a more expensive calculation than uint256(-1), however if you store MAX_INT as a constant, it will only need be calculated once during compilation.

This method can easily be mistaken for an overflow - after all it first calculates 2**256 which would need 257 bits to store (larger than can be stored in solidity). However because this is just an intermediate result that does not get stored in a variable, the value does not overflow and - 1 is calculated safely bringing the value back into 256 bits.

This post certainly doesn’t cover everything, so I’d love to hear if people have other pros/cons of the methods above or new methods entirely! Which is your preferred method? Should we add this constant to OpenZeppelin contracts to try to standardize it?

12 Likes

Personally, I think option number 4 is the way to go.

Options 1 and 2 don’t inmediatly tell me what I’m looking at, specially 1 I’d be like “what the hell is this big ass number?”.
In the case of option 3 I think you nailed the explanation - You are basically relying on an underflow, which might not always be reliable and it can even be counterintuitive since you are using an uint and casting a signed int.

tbh, I wonder if it would be interesting for Solidity to include these kind of constants by default i.e address(0) MAXUINT , etc…

2 Likes

Hi @Alice,

Welcome to the community forum :wave:

I am not keen on approving a maximum as an allowance for ERC20 nor semantic overloading. Interested to learn other use cases.

I had a quick look through the List of Solidity libraries in the wild to see if any had a max uint256 constant.

OpenZeppelin Test Helpers have JavaScript constants such as MAX_UINT256. If there are use cases and a need then there would be an argument for adding common constants to OpenZeppelin Contracts.

1 Like

I think 3 and 4 could be right. I don’t think anyone would go with 1 and 2

1 Like

I’ve only ever used, and seen, 4 before this post. I’ve seen tutorials write out a really big number to make it easier to understand for newer viewers. But never the max integer Solidity can handle.

I’ve used OpenZeppelin’s MAX_UINT256 from the test helpers for writing tests. Otherwise, I’ve personally used number 4 for smart contract allowance approvals.

1 Like

One more: For solidity 0.6.8, there are new keywords for returning max/mins of certain value types that could be an option (type(T).max), thus:
“type(uint256).max”

see https://solidity.readthedocs.io/en/v0.6.8/types.html#integers

7 Likes

From Solidity 0.6.8 Option :five: type(uint256).max looks like the winner from a readability standpoint (and I assume gas too)

// SPDX-License-Identifier: MIT
pragma solidity ^0.6.8;

contract Max {
    // 1) Just type in the number!
    uint256 constant public MAX_INT_NUMBER = 115792089237316195423570985008687907853269984665640564039457584007913129639935;
    
    // 2) Use the hex version of the number
    uint256 constant public MAX_INT_HEX = 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff;
    
    // 3) Casting -1
    uint256 constant public MAX_INT_CAST = uint256(-1);
    
    // 4) Using exponentiation
    uint256 constant public MAX_INT_EXPONENTIATION = 2**256 - 1;
    
    // 5) Type (Solidity 0.6.8)
    uint256 constant public MAX_INT_TYPE = type(uint256).max;
}

Thanks @kev :pray: for option 5.

See the release notes: https://github.com/ethereum/solidity/releases/tag/v0.6.8 and Documentation for details.

6 Likes

@kev that one is awesome. I hadn’t noticed that in the latest releases. I’ll definitely be using that option from now on!

2 Likes

@Alice and @abcoathup :slightly_smiling_face:

It works on the shorter UINT types too!

2 Likes

\o/ I think we have a winner. Thanks @Alice and @kev. As people start to update their contracts to the latest solidity, we can start recommending type(uint256).max.

1 Like

Don't trust verify

I actually wanted to test 10**17 because some automated tools were pestering me about the number of zeros in 100000000000000000 while in fact all I wanted was 0.1 of the coin: https://eth-converter.com/

1 Like

Hi @marsxr,

Thanks for sharing :pray: Completely agree, don’t trust, verify yourself by trying it out.

I think type(uint...).max is the winner in terms of readbility and gas, but there’s a one-line way around the 0.8.x underflow checker: uint256(int256(-1))

type(uint256).max is clearly the most readable but if you're using a version of Solidity below ^0.6.8, 2**256 - 1 seems ideal.

This is a great post but I think that it would be worth it rename the variable to MAX_UINT, just so that it is clear that we're referring to the max value of uint256, not int256.

~uint256(0) Thanks:pray: for option 6 ?

1 Like

It depends on solidity version.
On current version, you should use type(uint256).max.

@abcoathup Getting a compilation error with "type(uint256).max" version used in
here https://github.com/Uniswap/v3-core/blob/main/contracts/libraries/FullMath.sol in the function mulDivRoundingUp(....)
Compiler run failed:
lib/v3-core/contracts/libraries/FullMath.sol:120:35: TypeError: Invalid type for argument in function call. Contract type required, but type(uint256) provided. on line require(result < type(uint256).max);
Any suggestions?

Changed "type(uint256).max" to "MAX_INT_EXPONENTIATION = 2**256 - 1" and the compile seems to be o.k with the exponentiation version.
Not clear why????
They are compiling with version >=0.5.0 is why.