Repository: vyperlang/vyper

Issue:
`_MinMax` and `AsWeiValue` perform too restrictive checks on `Decimal` inputs
### Version Information

* vyper Version (output of `vyper --version`): 0.3.8+commit.d76c6ed2
* OS: OSX
* Python Version (output of `python --version`): 3.8.0

### What's your issue about?

In the `evaluate` function of the `_MinMax` and `AsWeiValue`, decimal inputs are checked to be within range for the `Decimal` type, however, this checks seem to be too restrictive as `max_value(decimal)` would not pass the test itself.

https://github.com/vyperlang/vyper/blob/187ab0eec8efbe19ed5046e4e947249e9d43141c/vyper/builtins/functions.py#L1029
https://github.com/vyperlang/vyper/blob/187ab0eec8efbe19ed5046e4e947249e9d43141c/vyper/builtins/functions.py#L2015
```Vyper
@external
def test():
    a:decimal = min(max_value(decimal), 1.2)
```

```Vyper
@external
def test():
    a:uint256 = as_wei_value(max_value(decimal), "wei")
```
```Bash
Traceback (most recent call last):
  File "tester/tester.py", line 28, in <module>
    do()
  File "tester/tester.py", line 23, in do
    abi_a, bytecode_a,_ = compile(code)
  File "tester/tester.py", line 8, in compile
    res = vyper.compile_code(
  File "/Users/trocher/Documents/thesis/vyper/vyper/compiler/__init__.py", line 192, in compile_code
    return compile_codes(
  File "/Users/trocher/Documents/thesis/vyper/vyper/evm/opcodes.py", line 226, in _wrapper
    return fn(*args, **kwargs)
  File "/Users/trocher/Documents/thesis/vyper/vyper/compiler/__init__.py", line 143, in compile_codes
    raise exc
  File "/Users/trocher/Documents/thesis/vyper/vyper/compiler/__init__.py", line 138, in compile_codes
    out[contract_name][output_format] = OUTPUT_FORMATS[output_format](compiler_data)
  File "/Users/trocher/Documents/thesis/vyper/vyper/compiler/output.py", line 248, in build_bytecode_output
    return f"0x{compiler_data.bytecode.hex()}"
  File "/Library/Frameworks/Python.framework/Versions/3.8/lib/python3.8/functools.py", line 966, in __get__
    val = self.func(instance)
  File "/Users/trocher/Documents/thesis/vyper/vyper/compiler/phases.py", line 150, in bytecode
    self.assembly, is_runtime=False, no_bytecode_metadata=self.no_bytecode_metadata
  File "/Library/Frameworks/Python.framework/Versions/3.8/lib/python3.8/functools.py", line 966, in __get__
    val = self.func(instance)
  File "/Users/trocher/Documents/thesis/vyper/vyper/compiler/phases.py", line 141, in assembly
    return generate_assembly(self.ir_nodes, self.no_optimize)
  File "/Users/trocher/Documents/thesis/vyper/vyper/compiler/phases.py", line 126, in ir_nodes
    ir, ir_runtime, sigs = self._ir_output
  File "/Library/Frameworks/Python.framework/Versions/3.8/lib/python3.8/functools.py", line 966, in __get__
    val = self.func(instance)
  File "/Users/trocher/Documents/thesis/vyper/vyper/compiler/phases.py", line 122, in _ir_output
    return generate_ir_nodes(self.global_ctx, self.no_optimize)
  File "/Users/trocher/Documents/thesis/vyper/vyper/compiler/phases.py", line 117, in global_ctx
    return GlobalContext(self.vyper_module_folded)
  File "/Users/trocher/Documents/thesis/vyper/vyper/compiler/phases.py", line 107, in vyper_module_folded
    module, storage_layout = self._folded_module
  File "/Library/Frameworks/Python.framework/Versions/3.8/lib/python3.8/functools.py", line 966, in __get__
    val = self.func(instance)
  File "/Users/trocher/Documents/thesis/vyper/vyper/compiler/phases.py", line 101, in _folded_module
    return generate_folded_ast(
  File "/Users/trocher/Documents/thesis/vyper/vyper/compiler/phases.py", line 228, in generate_folded_ast
    vy_ast.folding.fold(vyper_module_folded)
  File "/Users/trocher/Documents/thesis/vyper/vyper/ast/folding.py", line 43, in fold
    changed_nodes += replace_builtin_functions(vyper_module)
  File "/Users/trocher/Documents/thesis/vyper/vyper/ast/folding.py", line 131, in replace_builtin_functions
    new_node = func.evaluate(node)  # type: ignore
  File "/Users/trocher/Documents/thesis/vyper/vyper/builtins/functions.py", line 2017, in evaluate
    raise InvalidType("Decimal value is outside of allowable range", node)
vyper.exceptions.InvalidType: Decimal value is outside of allowable range
  function "test", line 3:16 
       2 def test():
  ---> 3     a:decimal = min(max_value(decimal), 1.2)
  -----------------------^
```
```Bash
Error compiling: tests/customs/code.vy
Traceback (most recent call last):
  File "/Library/Frameworks/Python.framework/Versions/3.8/bin/vyper", line 8, in <module>
    sys.exit(_parse_cli_args())
  File "/Users/trocher/Documents/thesis/vyper/vyper/cli/vyper_compile.py", line 57, in _parse_cli_args
    return _parse_args(sys.argv[1:])
  File "/Users/trocher/Documents/thesis/vyper/vyper/cli/vyper_compile.py", line 154, in _parse_args
    compiled = compile_files(
  File "/Users/trocher/Documents/thesis/vyper/vyper/cli/vyper_compile.py", line 294, in compile_files
    compiler_data = vyper.compile_codes(
  File "/Users/trocher/Documents/thesis/vyper/vyper/evm/opcodes.py", line 226, in _wrapper
    return fn(*args, **kwargs)
  File "/Users/trocher/Documents/thesis/vyper/vyper/compiler/__init__.py", line 141, in compile_codes
    exc_handler(contract_name, exc)
  File "/Users/trocher/Documents/thesis/vyper/vyper/cli/vyper_compile.py", line 189, in exc_handler
    raise exception
  File "/Users/trocher/Documents/thesis/vyper/vyper/compiler/__init__.py", line 138, in compile_codes
    out[contract_name][output_format] = OUTPUT_FORMATS[output_format](compiler_data)
  File "/Users/trocher/Documents/thesis/vyper/vyper/compiler/output.py", line 82, in build_ir_output
    return compiler_data.ir_nodes
  File "/Users/trocher/Documents/thesis/vyper/vyper/compiler/phases.py", line 126, in ir_nodes
    ir, ir_runtime, sigs = self._ir_output
  File "/Library/Frameworks/Python.framework/Versions/3.8/lib/python3.8/functools.py", line 966, in __get__
    val = self.func(instance)
  File "/Users/trocher/Documents/thesis/vyper/vyper/compiler/phases.py", line 122, in _ir_output
    return generate_ir_nodes(self.global_ctx, self.no_optimize)
  File "/Users/trocher/Documents/thesis/vyper/vyper/compiler/phases.py", line 117, in global_ctx
    return GlobalContext(self.vyper_module_folded)
  File "/Users/trocher/Documents/thesis/vyper/vyper/compiler/phases.py", line 107, in vyper_module_folded
    module, storage_layout = self._folded_module
  File "/Library/Frameworks/Python.framework/Versions/3.8/lib/python3.8/functools.py", line 966, in __get__
    val = self.func(instance)
  File "/Users/trocher/Documents/thesis/vyper/vyper/compiler/phases.py", line 101, in _folded_module
    return generate_folded_ast(
  File "/Users/trocher/Documents/thesis/vyper/vyper/compiler/phases.py", line 228, in generate_folded_ast
    vy_ast.folding.fold(vyper_module_folded)
  File "/Users/trocher/Documents/thesis/vyper/vyper/ast/folding.py", line 43, in fold
    changed_nodes += replace_builtin_functions(vyper_module)
  File "/Users/trocher/Documents/thesis/vyper/vyper/ast/folding.py", line 131, in replace_builtin_functions
    new_node = func.evaluate(node)  # type: ignore
  File "/Users/trocher/Documents/thesis/vyper/vyper/builtins/functions.py", line 1030, in evaluate
    raise InvalidLiteral("Value out of range for decimal", node.args[0])
vyper.exceptions.InvalidLiteral: Value out of range for decimal
  contract "tests/customs/code.vy:3", function "test", line 3:29 
       2 def test():
  ---> 3     a:uint256 = as_wei_value(max_value(decimal), "wei")
  ------------------------------------^
       4
```


Discussion:

Resolving patch:
diff --git a/vyper/builtins/functions.py b/vyper/builtins/functions.py
--- a/vyper/builtins/functions.py
+++ b/vyper/builtins/functions.py
@@ -1026,7 +1026,7 @@ def evaluate(self, node):
 
         if isinstance(value, int) and value >= 2 ** 256:
             raise InvalidLiteral("Value out of range for uint256", node.args[0])
-        if isinstance(value, Decimal) and value >= 2 ** 127:
+        if isinstance(value, Decimal) and value > SizeLimits.MAX_AST_DECIMAL:
             raise InvalidLiteral("Value out of range for decimal", node.args[0])
 
         return vy_ast.Int.from_node(node, value=int(value * denom))
@@ -2012,7 +2012,8 @@ def evaluate(self, node):
 
         left, right = (i.value for i in node.args)
         if isinstance(left, Decimal) and (
-            min(left, right) < -(2 ** 127) or max(left, right) >= 2 ** 127
+            min(left, right) < SizeLimits.MIN_AST_DECIMAL
+            or max(left, right) > SizeLimits.MAX_AST_DECIMAL
         ):
             raise InvalidType("Decimal value is outside of allowable range", node)
         if isinstance(left, int) and (min(left, right) < 0 and max(left, right) >= 2 ** 127):


Test patch:
diff --git a/tests/builtins/folding/test_fold_as_wei_value.py b/tests/builtins/folding/test_fold_as_wei_value.py
--- a/tests/builtins/folding/test_fold_as_wei_value.py
+++ b/tests/builtins/folding/test_fold_as_wei_value.py
@@ -4,12 +4,17 @@
 
 from vyper import ast as vy_ast
 from vyper.builtins import functions as vy_fn
+from vyper.utils import SizeLimits
 
 denoms = [x for k in vy_fn.AsWeiValue.wei_denoms.keys() for x in k]
 
 
 st_decimals = st.decimals(
-    min_value=0, max_value=2 ** 32, allow_nan=False, allow_infinity=False, places=10
+    min_value=0,
+    max_value=SizeLimits.MAX_AST_DECIMAL,
+    allow_nan=False,
+    allow_infinity=False,
+    places=10,
 )
 
 
diff --git a/tests/builtins/folding/test_min_max.py b/tests/builtins/folding/test_min_max.py
--- a/tests/builtins/folding/test_min_max.py
+++ b/tests/builtins/folding/test_min_max.py
@@ -4,9 +4,14 @@
 
 from vyper import ast as vy_ast
 from vyper.builtins import functions as vy_fn
+from vyper.utils import SizeLimits
 
 st_decimals = st.decimals(
-    min_value=-(2 ** 32), max_value=2 ** 32, allow_nan=False, allow_infinity=False, places=10
+    min_value=SizeLimits.MIN_AST_DECIMAL,
+    max_value=SizeLimits.MAX_AST_DECIMAL,
+    allow_nan=False,
+    allow_infinity=False,
+    places=10,
 )
 st_int128 = st.integers(min_value=-(2 ** 127), max_value=2 ** 127 - 1)
 st_uint256 = st.integers(min_value=0, max_value=2 ** 256 - 1)