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 make_struct(self, name, body): members = [] for item in body: if isinstance(item, ast.AnnAssign): member_name = item.target member_type = item.annotation # Check well-formedness of member names if not (isinstance(member_name, ast.Name) and is_varname_valid(member_name.id, custom_units=self._custom_units, custom_structs=self._structs)): raise InvalidTypeException( "Invalid member name for struct %r" % name, item) # Check well-formedness of member types # Note this kicks out mutually recursive structs, # raising an exception instead of stackoverflow. # A struct must be defined before it is referenced. # This feels like a semantic step and maybe should be pushed # to a later compilation stage. parse_type(member_type, 'storage', custom_units=self._custom_units, custom_structs=self._structs) members.append((member_name, member_type)) else: raise StructureException("Structs can only contain variables", item) return members
def new_variable(self, name, typ): if not is_varname_valid(name, custom_units=self.custom_units): raise VariableDeclarationException("Variable name invalid or reserved: " + name) if name in self.vars or name in self.globals: raise VariableDeclarationException("Duplicate variable name: %s" % name) self.vars[name] = VariableRecord(name, self.next_mem, typ, True, self.blockscopes.copy()) pos = self.next_mem self.next_mem += 32 * get_size_of_type(typ) return pos
def struct_literals(self): o = {} members = {} for key, value in zip(self.expr.keys, self.expr.values): if not isinstance(key, ast.Name) or not is_varname_valid(key.id, self.context.custom_units): raise TypeMismatchException("Invalid member variable for struct: %r" % vars(key).get('id', key), key) if key.id in o: raise TypeMismatchException("Member variable duplicated: " + key.id, key) o[key.id] = Expr(value, self.context).lll_node members[key.id] = o[key.id].typ return LLLnode.from_list(["multi"] + [o[key] for key in sorted(list(o.keys()))], typ=StructType(members), pos=getpos(self.expr))
def is_valid_varname(self, name, item): if not is_varname_valid(name, custom_units=self._custom_units, custom_structs=self._structs): raise VariableDeclarationException('Invalid name "%s"' % name, item) if name in self._globals: raise VariableDeclarationException('Invalid name "%s", previously defined as global.' % name, item) if name in self._constants: raise VariableDeclarationException('Invalid name "%s", previously defined as constant.' % name, item) if name in self._custom_units: raise VariableDeclarationException('Invalid name "%s", previously defined as custom unit.' % name, item) return True
def make_struct_type(name, location, members, custom_units, custom_structs): o = OrderedDict() for key, value in members: if not isinstance(key, ast.Name) or not is_varname_valid( key.id, custom_units, custom_structs): raise InvalidTypeException( "Invalid member variable for struct %r" % key.id, key) o[key.id] = parse_type(value, location, custom_units=custom_units, custom_structs=custom_structs) return StructType(o, name)
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 add_globals_and_events(self, item): item_attributes = {"public": False} # Handle constants. if isinstance(item.annotation, ast.Call) and item.annotation.func.id == "constant": self.add_constant(item) return # Handle events. if not (isinstance(item.annotation, ast.Call) and item.annotation.func.id == "event"): item_name, item_attributes = self.get_item_name_and_attributes( item, item_attributes) if not all([ attr in valid_global_keywords for attr in item_attributes.keys() ]): raise StructureException( 'Invalid global keyword used: %s' % item_attributes, item) if item.value is not None: raise StructureException( 'May not assign value whilst defining type', item) elif isinstance(item.annotation, ast.Call) and item.annotation.func.id == "event": if self._globals or len(self._defs): raise EventDeclarationException( "Events must all come before global declarations and function definitions", item) self._events.append(item) elif not isinstance(item.target, ast.Name): raise StructureException( "Can only assign type to variable in top-level statement", item) # Is this a custom unit definition. elif item.target.id == 'units': if not self._custom_units: if not isinstance(item.annotation, ast.Dict): raise VariableDeclarationException( "Define custom units using units: { }.", item.target) for key, value in zip(item.annotation.keys, item.annotation.values): if not isinstance(value, ast.Str): raise VariableDeclarationException( "Custom unit description must be a valid string", value) if not isinstance(key, ast.Name): raise VariableDeclarationException( "Custom unit name must be a valid string", key) if key.id in self._custom_units: raise VariableDeclarationException( "Custom unit name may only be used once", key) if not is_varname_valid(key.id, custom_units=self._custom_units, custom_structs=self._structs): raise VariableDeclarationException( "Custom unit may not be a reserved keyword", key) self._custom_units.append(key.id) self._custom_units_descriptions[key.id] = value.s else: raise VariableDeclarationException( "Custom units can only be defined once", item.target) # Check if variable name is valid. elif not self.is_valid_varname(item.target.id, item): pass elif len(self._defs): raise StructureException( "Global variables must all come before function definitions", item) # If the type declaration is of the form public(<type here>), then proceed with # the underlying type but also add getters elif isinstance(item.annotation, ast.Call) and item.annotation.func.id == "address": if item.annotation.args[0].id not in premade_contracts: raise VariableDeclarationException( "Unsupported premade contract declaration", item.annotation.args[0]) premade_contract = premade_contracts[item.annotation.args[0].id] self._contracts[item.target.id] = self.make_contract( premade_contract.body) self._globals[item.target.id] = VariableRecord( item.target.id, len(self._globals), BaseType('address'), True) elif item_name in self._contracts: self._globals[item.target.id] = ContractRecord( item.target.id, len(self._globals), ContractType(item_name), True) if item_attributes["public"]: typ = ContractType(item_name) for getter in self.mk_getter(item.target.id, typ): self._getters.append( self.parse_line('\n' * (item.lineno - 1) + getter)) self._getters[-1].pos = getpos(item) elif isinstance(item.annotation, ast.Call) and item.annotation.func.id == "public": if isinstance(item.annotation.args[0], ast.Name) and item_name in self._contracts: typ = ContractType(item_name) else: typ = parse_type(item.annotation.args[0], 'storage', custom_units=self._custom_units, custom_structs=self._structs) self._globals[item.target.id] = VariableRecord( item.target.id, len(self._globals), typ, True) # Adding getters here for getter in self.mk_getter(item.target.id, typ): self._getters.append( self.parse_line('\n' * (item.lineno - 1) + getter)) self._getters[-1].pos = getpos(item) else: self._globals[item.target.id] = VariableRecord( item.target.id, len(self._globals), parse_type(item.annotation, 'storage', custom_units=self._custom_units, custom_structs=self._structs), True)
def parse_type(item, location, sigs=None, custom_units=None): custom_units = [] if custom_units is None else custom_units sigs = {} if sigs is None else sigs # Base types, e.g. num if isinstance(item, ast.Name): if item.id in base_types: return BaseType(item.id) elif item.id in special_types: return special_types[item.id] else: raise InvalidTypeException("Invalid base type: " + item.id, item) # Units, e.g. num (1/sec) or contracts elif isinstance(item, ast.Call): # Contract_types if item.func.id == 'contract' or item.func.id == 'address': if sigs and item.args[0].id in sigs: return BaseType('address', item.args[0].id) else: raise InvalidTypeException('Invalid contract declaration') if not isinstance(item.func, ast.Name): raise InvalidTypeException("Malformed unit type:", item) base_type = item.func.id if base_type not in ('int128', 'uint256', 'decimal'): raise InvalidTypeException( "You must use int128, decimal, address, contract, \ for variable declarations and indexed for logging topics ", item) if len(item.args) == 0: raise InvalidTypeException("Malformed unit type", item) if isinstance(item.args[-1], ast.Name) and item.args[-1].id == "positional": positional = True argz = item.args[:-1] else: positional = False argz = item.args if len(argz) != 1: raise InvalidTypeException("Malformed unit type", item) # Check for uint256 to num casting if item.func.id == 'int128' and getattr(item.args[0], 'id', '') == 'uint256': return BaseType('int128', override_signature='uint256') unit = parse_unit(argz[0], custom_units=custom_units) return BaseType(base_type, unit, positional) # Subscripts elif isinstance(item, ast.Subscript): if 'value' not in vars(item.slice): raise InvalidTypeException( "Array / ByteArray access must access a single element, not a slice", item) # Fixed size lists or bytearrays, e.g. num[100] elif isinstance(item.slice.value, ast.Num): if not isinstance(item.slice.value.n, int) or item.slice.value.n <= 0: raise InvalidTypeException( "Arrays / ByteArrays must have a positive integral number of elements", item.slice.value) # ByteArray if getattr(item.value, 'id', None) == 'bytes': return ByteArrayType(item.slice.value.n) # List else: return ListType( parse_type(item.value, location, custom_units=custom_units), item.slice.value.n) # Mappings, e.g. num[address] else: if location == 'memory': raise InvalidTypeException( "No mappings allowed for in-memory types, only fixed-size arrays", item) keytype = parse_type(item.slice.value, None) if not isinstance(keytype, (BaseType, ByteArrayType)): raise InvalidTypeException( "Mapping keys must be base or bytes types", item.slice.value) return MappingType( keytype, parse_type(item.value, location, custom_units=custom_units)) # Dicts, used to represent mappings, e.g. {uint: uint}. Key must be a base type elif isinstance(item, ast.Dict): o = {} for key, value in zip(item.keys, item.values): if not isinstance(key, ast.Name) or not is_varname_valid( key.id, custom_units): raise InvalidTypeException( "Invalid member variable for struct", key) o[key.id] = parse_type(value, location, custom_units=custom_units) return StructType(o) elif isinstance(item, ast.Tuple): members = [ parse_type(x, location, custom_units=custom_units) for x in item.elts ] return TupleType(members) else: raise InvalidTypeException("Invalid type: %r" % ast.dump(item), item)
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 add_globals_and_events(_custom_units, _contracts, _defs, _events, _getters, _globals, item): item_attributes = {"public": False, "modifiable": False, "static": False} if not (isinstance(item.annotation, ast.Call) and item.annotation.func.id == "event"): item_name, item_attributes = get_item_name_and_attributes(item, item_attributes) if not all([attr in valid_global_keywords for attr in item_attributes.keys()]): raise StructureException('Invalid global keyword used: %s' % item_attributes, item) if item.value is not None: raise StructureException('May not assign value whilst defining type', item) elif isinstance(item.annotation, ast.Call) and item.annotation.func.id == "event": if _globals or len(_defs): raise StructureException("Events must all come before global declarations and function definitions", item) _events.append(item) elif not isinstance(item.target, ast.Name): raise StructureException("Can only assign type to variable in top-level statement", item) # Is this a custom unit definition. elif item.target.id == 'units': if not _custom_units: if not isinstance(item.annotation, ast.Dict): raise VariableDeclarationException("Define custom units using units: { }.", item.target) for key, value in zip(item.annotation.keys, item.annotation.values): if not isinstance(value, ast.Str): raise VariableDeclarationException("Custom unit description must be a valid string.", value) if not isinstance(key, ast.Name): raise VariableDeclarationException("Custom unit name must be a valid string unquoted string.", key) if key.id in _custom_units: raise VariableDeclarationException("Custom unit may only be defined once", key) if not is_varname_valid(key.id, custom_units=_custom_units): raise VariableDeclarationException("Custom unit may not be a reserved keyword", key) _custom_units.append(key.id) else: raise VariableDeclarationException("Can units can only defined once.", item.target) # Check if variable name is reserved or invalid elif not is_varname_valid(item.target.id, custom_units=_custom_units): raise VariableDeclarationException("Variable name invalid or reserved: ", item.target) # Check if global already exists, if so error elif item.target.id in _globals: raise VariableDeclarationException("Cannot declare a persistent variable twice!", item.target) elif len(_defs): raise StructureException("Global variables must all come before function definitions", item) # If the type declaration is of the form public(<type here>), then proceed with # the underlying type but also add getters elif isinstance(item.annotation, ast.Call) and item.annotation.func.id == "address": if item.annotation.args[0].id not in premade_contracts: raise VariableDeclarationException("Unsupported premade contract declaration", item.annotation.args[0]) premade_contract = premade_contracts[item.annotation.args[0].id] _contracts[item.target.id] = add_contract(premade_contract.body) _globals[item.target.id] = VariableRecord(item.target.id, len(_globals), BaseType('address'), True) elif item_name in _contracts: if not item_attributes["modifiable"] and not item_attributes["static"]: raise StructureException("All contracts must have `modifiable` or `static` keywords: %s" % item_attributes) _globals[item.target.id] = ContractRecord(item_attributes["modifiable"], item.target.id, len(_globals), ContractType(item_name), True) if item_attributes["public"]: typ = ContractType(item_name) for getter in mk_getter(item.target.id, typ): _getters.append(parse_line('\n' * (item.lineno - 1) + getter)) _getters[-1].pos = getpos(item) elif isinstance(item.annotation, ast.Call) and item.annotation.func.id == "public": if isinstance(item.annotation.args[0], ast.Name) and item_name in _contracts: typ = ContractType(item_name) else: typ = parse_type(item.annotation.args[0], 'storage', custom_units=_custom_units) _globals[item.target.id] = VariableRecord(item.target.id, len(_globals), typ, True) # Adding getters here for getter in mk_getter(item.target.id, typ): _getters.append(parse_line('\n' * (item.lineno - 1) + getter)) _getters[-1].pos = getpos(item) else: _globals[item.target.id] = VariableRecord( item.target.id, len(_globals), parse_type(item.annotation, 'storage', custom_units=_custom_units), True ) return _custom_units, _contracts, _events, _globals, _getters
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 add_globals_and_events(_contracts, _defs, _events, _getters, _globals, item): if item.value is not None: raise StructureException('May not assign value whilst defining type', item) elif isinstance(item.annotation, ast.Call) and item.annotation.func.id == "event": if _globals or len(_defs): raise StructureException( "Events must all come before global declarations and function definitions", item) _events.append(item) elif not isinstance(item.target, ast.Name): raise StructureException( "Can only assign type to variable in top-level statement", item) # Check if variable name is reserved or invalid elif not is_varname_valid(item.target.id): raise VariableDeclarationException( "Variable name invalid or reserved: ", item.target) # Check if global already exists, if so error elif item.target.id in _globals: raise VariableDeclarationException( "Cannot declare a persistent variable twice!", item.target) elif len(_defs): raise StructureException( "Global variables must all come before function definitions", item) # If the type declaration is of the form public(<type here>), then proceed with # the underlying type but also add getters elif isinstance(item.annotation, ast.Call) and item.annotation.func.id == "address": if len(item.annotation.args) != 1: raise StructureException("Address expects one arg (the type)") if item.annotation.args[0].id not in premade_contracts: raise VariableDeclarationException( "Unsupported premade contract declaration", item.annotation.args[0]) premade_contract = premade_contracts[item.annotation.args[0].id] _contracts[item.target.id] = add_contract(premade_contract.body) _globals[item.target.id] = VariableRecord(item.target.id, len(_globals), BaseType('address'), True) elif isinstance(item, ast.AnnAssign) and isinstance( item.annotation, ast.Name) and item.annotation.id in _contracts: _globals[item.target.id] = VariableRecord( item.target.id, len(_globals), BaseType('address', item.annotation.id), True) elif isinstance(item.annotation, ast.Call) and item.annotation.func.id == "public": if len(item.annotation.args) != 1: raise StructureException("Public expects one arg (the type)") if isinstance(item.annotation.args[0], ast.Name) and item.annotation.args[0].id in _contracts: typ = BaseType('address', item.annotation.args[0].id) else: typ = parse_type(item.annotation.args[0], 'storage') _globals[item.target.id] = VariableRecord(item.target.id, len(_globals), typ, True) # Adding getters here for getter in mk_getter(item.target.id, typ): _getters.append(parse_line('\n' * (item.lineno - 1) + getter)) _getters[-1].pos = getpos(item) else: _globals[item.target.id] = VariableRecord( item.target.id, len(_globals), parse_type(item.annotation, 'storage'), True) return _contracts, _events, _globals, _getters