class EthereumTxWithTransfersResponseSerializer(serializers.Serializer): class Meta: model = EthereumTx exclude = ('block', ) _from = EthereumAddressField(allow_null=False, allow_zero_address=True, source='_from') to = EthereumAddressField(allow_null=True, allow_zero_address=True) data = HexadecimalField() tx_hash = HexadecimalField() block_number = serializers.SerializerMethodField() block_timestamp = serializers.SerializerMethodField() transfers = TransferResponseSerializer(many=True) tx_type = serializers.SerializerMethodField() def get_tx_type(self, obj): return TxType.ETHEREUM_TRANSACTION.name def get_fields(self): result = super().get_fields() # Rename `_from` to `from` _from = result.pop('_from') result['from'] = _from return result def get_block_number(self, obj: EthereumTx): if obj.block: return obj.block.number def get_block_timestamp(self, obj: EthereumTx): if obj.block: return obj.block.timestamp
class SafeResponseSerializer(serializers.Serializer): address = EthereumAddressField() master_copy = EthereumAddressField() nonce = serializers.IntegerField(min_value=0) threshold = serializers.IntegerField(min_value=1) owners = serializers.ListField(child=EthereumAddressField(), min_length=1) version = serializers.CharField()
class EthereumTxWithTransfersResponseSerializer(serializers.Serializer): class Meta: model = EthereumTx exclude = ("block", ) execution_date = serializers.DateTimeField() _from = EthereumAddressField(allow_null=False, allow_zero_address=True, source="_from") to = EthereumAddressField(allow_null=True, allow_zero_address=True) data = HexadecimalField() tx_hash = HexadecimalField() block_number = serializers.SerializerMethodField() transfers = TransferWithTokenInfoResponseSerializer(many=True) tx_type = serializers.SerializerMethodField() def get_tx_type(self, obj) -> str: return TxType.ETHEREUM_TRANSACTION.name def get_fields(self): result = super().get_fields() # Rename `_from` to `from` _from = result.pop("_from") result["from"] = _from return result def get_block_number(self, obj: EthereumTx) -> Optional[int]: if obj.block_id: return obj.block_id
class TransferResponseSerializer(serializers.Serializer): type = serializers.SerializerMethodField() execution_date = serializers.DateTimeField() block_number = serializers.IntegerField(source="block") transaction_hash = Sha3HashField() to = EthereumAddressField() from_ = EthereumAddressField(source="_from", allow_zero_address=True) value = serializers.CharField(allow_null=True, source="_value") token_id = serializers.CharField(allow_null=True, source="_token_id") token_address = EthereumAddressField(allow_null=True, default=None) def get_fields(self): result = super().get_fields() # Rename `from_` to `from` from_ = result.pop("from_") result["from"] = from_ return result def get_type(self, obj: TransferDict) -> str: if obj["token_address"] is None: return TransferType.ETHER_TRANSFER.name else: if obj["_value"] is not None: return TransferType.ERC20_TRANSFER.name elif obj["_token_id"] is not None: return TransferType.ERC721_TRANSFER.name else: return TransferType.UNKNOWN.name def validate(self, data): super().validate(data) if data["value"] is None and data["token_id"] is None: raise ValidationError("Both value and token_id cannot be null") return data
class DelegateDeleteSerializer(DelegateSignatureCheckerMixin, serializers.Serializer): delegate = EthereumAddressField() delegator = EthereumAddressField() signature = HexadecimalField(min_length=65) def validate(self, data): super().validate(data) signature = data["signature"] delegate = data["delegate"] # Delegate address to be added/removed delegator = data["delegator"] # Delegator ethereum_client = EthereumClientProvider() # Tries to find a valid delegator using multiple strategies for operation_hash in DelegateSignatureHelper.calculate_all_possible_hashes( delegate): for signer in (delegate, delegator): if self.check_delegate_signature(ethereum_client, signature, operation_hash, signer): return data raise ValidationError( f"Signature does not match provided delegate={delegate} or delegator={delegator}" )
class TransferResponseSerializer(serializers.Serializer): type = serializers.SerializerMethodField() execution_date = serializers.DateTimeField() block_number = serializers.IntegerField() transaction_hash = Sha3HashField() to = EthereumAddressField() from_ = EthereumAddressField(source='_from') value = serializers.CharField() token_id = serializers.CharField() token_address = EthereumAddressField(allow_null=True, default=None) def get_fields(self): result = super().get_fields() # Rename `from_` to `from` from_ = result.pop('from_') result['from'] = from_ return result def get_type(self, obj: Dict[str, Any]) -> str: if not obj.get('token_address'): return TransferType.ETHER_TRANSFER.name else: if obj.get('value') is not None: return TransferType.ERC20_TRANSFER.name elif obj.get('token_id') is not None: return TransferType.ERC721_TRANSFER.name return TransferType.UNKNOWN
class InternalTxSerializer(serializers.ModelSerializer): class Meta: model = InternalTx exclude = ('id', ) _from = EthereumAddressField(allow_null=False, allow_zero_address=True, source='_from') to = EthereumAddressField(allow_null=True, allow_zero_address=True) contract_address = EthereumAddressField(allow_null=True, allow_zero_address=True) data = HexadecimalField() code = HexadecimalField() output = HexadecimalField() tx_type = serializers.SerializerMethodField() call_type = serializers.SerializerMethodField() ethereum_tx = None def get_fields(self): result = super().get_fields() # Rename `_from` to `from` _from = result.pop('_from') result['from'] = _from return result def get_tx_type(self, obj) -> str: return EthereumTxType(obj.tx_type).name def get_call_type(self, obj) -> str: if obj.call_type is None: return None else: return EthereumTxCallType(obj.call_type).name
class SafeMultisigEstimateTxSerializer(serializers.Serializer): safe = EthereumAddressField() to = EthereumAddressField() value = serializers.IntegerField(min_value=0) data = HexadecimalField(default=None, allow_null=True, allow_blank=True) operation = serializers.IntegerField(min_value=0) gas_token = EthereumAddressField( default=None, allow_null=True, allow_zero_address=True ) def validate_operation(self, value): try: return SafeOperation(value).value except ValueError: raise ValidationError("Unknown operation") def validate(self, data): super().validate(data) if not data["to"] and not data["data"]: raise ValidationError("`data` and `to` cannot both be null") if not data["to"] and not data["data"]: raise ValidationError("`data` and `to` cannot both be null") if data["operation"] == SafeOperation.CREATE.value: raise ValidationError( "Operation CREATE not supported. Please use Gnosis Safe CreateLib" ) # if data['to']: # raise ValidationError('Operation is Create, but `to` was provided') # elif not data['data']: # raise ValidationError('Operation is Create, but not `data` was provided') return data
class SafeMultisigTxResponseSerializer(serializers.Serializer): to = EthereumAddressField(allow_null=True, allow_zero_address=True) ethereum_tx = EthereumTxSerializer() value = serializers.IntegerField(min_value=0) data = HexadecimalField() timestamp = serializers.DateTimeField(source='created') operation = serializers.SerializerMethodField() safe_tx_gas = serializers.IntegerField(min_value=0) data_gas = serializers.IntegerField(min_value=0) gas_price = serializers.IntegerField(min_value=0) gas_token = EthereumAddressField(allow_null=True, allow_zero_address=True) refund_receiver = EthereumAddressField(allow_null=True, allow_zero_address=True) nonce = serializers.IntegerField(min_value=0) safe_tx_hash = Sha3HashField() tx_hash = serializers.SerializerMethodField() transaction_hash = serializers.SerializerMethodField( method_name='get_tx_hash') # Retro compatibility def get_operation(self, obj): """ Filters confirmations queryset :param obj: MultisigConfirmation instance :return: serialized queryset """ return SafeOperation(obj.operation).name def get_tx_hash(self, obj): tx_hash = obj.ethereum_tx.tx_hash if tx_hash and isinstance(tx_hash, bytes): return tx_hash.hex() return tx_hash
class EthereumTxSerializer(serializers.ModelSerializer): class Meta: model = EthereumTx exclude = ('block', ) _from = EthereumAddressField(allow_null=False, allow_zero_address=True, source='_from') to = EthereumAddressField(allow_null=True, allow_zero_address=True) data = HexadecimalField() tx_hash = HexadecimalField() block_number = serializers.SerializerMethodField() block_timestamp = serializers.SerializerMethodField() def get_fields(self): result = super().get_fields() # Rename `_from` to `from` _from = result.pop('_from') result['from'] = _from return result def get_block_number(self, obj: EthereumTx): if obj.block: return obj.block.number def get_block_timestamp(self, obj: EthereumTx): if obj.block: return obj.block.timestamp
class SafeCreationInfoResponseSerializer(serializers.Serializer): created = serializers.DateTimeField() creator = EthereumAddressField() factory_address = EthereumAddressField() master_copy = EthereumAddressField(allow_null=True) setup_data = HexadecimalField(allow_null=True) transaction_hash = Sha3HashField()
class SafeDelegateDeleteSerializer(serializers.Serializer): safe = EthereumAddressField() delegate = EthereumAddressField() signature = HexadecimalField(min_length=130) def check_signature(self, signature: bytes, operation_hash: bytes, safe_owners: List[str]) -> Optional[str]: """ Checks signature and returns a valid owner if found, None otherwise :param signature: :param operation_hash: :param safe_owners: :return: Valid delegator address if found, None otherwise """ safe_signatures = SafeSignature.parse_signature(signature, operation_hash) if not safe_signatures: raise ValidationError('Signature is not valid') elif len(safe_signatures) > 1: raise ValidationError('More than one signatures detected, just one is expected') safe_signature = safe_signatures[0] delegator = safe_signature.owner if delegator in safe_owners: if not safe_signature.is_valid(): raise ValidationError(f'Signature of type={safe_signature.signature_type.name} ' f'for delegator={delegator} is not valid') return delegator def validate(self, data): super().validate(data) if not SafeContract.objects.filter(address=data['safe']).exists(): raise ValidationError(f"Safe={data['safe']} does not exist or it's still not indexed") ethereum_client = EthereumClientProvider() safe = Safe(data['safe'], ethereum_client) # Check owners and pending owners try: safe_owners = safe.retrieve_owners(block_identifier='pending') except BadFunctionCallOutput: # Error using pending block identifier safe_owners = safe.retrieve_owners(block_identifier='latest') signature = data['signature'] delegate = data['delegate'] # Delegate address to be added # Tries to find a valid delegator using multiple strategies for operation_hash in (DelegateSignatureHelper.calculate_hash(delegate), DelegateSignatureHelper.calculate_hash(delegate, eth_sign=True), DelegateSignatureHelper.calculate_hash(delegate, previous_topt=True), DelegateSignatureHelper.calculate_hash(delegate, eth_sign=True, previous_topt=True)): delegator = self.check_signature(signature, operation_hash, safe_owners) if delegator: break if not delegator: raise ValidationError('Signing owner is not an owner of the Safe') data['delegator'] = delegator return data
class SafeCreationResponseSerializer(serializers.Serializer): signature = SignatureResponseSerializer() tx = TransactionResponseSerializer() tx_hash = Sha3HashField() payment = serializers.CharField() payment_token = EthereumAddressField(allow_null=True, allow_zero_address=True) safe = EthereumAddressField() deployer = EthereumAddressField() funder = EthereumAddressField()
class SafeCreation2Serializer(ThresholdValidatorSerializerMixin, serializers.Serializer): salt_nonce = serializers.IntegerField(min_value=0, max_value=2**256 - 1) # Uint256 owners = serializers.ListField(child=EthereumAddressField(), min_length=1) threshold = serializers.IntegerField(min_value=1) payment_token = EthereumAddressField(default=None, allow_null=True, allow_zero_address=True)
class SafeCreationSerializer(ThresholdValidatorSerializerMixin, serializers.Serializer): s = serializers.IntegerField(min_value=SIGNATURE_S_MIN_VALUE, max_value=SIGNATURE_S_MAX_VALUE) owners = serializers.ListField(child=EthereumAddressField(), min_length=1) threshold = serializers.IntegerField(min_value=1) payment_token = EthereumAddressField(default=None, allow_null=True, allow_zero_address=True)
class SafeInfoResponseSerializer(serializers.Serializer): address = EthereumAddressField() nonce = serializers.IntegerField() threshold = serializers.IntegerField() owners = serializers.ListField(child=EthereumAddressField()) master_copy = EthereumAddressField() modules = serializers.ListField(child=EthereumAddressField()) fallback_handler = EthereumAddressField() version = serializers.CharField()
class SafeCreation2ResponseSerializer(serializers.Serializer): safe = EthereumAddressField() master_copy = EthereumAddressField() proxy_factory = EthereumAddressField() payment_token = EthereumAddressField(allow_zero_address=True) payment = serializers.CharField() payment_receiver = EthereumAddressField(allow_zero_address=True) setup_data = HexadecimalField() gas_estimated = serializers.CharField() gas_price_estimated = serializers.CharField()
class SafeDelegateDeleteSerializer(serializers.Serializer): safe = EthereumAddressField() delegate = EthereumAddressField() signature = HexadecimalField(min_length=130) def validate(self, data): super().validate(data) if not SafeContract.objects.filter(address=data['safe']).exists(): raise ValidationError( f"Safe={data['safe']} does not exist or it's still not indexed" ) ethereum_client = EthereumClientProvider() safe = Safe(data['safe'], ethereum_client) # Check owners and pending owners try: safe_owners = safe.retrieve_owners(block_identifier='pending') except BadFunctionCallOutput: # Error using pending block identifier safe_owners = safe.retrieve_owners(block_identifier='latest') signature = data['signature'] delegate = data['delegate'] operation_hash = DelegateSignatureHelper.calculate_hash(delegate) safe_signatures = SafeSignature.parse_signature( signature, operation_hash) if not safe_signatures: raise ValidationError('Cannot a valid signature') elif len(safe_signatures) > 1: raise ValidationError( 'More than one signatures detected, just one is expected') safe_signature = safe_signatures[0] delegator = safe_signature.owner if delegator not in safe_owners: if safe_signature.signature_type == SafeSignatureType.EOA: # Maybe it's an `eth_sign` signature without Gnosis Safe `v + 4`, let's try safe_signatures = SafeSignature.parse_signature( signature, DelegateSignatureHelper.calculate_hash(delegate, eth_sign=True)) safe_signature = safe_signatures[0] delegator = safe_signature.owner if delegator not in safe_owners: raise ValidationError( 'Signing owner is not an owner of the Safe') if not safe_signature.is_valid(): raise ValidationError( f'Signature of type={safe_signature.signature_type.name} for delegator={delegator} ' f'is not valid') data['delegator'] = delegator return data
class SafeCreationInfoResponseSerializer(serializers.Serializer): created = serializers.DateTimeField() creator = EthereumAddressField() transaction_hash = Sha3HashField() factory_address = EthereumAddressField() master_copy = EthereumAddressField(allow_null=True) setup_data = HexadecimalField(allow_null=True) data_decoded = serializers.SerializerMethodField() def get_data_decoded(self, obj: SafeCreationInfo) -> Dict[str, Any]: return get_data_decoded_from_data(obj.setup_data or b'')
class SafeMultisigEstimateTxResponseV2Serializer(serializers.Serializer): """ Same as `SafeMultisigEstimateTxResponseSerializer`, but formatting `big integers` as `strings` """ safe_tx_gas = serializers.CharField() base_gas = serializers.CharField() data_gas = serializers.CharField() operational_gas = serializers.CharField() gas_price = serializers.CharField() last_used_nonce = serializers.IntegerField(min_value=0, allow_null=True) gas_token = EthereumAddressField(allow_null=True, allow_zero_address=True) refund_receiver = EthereumAddressField(allow_zero_address=True)
class IncomingTransactionResponseSerializer(serializers.Serializer): execution_date = serializers.DateTimeField() block_number = serializers.IntegerField() transaction_hash = Sha3HashField() to = EthereumAddressField() from_ = EthereumAddressField(source='_from') value = serializers.CharField() token_address = EthereumAddressField(allow_null=True, default=None) def get_fields(self): result = super().get_fields() # Rename `from_` to `from` from_ = result.pop('from_') result['from'] = from_ return result
class NotificationSerializer(SignedMessageSerializer): devices = serializers.ListField(child=EthereumAddressField(), min_length=1) message = serializers.CharField( max_length=4096) # 4Kb, expecting UTF-8 characters to get into 1 Byte def validate_message(self, data): try: json.loads(data) except json.JSONDecodeError: raise ValidationError("Message must be a valid stringified JSON") return data def validate(self, data): super().validate(data) devices = data['devices'] if len(set(devices)) != len(devices): raise ValidationError("Duplicated addresses are forbidden") signing_address = data['signing_address'] if signing_address in devices: raise ValidationError( "Signing address cannot be in the destination addresses") return data def get_hashed_fields(self, data: Dict[str, Any]) -> Tuple[str]: return data['message']
class SafeContractSerializer(serializers.Serializer): created = serializers.DateTimeField() address = EthereumAddressField() tokens_with_balance = serializers.SerializerMethodField() def get_tokens_with_balance(self, obj): return StatsServiceProvider().get_balances(obj.address)
class SafeMultisigTransactionEstimateSerializer(serializers.Serializer): to = EthereumAddressField() value = serializers.IntegerField(min_value=0) data = HexadecimalField(default=None, allow_null=True, allow_blank=True) operation = serializers.IntegerField(min_value=0) def save(self, **kwargs): safe_address = self.context["safe_address"] ethereum_client = EthereumClientProvider() safe = Safe(safe_address, ethereum_client) exc = None # Retry thrice to get an estimation for _ in range(3): try: safe_tx_gas = safe.estimate_tx_gas( self.validated_data["to"], self.validated_data["value"], self.validated_data["data"], self.validated_data["operation"], ) return {"safe_tx_gas": safe_tx_gas} except (IOError, ValueError) as _exc: exc = _exc raise NodeConnectionException( f"Node connection error when estimating gas for Safe {safe_address}" ) from exc
class ContractSerializer(serializers.Serializer): address = EthereumAddressField() name = serializers.CharField() display_name = serializers.CharField() logo_uri = serializers.ImageField(source="logo") contract_abi = ContractAbiSerializer() trusted_for_delegate_call = serializers.BooleanField()
class ContractSerializer(serializers.Serializer): address = EthereumAddressField() name = serializers.SerializerMethodField() logo_uri = serializers.ImageField(source='logo') contract_abi = ContractAbiSerializer() def get_name(self, obj: Contract): return obj.get_main_name()
class SafeMultisigEstimateTxResponseSerializer(serializers.Serializer): safe_tx_gas = serializers.IntegerField(min_value=0) base_gas = serializers.IntegerField(min_value=0) data_gas = serializers.IntegerField(min_value=0) operational_gas = serializers.IntegerField(min_value=0) gas_price = serializers.IntegerField(min_value=0) last_used_nonce = serializers.IntegerField(min_value=0, allow_null=True) gas_token = EthereumAddressField(allow_null=True, allow_zero_address=True)
class MasterCopyResponseSerializer(serializers.Serializer): address = EthereumAddressField() version = serializers.CharField() deployer = serializers.CharField() deployed_block_number = serializers.IntegerField( source='initial_block_number') last_indexed_block_number = serializers.IntegerField( source='tx_block_number')
class SafeMultisigTxSerializerV1(SafeMultisigEstimateTxSerializer): """ Version 1.0.0 of the Safe changes `data_gas` to `base_gas` """ safe_tx_gas = serializers.IntegerField(min_value=0) base_gas = serializers.IntegerField(min_value=0) gas_price = serializers.IntegerField(min_value=0) refund_receiver = EthereumAddressField(default=None, allow_null=True, allow_zero_address=True) nonce = serializers.IntegerField(min_value=0)
class SafeMultisigTxSerializer(SafeMultisigEstimateTxSerializer): """ DEPRECATED, use `SafeMultisigTxSerializerV1` instead """ safe_tx_gas = serializers.IntegerField(min_value=0) data_gas = serializers.IntegerField(min_value=0) gas_price = serializers.IntegerField(min_value=0) refund_receiver = EthereumAddressField(default=None, allow_null=True, allow_zero_address=True) nonce = serializers.IntegerField(min_value=0)