def get_type(arg): if isinstance(arg, LLLnode): return canonicalize_type(arg.typ) elif hasattr(arg, "annotation"): return canonicalize_type( parse_type(arg.annotation, None, sigs, custom_structs=custom_structs,) )
def _generate_output_abi(self, custom_units_descriptions=None): t = self.output_type if not t: return [] elif isinstance(t, TupleType): res = [(canonicalize_type(x), print_unit(unit_from_type(x), custom_units_descriptions)) for x in t.members] elif isinstance(t, TupleLike): res = [(canonicalize_type(x), print_unit(unit_from_type(x), custom_units_descriptions)) for x in t.tuple_members()] else: res = [(canonicalize_type(t), print_unit(unit_from_type(t), custom_units_descriptions))] abi_outputs = [{ "type": x, "name": "out", "unit": unit } for x, unit in res] for abi_output in abi_outputs: delete_unit_if_empty(abi_output) return abi_outputs
def get_type(arg): if isinstance(arg, LLLnode): return canonicalize_type(arg.typ) elif hasattr(arg, 'annotation'): return canonicalize_type( parse_type(arg.annotation, None, sigs, custom_units=custom_units))
def test_canonicalize_type(): # Non-basetype not allowed with raises(Exception): canonicalize_type(int) # List of byte arrays not allowed a = ListType(ByteArrayType(12), 2) with raises(Exception): canonicalize_type(a) # Test ABI format of multiple args. c = TupleType([BaseType('int128'), BaseType('address')]) assert canonicalize_type(c) == "(int128,address)"
def _generate_output_abi(self): t = self.output_type if not t: return [] elif isinstance(t, TupleType): res = [canonicalize_type(x) for x in t.members] else: res = [canonicalize_type(t)] return [{"type": x, "name": "out"} for x in res]
def to_abi_dict(self, custom_units_descriptions=None): func_type = "function" if self.name == "__init__": func_type = "constructor" if self.name == "__default__": func_type = "fallback" abi_dict = { "name": self.name, "outputs": self._generate_output_abi(custom_units_descriptions), "inputs": [{ "type": canonicalize_type(arg.typ), "name": arg.name, "unit": print_unit(unit_from_type(arg.typ), custom_units_descriptions) } for arg in self.args], "constant": self.const, "payable": self.payable, "type": func_type } for abi_input in abi_dict['inputs']: delete_unit_if_empty(abi_input) if self.name in ('__default__', '__init__'): del abi_dict['name'] if self.name == '__default__': del abi_dict['inputs'] del abi_dict['outputs'] return abi_dict
def to_abi_dict(self, custom_units_descriptions=None): abi_dict = { "name": self.name, "outputs": self._generate_output_abi(custom_units_descriptions), "inputs": [{ "type": canonicalize_type(arg.typ), "name": arg.name, "unit": print_unit(unit_from_type(arg.typ), custom_units_descriptions) } for arg in self.args], "constant": self.const, "payable": self.payable, "type": "constructor" if self.name == "__init__" else "function" } for abi_input in abi_dict['inputs']: delete_unit_if_empty(abi_input) return abi_dict
def _generate_base_type(self, arg_type, name=None, custom_units_descriptions=None): yield "type", canonicalize_type(arg_type) u = unit_from_type(arg_type) if u: yield "unit", print_unit(u, custom_units_descriptions) name = "out" if not name else name yield "name", name
def to_abi_dict(self): return { "name": self.name, "inputs": [{"type": canonicalize_type(arg.typ, self.indexed_list[pos]), "name": arg.name, "indexed": self.indexed_list[pos]} for pos, arg in enumerate(self.args)] if self.args else [], "anonymous": False, "type": "event" }
def to_abi_event_dict(self, arg, pos, custom_units_descriptions): yield "type", canonicalize_type(arg.typ, self.indexed_list[pos]), yield "name", arg.name, yield "indexed", self.indexed_list[pos], u = unit_from_type(arg.typ) if u: yield "unit", print_unit(u, custom_units_descriptions)
def from_declaration(cls, code, custom_units=None): name = code.target.id pos = 0 if not is_varname_valid(name, custom_units=custom_units): raise EventDeclarationException("Event name invalid: " + name) # Determine the arguments, expects something of the form def foo(arg1: num, arg2: num ... args = [] indexed_list = [] topics_count = 1 if code.annotation.args: keys = code.annotation.args[0].keys values = code.annotation.args[0].values for i in range(len(keys)): typ = values[i] arg = keys[i].id is_indexed = False # Check to see if argument is a topic if isinstance(typ, ast.Call) and typ.func.id == 'indexed': typ = values[i].args[0] indexed_list.append(True) topics_count += 1 is_indexed = True else: indexed_list.append(False) if isinstance(typ, ast.Subscript) and getattr( typ.value, 'id', None ) == 'bytes' and typ.slice.value.n > 32 and is_indexed: raise EventDeclarationException( "Indexed arguments are limited to 32 bytes") if topics_count > 4: raise EventDeclarationException( "Maximum of 3 topics {} given".format(topics_count - 1), arg) if not isinstance(arg, str): raise VariableDeclarationException("Argument name invalid", arg) if not typ: raise InvalidTypeException("Argument must have type", arg) if not is_varname_valid(arg, custom_units): raise VariableDeclarationException( "Argument name invalid or reserved: " + arg, arg) if arg in (x.name for x in args): raise VariableDeclarationException( "Duplicate function argument name: " + arg, arg) parsed_type = parse_type(typ, None, custom_units=custom_units) args.append(VariableRecord(arg, pos, parsed_type, False)) if isinstance(parsed_type, ByteArrayType): pos += ceil32(typ.slice.value.n) else: pos += get_size_of_type(parsed_type) * 32 sig = name + '(' + ','.join([ canonicalize_type(arg.typ, indexed_list[pos]) for pos, arg in enumerate(args) ]) + ')' # noqa F812 event_id = bytes_to_int(sha3(bytes(sig, 'utf-8'))) return cls(name, args, indexed_list, event_id, sig)
def to_abi_dict(self): return { "name": self.name, "outputs": self._generate_output_abi(), "inputs": [{"type": canonicalize_type(arg.typ), "name": arg.name} for arg in self.args], "constant": self.const, "payable": self.payable, "type": "constructor" if self.name == "__init__" else "function" }
def from_declaration(cls, class_node, global_ctx): name = class_node.name pos = 0 check_valid_varname( name, global_ctx._structs, global_ctx._constants, pos=class_node, error_prefix="Event name invalid. ", exc=EventDeclarationException, ) args = [] indexed_list = [] if len(class_node.body) != 1 or not isinstance(class_node.body[0], vy_ast.Pass): for node in class_node.body: arg_item = node.target arg = node.target.id typ = node.annotation if isinstance(typ, vy_ast.Call) and typ.get("func.id") == "indexed": indexed_list.append(True) typ = typ.args[0] else: indexed_list.append(False) check_valid_varname( arg, global_ctx._structs, global_ctx._constants, pos=arg_item, error_prefix="Event argument name invalid or reserved.", ) if arg in (x.name for x in args): raise TypeCheckFailure( f"Duplicate function argument name: {arg}") # Can struct be logged? parsed_type = global_ctx.parse_type(typ, None) args.append(VariableRecord(arg, pos, parsed_type, False)) if isinstance(parsed_type, ByteArrayType): pos += ceil32(typ.slice.value.n) else: pos += get_size_of_type(parsed_type) * 32 sig = (name + "(" + ",".join([ canonicalize_type(arg.typ, indexed_list[pos]) for pos, arg in enumerate(args) ]) + ")") # noqa F812 event_id = bytes_to_int(keccak256(bytes(sig, "utf-8"))) return cls(name, args, indexed_list, event_id, sig)
def to_abi_dict(self, custom_units_descriptions=None): abi_dict = { "name": self.name, "inputs": [{ "type": canonicalize_type(arg.typ, self.indexed_list[pos]), "name": arg.name, "indexed": self.indexed_list[pos], "unit": print_unit(unit_from_type(arg.typ), custom_units_descriptions) } for pos, arg in enumerate(self.args)] if self.args else [], "anonymous": False, "type": "event" } for abi_input in abi_dict['inputs']: delete_unit_if_empty(abi_input) return abi_dict
def from_definition( cls, code, sigs=None, custom_structs=None, interface_def=False, constant_override=False, is_from_json=False, ): if not custom_structs: custom_structs = {} name = code.name mem_pos = 0 # Determine the arguments, expects something of the form def foo(arg1: # int128, arg2: int128 ... args = [] for arg in code.args.args: # Each arg needs a type specified. typ = arg.annotation parsed_type = parse_type( typ, None, sigs, custom_structs=custom_structs, ) args.append( VariableRecord( arg.arg, mem_pos, parsed_type, False, defined_at=getpos(arg), )) if isinstance(parsed_type, ByteArrayLike): mem_pos += 32 else: mem_pos += get_size_of_type(parsed_type) * 32 mutability = "nonpayable" # Assume nonpayable by default nonreentrant_key = "" is_internal = False # Update function properties from decorators # NOTE: Can't import enums here because of circular import for dec in code.decorator_list: if isinstance(dec, vy_ast.Name) and dec.id in ("payable", "view", "pure"): mutability = dec.id elif isinstance(dec, vy_ast.Name) and dec.id == "internal": is_internal = True elif isinstance(dec, vy_ast.Name) and dec.id == "external": is_internal = False elif isinstance(dec, vy_ast.Call) and dec.func.id == "nonreentrant": nonreentrant_key = dec.args[0].s if constant_override: # In case this override is abused, match previous behavior if mutability == "payable": raise StructureException( f"Function {name} cannot be both constant and payable.") mutability = "view" # Determine the return type and whether or not it's constant. Expects something # of the form: # def foo(): ... # def foo() -> int128: ... # If there is no return type, ie. it's of the form def foo(): ... # and NOT def foo() -> type: ..., then it's null output_type = None if code.returns: output_type = parse_type( code.returns, None, sigs, custom_structs=custom_structs, ) # Output type must be canonicalizable assert isinstance(output_type, TupleType) or canonicalize_type(output_type) # Get the canonical function signature sig = cls.get_full_sig(name, code.args.args, sigs, custom_structs) # Take the first 4 bytes of the hash of the sig to get the method ID method_id = fourbytes_to_int(keccak256(bytes(sig, "utf-8"))[:4]) return cls( name, args, output_type, mutability, is_internal, nonreentrant_key, sig, method_id, code, is_from_json, )
def to_abi_event_dict(self, arg, pos): yield "type", canonicalize_type(arg.typ, self.indexed_list[pos]), yield "name", arg.name, yield "indexed", self.indexed_list[pos],
def from_definition(cls, code, sigs=None, custom_units=None, contract_def=False, constant=False): name = code.name pos = 0 if not is_varname_valid(name, custom_units=custom_units): raise FunctionDeclarationException("Function name invalid: " + name) # Determine the arguments, expects something of the form def foo(arg1: int128, arg2: int128 ... args = [] for arg in code.args.args: typ = arg.annotation if not typ: raise InvalidTypeException("Argument must have type", arg) if not is_varname_valid(arg.arg, custom_units=custom_units): raise FunctionDeclarationException( "Argument name invalid or reserved: " + arg.arg, arg) if arg.arg in (x.name for x in args): raise FunctionDeclarationException( "Duplicate function argument name: " + arg.arg, arg) parsed_type = parse_type(typ, None, sigs, custom_units=custom_units) args.append(VariableRecord(arg.arg, pos, parsed_type, False)) if isinstance(parsed_type, ByteArrayType): pos += 32 else: pos += get_size_of_type(parsed_type) * 32 # Apply decorators const, payable, private, public = False, False, False, False for dec in code.decorator_list: if isinstance(dec, ast.Name) and dec.id == "constant": const = True elif isinstance(dec, ast.Name) and dec.id == "payable": payable = True elif isinstance(dec, ast.Name) and dec.id == "private": private = True elif isinstance(dec, ast.Name) and dec.id == "public": public = True else: raise StructureException("Bad decorator", dec) if public and private: raise StructureException( "Cannot use public and private decorators on the same function: {}" .format(name)) if payable and const: raise StructureException( "Function {} cannot be both constant and payable.".format( name)) if payable and private: raise StructureException( "Function {} cannot be both private and payable.".format(name)) if (not public and not private) and not contract_def: raise StructureException( "Function visibility must be declared (@public or @private)", code) if constant: const = True # Determine the return type and whether or not it's constant. Expects something # of the form: # def foo(): ... # def foo() -> int128: ... # If there is no return type, ie. it's of the form def foo(): ... # and NOT def foo() -> type: ..., then it's null if not code.returns: output_type = None elif isinstance( code.returns, (ast.Name, ast.Compare, ast.Subscript, ast.Call, ast.Tuple)): output_type = parse_type(code.returns, None, sigs, custom_units=custom_units) else: raise InvalidTypeException( "Output type invalid or unsupported: %r" % parse_type(code.returns, None), code.returns, ) # Output type must be canonicalizable if output_type is not None: assert isinstance(output_type, TupleType) or canonicalize_type(output_type) # Get the canonical function signature sig = cls.get_full_sig(name, code.args.args, sigs, custom_units) # Take the first 4 bytes of the hash of the sig to get the method ID method_id = fourbytes_to_int(sha3(bytes(sig, 'utf-8'))[:4]) return cls(name, args, output_type, const, payable, private, sig, method_id, custom_units)
def from_definition(cls, code, sigs=None, custom_units=None, custom_structs=None, contract_def=False, constants=None, constant=False): if not custom_structs: custom_structs = {} name = code.name mem_pos = 0 valid_name, msg = is_varname_valid(name, custom_units, custom_structs, constants) if not valid_name and (not name.lower() in function_whitelist): raise FunctionDeclarationException("Function name invalid. " + msg, code) # Validate default values. for default_value in getattr(code.args, 'defaults', []): allowed_types = (ast.Num, ast.Str, ast.Bytes, ast.List, ast.NameConstant) if not isinstance(default_value, allowed_types): raise FunctionDeclarationException( "Default parameter values have to be literals.") # Determine the arguments, expects something of the form def foo(arg1: # int128, arg2: int128 ... args = [] for arg in code.args.args: # Each arg needs a type specified. typ = arg.annotation if not typ: raise InvalidTypeException("Argument must have type", arg) # Validate arg name. check_valid_varname( arg.arg, custom_units, custom_structs, constants, arg, "Argument name invalid or reserved. ", FunctionDeclarationException, ) # Check for duplicate arg name. if arg.arg in (x.name for x in args): raise FunctionDeclarationException( "Duplicate function argument name: " + arg.arg, arg, ) parsed_type = parse_type( typ, None, sigs, custom_units=custom_units, custom_structs=custom_structs, constants=constants, ) args.append( VariableRecord( arg.arg, mem_pos, parsed_type, False, defined_at=getpos(arg), )) if isinstance(parsed_type, ByteArrayLike): mem_pos += 32 else: mem_pos += get_size_of_type(parsed_type) * 32 # Apply decorators const, payable, private, public, nonreentrant_key = False, False, False, False, '' for dec in code.decorator_list: if isinstance(dec, ast.Name) and dec.id == "constant": const = True elif isinstance(dec, ast.Name) and dec.id == "payable": payable = True elif isinstance(dec, ast.Name) and dec.id == "private": private = True elif isinstance(dec, ast.Name) and dec.id == "public": public = True elif isinstance(dec, ast.Call) and dec.func.id == "nonreentrant": if dec.args and len(dec.args) == 1 and isinstance( dec.args[0], ast.Str) and dec.args[0].s: # noqa: E501 nonreentrant_key = dec.args[0].s else: raise StructureException( "@nonreentrant decorator requires a non-empty string to use as a key.", dec) else: raise StructureException("Bad decorator", dec) if public and private: raise StructureException( "Cannot use public and private decorators on the same function: {}" .format(name)) if payable and const: raise StructureException( "Function {} cannot be both constant and payable.".format( name)) if payable and private: raise StructureException( "Function {} cannot be both private and payable.".format(name)) if (not public and not private) and not contract_def: raise StructureException( "Function visibility must be declared (@public or @private)", code, ) if constant and nonreentrant_key: raise StructureException( "@nonreentrant makes no sense on a @constant function.", code) if constant: const = True # Determine the return type and whether or not it's constant. Expects something # of the form: # def foo(): ... # def foo() -> int128: ... # If there is no return type, ie. it's of the form def foo(): ... # and NOT def foo() -> type: ..., then it's null if not code.returns: output_type = None elif isinstance( code.returns, (ast.Name, ast.Compare, ast.Subscript, ast.Call, ast.Tuple)): output_type = parse_type( code.returns, None, sigs, custom_units=custom_units, custom_structs=custom_structs, constants=constants, ) else: raise InvalidTypeException( "Output type invalid or unsupported: %r" % parse_type(code.returns, None), code.returns, ) # Output type must be canonicalizable if output_type is not None: assert isinstance(output_type, TupleType) or canonicalize_type(output_type) # Get the canonical function signature sig = cls.get_full_sig(name, code.args.args, sigs, custom_units, custom_structs, constants) # Take the first 4 bytes of the hash of the sig to get the method ID method_id = fourbytes_to_int(keccak256(bytes(sig, 'utf-8'))[:4]) return cls(name, args, output_type, const, payable, private, nonreentrant_key, sig, method_id, custom_units, code)
def from_declaration(cls, code, global_ctx): name = code.target.id pos = 0 check_valid_varname(name, global_ctx._custom_units, global_ctx._structs, global_ctx._constants, pos=code, error_prefix="Event name invalid. ", exc=EventDeclarationException) # Determine the arguments, expects something of the form def foo(arg1: num, arg2: num ... args = [] indexed_list = [] topics_count = 1 if code.annotation.args: keys = code.annotation.args[0].keys values = code.annotation.args[0].values for i in range(len(keys)): typ = values[i] if not isinstance(keys[i], ast.Name): raise EventDeclarationException( 'Invalid key type, expected a valid name.', keys[i], ) if not isinstance(typ, (ast.Name, ast.Call, ast.Subscript)): raise EventDeclarationException( 'Invalid event argument type.', typ) if isinstance(typ, ast.Call) and not isinstance(typ.func, ast.Name): raise EventDeclarationException( 'Invalid event argument type', typ) arg = keys[i].id arg_item = keys[i] is_indexed = False # Check to see if argument is a topic if isinstance(typ, ast.Call) and typ.func.id == 'indexed': typ = values[i].args[0] indexed_list.append(True) topics_count += 1 is_indexed = True else: indexed_list.append(False) if isinstance(typ, ast.Subscript) and getattr( typ.value, 'id', None ) == 'bytes' and typ.slice.value.n > 32 and is_indexed: # noqa: E501 raise EventDeclarationException( "Indexed arguments are limited to 32 bytes") if topics_count > 4: raise EventDeclarationException( f"Maximum of 3 topics {topics_count - 1} given", arg, ) if not isinstance(arg, str): raise VariableDeclarationException("Argument name invalid", arg) if not typ: raise InvalidTypeException("Argument must have type", arg) check_valid_varname( arg, global_ctx._custom_units, global_ctx._structs, global_ctx._constants, pos=arg_item, error_prefix="Event argument name invalid or reserved.", ) if arg in (x.name for x in args): raise VariableDeclarationException( "Duplicate function argument name: " + arg, arg_item, ) # Can struct be logged? parsed_type = global_ctx.parse_type(typ, None) args.append(VariableRecord(arg, pos, parsed_type, False)) if isinstance(parsed_type, ByteArrayType): pos += ceil32(typ.slice.value.n) else: pos += get_size_of_type(parsed_type) * 32 sig = name + '(' + ','.join([ canonicalize_type(arg.typ, indexed_list[pos]) for pos, arg in enumerate(args) ]) + ')' # noqa F812 event_id = bytes_to_int(keccak256(bytes(sig, 'utf-8'))) return cls(name, args, indexed_list, event_id, sig)
def from_definition( cls, code, sigs=None, custom_structs=None, interface_def=False, constants=None, constant_override=False, is_from_json=False, ): if not custom_structs: custom_structs = {} name = code.name mem_pos = 0 valid_name, msg = is_varname_valid(name, custom_structs, constants) if not valid_name and (not name.lower() in FUNCTION_WHITELIST): raise FunctionDeclarationException("Function name invalid. " + msg, code) # Validate default values. for default_value in getattr(code.args, "defaults", []): validate_default_values(default_value) # Determine the arguments, expects something of the form def foo(arg1: # int128, arg2: int128 ... args = [] for arg in code.args.args: # Each arg needs a type specified. typ = arg.annotation if not typ: raise InvalidType("Argument must have type", arg) # Validate arg name. check_valid_varname( arg.arg, custom_structs, constants, arg, "Argument name invalid or reserved. ", FunctionDeclarationException, ) # Check for duplicate arg name. if arg.arg in (x.name for x in args): raise FunctionDeclarationException( "Duplicate function argument name: " + arg.arg, arg, ) parsed_type = parse_type( typ, None, sigs, custom_structs=custom_structs, constants=constants, ) args.append( VariableRecord(arg.arg, mem_pos, parsed_type, False, defined_at=getpos(arg),) ) if isinstance(parsed_type, ByteArrayLike): mem_pos += 32 else: mem_pos += get_size_of_type(parsed_type) * 32 mutability = "nonpayable" # Assume nonpayable by default internal = False external = False nonreentrant_key = "" # Update function properties from decorators # NOTE: Can't import enums here because of circular import for dec in code.decorator_list: if isinstance(dec, vy_ast.Name) and dec.id in ("payable", "view", "pure"): mutability = dec.id elif isinstance(dec, vy_ast.Name) and dec.id == "internal": internal = True elif isinstance(dec, vy_ast.Name) and dec.id == "external": external = True elif isinstance(dec, vy_ast.Call) and dec.func.id == "nonreentrant": if nonreentrant_key: raise StructureException( "Only one @nonreentrant decorator allowed per function", dec ) if ( dec.args and len(dec.args) == 1 and isinstance(dec.args[0], vy_ast.Str) and dec.args[0].s ): # noqa: E501 nonreentrant_key = dec.args[0].s else: raise StructureException( "@nonreentrant decorator requires a non-empty string to use as a key.", dec ) else: raise StructureException("Bad decorator", dec) if constant_override: # In case this override is abused, match previous behavior if mutability == "payable": raise StructureException(f"Function {name} cannot be both constant and payable.") mutability = "view" if external and internal: raise StructureException( f"Cannot use external and internal decorators on the same function: {name}" ) if mutability == "payable" and internal: raise StructureException(f"Function {name} cannot be both internal and payable.") if (not external and not internal) and not interface_def: raise StructureException( "Function visibility must be declared (@external or @internal)", code, ) if mutability in ("view", "pure") and nonreentrant_key: raise StructureException( f"@nonreentrant makes no sense on a @{mutability} function.", code ) # Determine the return type and whether or not it's constant. Expects something # of the form: # def foo(): ... # def foo() -> int128: ... # If there is no return type, ie. it's of the form def foo(): ... # and NOT def foo() -> type: ..., then it's null if not code.returns: output_type = None elif isinstance( code.returns, (vy_ast.Name, vy_ast.Compare, vy_ast.Subscript, vy_ast.Call, vy_ast.Tuple) ): output_type = parse_type( code.returns, None, sigs, custom_structs=custom_structs, constants=constants, ) else: raise InvalidType( f"Output type invalid or unsupported: {parse_type(code.returns, None)}", code.returns, ) # Output type must be canonicalizable if output_type is not None: assert isinstance(output_type, TupleType) or canonicalize_type(output_type) # Get the canonical function signature sig = cls.get_full_sig(name, code.args.args, sigs, custom_structs, constants) # Take the first 4 bytes of the hash of the sig to get the method ID method_id = fourbytes_to_int(keccak256(bytes(sig, "utf-8"))[:4]) return cls( name, args, output_type, mutability, internal, nonreentrant_key, sig, method_id, code, is_from_json, )
def _generate_base_type(self, arg_type, name=""): yield "type", canonicalize_type(arg_type) yield "name", name