class FirebaseDeviceSerializer(serializers.Serializer): uuid = serializers.UUIDField(default=uuid4) # TODO Make it required safes = serializers.ListField(allow_empty=False, child=EthereumAddressField()) cloud_messaging_token = serializers.CharField(min_length=100, max_length=200) build_number = serializers.IntegerField(min_value=0) # e.g. 1644 bundle = serializers.CharField(min_length=1, max_length=100) device_type = serializers.ChoiceField( choices=[element.name for element in DeviceTypeEnum]) version = serializers.CharField(min_length=1, max_length=100) # e.g. 1.0.0-beta timestamp = serializers.IntegerField( required=False) # TODO Make it required signatures = serializers.ListField( required=False, child=HexadecimalField(required=False, min_length=65, max_length=65) # Signatures must be 65 bytes ) def validate_safes(self, safes: Sequence[str]): if SafeContract.objects.filter( address__in=safes).count() != len(safes): raise serializers.ValidationError( 'At least one Safe provided was not found or is duplicated') return safes def validate_timestamp(self, timestamp: int): """ Validate if timestamp is not on a range within 5 minutes :param timestamp: :return: """ if timestamp is not None: minutes_allowed = 5 current_epoch = int(time.time()) time_delta = abs(current_epoch - timestamp) if time_delta > ( 60 * minutes_allowed): # Timestamp older than 5 minutes raise ValidationError( f'Provided timestamp is not in a range within {minutes_allowed} minutes' ) return timestamp def validate_version(self, value: str): try: semantic_version.Version(value) except semantic_version.InvalidVersion: raise serializers.ValidationError('Semantic version was expected') return value def validate(self, data: Dict[str, Any]): data = super().validate(data) signature_owners = [] signatures = data.get('signatures') or [] if signatures: current_owners = { owner for safe in data['safes'] for owner in get_safe_owners(safe) } for signature in signatures: hash_to_sign = calculate_device_registration_hash( data['timestamp'], data['uuid'], data['cloud_messaging_token'], data['safes']) parsed_signatures = SafeSignature.parse_signature( signature, hash_to_sign) if not parsed_signatures: raise ValidationError('Signature cannot be parsed') for safe_signature in parsed_signatures: if safe_signature.signature_type != SafeSignatureType.EOA or not safe_signature.is_valid( ): raise ValidationError( 'An externally owned account signature was expected' ) owner = safe_signature.owner if owner in signature_owners: raise ValidationError( f'Signature for owner={owner} is duplicated') elif owner not in current_owners: raise ValidationError( f'Owner={owner} is not an owner of any of the safes={data["safes"]}. ' f'Expected hash to sign {hash_to_sign.hex()}') else: signature_owners.append(owner) if len(signatures) > len(signature_owners): raise ValidationError( 'Number of signatures is less than the number of owners detected' ) data['owners'] = signature_owners return data def save(self, **kwargs): try: uuid = self.validated_data['uuid'] firebase_device, _ = FirebaseDevice.objects.update_or_create( uuid=uuid, defaults={ 'cloud_messaging_token': self.validated_data['cloud_messaging_token'], 'build_number': self.validated_data['build_number'], 'bundle': self.validated_data['bundle'], 'device_type': DeviceTypeEnum[self.validated_data['device_type']].value, 'version': self.validated_data['version'], }) except IntegrityError as e: raise serializers.ValidationError( 'Cloud messaging token is linked to another device') # Remove every owner registered for the device and add the provided ones firebase_device.owners.all().delete() for owner in self.validated_data['owners']: FirebaseDeviceOwner.objects.create(firebase_device=firebase_device, owner=owner) # Remove every Safe registered for the device and add the provided ones firebase_device.safes.clear() safe_contracts = SafeContract.objects.filter( address__in=self.validated_data['safes']) firebase_device.safes.add(*safe_contracts) return firebase_device
class SafeCreationEstimateResponseSerializer(serializers.Serializer): gas = serializers.CharField() gas_price = serializers.CharField() payment = serializers.CharField() payment_token = EthereumAddressField(allow_null=True)
class SafeMultisigTransactionSerializer(SafeMultisigTxSerializerV1): contract_transaction_hash = Sha3HashField() sender = EthereumAddressField() # TODO Make signature mandatory signature = HexadecimalField(required=False, min_length=65) # Signatures must be at least 65 bytes origin = serializers.CharField(max_length=200, allow_null=True, default=None) def validate(self, data): super().validate(data) ethereum_client = EthereumClientProvider() safe = Safe(data['safe'], ethereum_client) try: safe_version = safe.retrieve_version() except BadFunctionCallOutput as e: raise ValidationError(f'Could not get Safe version from blockchain, check contract exists on network ' f'{ethereum_client.get_network().name}') from e except IOError: raise ValidationError('Problem connecting to the ethereum node, please try again later') safe_tx = safe.build_multisig_tx(data['to'], data['value'], data['data'], data['operation'], data['safe_tx_gas'], data['base_gas'], data['gas_price'], data['gas_token'], data['refund_receiver'], safe_nonce=data['nonce'], safe_version=safe_version) contract_transaction_hash = safe_tx.safe_tx_hash # Check safe tx hash matches if contract_transaction_hash != data['contract_transaction_hash']: raise ValidationError(f'Contract-transaction-hash={contract_transaction_hash.hex()} ' f'does not match provided contract-tx-hash={data["contract_transaction_hash"].hex()}') # Check there's not duplicated tx with same `nonce` or same `safeTxHash` for the same Safe. # We allow duplicated if existing tx is not executed multisig_transactions = MultisigTransaction.objects.filter( safe=safe.address, nonce=data['nonce'] ).executed() if multisig_transactions: for multisig_transaction in multisig_transactions: if multisig_transaction.safe_tx_hash == contract_transaction_hash.hex(): raise ValidationError(f'Tx with safe-tx-hash={contract_transaction_hash.hex()} ' f'for safe={safe.address} was already executed in ' f'tx-hash={multisig_transaction.ethereum_tx_id}') raise ValidationError(f'Tx with nonce={safe_tx.safe_nonce} for safe={safe.address} ' f'already executed in tx-hash={multisig_transactions[0].ethereum_tx_id}') # 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') except IOError: raise ValidationError('Problem connecting to the ethereum node, please try again later') data['safe_owners'] = safe_owners delegates = SafeContractDelegate.objects.get_delegates_for_safe(safe.address) allowed_senders = safe_owners + delegates if not data['sender'] in allowed_senders: raise ValidationError(f'Sender={data["sender"]} is not an owner or delegate. ' f'Current owners={safe_owners}. Delegates={delegates}') signature_owners = [] # TODO Make signature mandatory signature = data.get('signature', b'') parsed_signatures = SafeSignature.parse_signature(signature, contract_transaction_hash) data['parsed_signatures'] = parsed_signatures # If there's at least one signature, transaction is trusted (until signatures are mandatory) data['trusted'] = bool(parsed_signatures) for safe_signature in parsed_signatures: owner = safe_signature.owner if not safe_signature.is_valid(ethereum_client, safe.address): raise ValidationError(f'Signature={safe_signature.signature.hex()} for owner={owner} is not valid') if owner in delegates and len(parsed_signatures) > 1: raise ValidationError('Just one signature is expected if using delegates') if owner not in allowed_senders: raise ValidationError(f'Signer={owner} is not an owner or delegate. ' f'Current owners={safe_owners}. Delegates={delegates}') if owner in signature_owners: raise ValidationError(f'Signature for owner={owner} is duplicated') signature_owners.append(owner) # TODO Make signature mandatory. len(signature_owners) must be >= 1 if signature_owners and data['sender'] not in signature_owners: raise ValidationError(f'Signature does not match sender={data["sender"]}. ' f'Calculated owners={signature_owners}') return data def save(self, **kwargs): safe_tx_hash = self.validated_data['contract_transaction_hash'] origin = self.validated_data['origin'] trusted = self.validated_data['trusted'] multisig_transaction, created = MultisigTransaction.objects.get_or_create( safe_tx_hash=safe_tx_hash, defaults={ 'safe': self.validated_data['safe'], 'to': self.validated_data['to'], 'value': self.validated_data['value'], 'data': self.validated_data['data'] if self.validated_data['data'] else None, 'operation': self.validated_data['operation'], 'safe_tx_gas': self.validated_data['safe_tx_gas'], 'base_gas': self.validated_data['base_gas'], 'gas_price': self.validated_data['gas_price'], 'gas_token': self.validated_data['gas_token'], 'refund_receiver': self.validated_data['refund_receiver'], 'nonce': self.validated_data['nonce'], 'origin': origin, 'trusted': trusted, } ) if not created and trusted and not multisig_transaction.trusted: multisig_transaction.origin = origin multisig_transaction.trusted = trusted multisig_transaction.save(update_fields=['origin', 'trusted']) for safe_signature in self.validated_data.get('parsed_signatures'): if safe_signature.owner in self.validated_data['safe_owners']: multisig_confirmation, _ = MultisigConfirmation.objects.get_or_create( multisig_transaction_hash=safe_tx_hash, owner=safe_signature.owner, defaults={ 'multisig_transaction': multisig_transaction, 'signature': safe_signature.export_signature(), 'signature_type': safe_signature.signature_type.value, } ) return multisig_transaction
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 SafeDelegateResponseSerializer(serializers.Serializer): delegate = EthereumAddressField() delegator = EthereumAddressField() label = serializers.CharField(max_length=50)
class OwnerResponseSerializer(serializers.Serializer): safes = serializers.ListField(child=EthereumAddressField())
class ContractSerializer(serializers.Serializer): address = EthereumAddressField() name = serializers.CharField() contract_abi = ContractAbiSerializer()
class SafeCreationEstimateSerializer(serializers.Serializer): number_owners = serializers.IntegerField(min_value=1) payment_token = EthereumAddressField(default=None, allow_null=True, allow_zero_address=True)
class SafeDelegateResponseSerializer(serializers.Serializer): safe = EthereumAddressField(source="safe_contract_id") delegate = EthereumAddressField() delegator = EthereumAddressField() label = serializers.CharField(max_length=50)
class SafeDelegateDeleteSerializer(serializers.Serializer): """ Deprecated in favour of DelegateDeleteSerializer """ safe = EthereumAddressField() delegate = EthereumAddressField() signature = HexadecimalField(min_length=65) def get_safe_owners( self, ethereum_client: EthereumClient, safe_address: ChecksumAddress) -> List[ChecksumAddress]: safe = Safe(safe_address, ethereum_client) try: return safe.retrieve_owners(block_identifier="pending") except BadFunctionCallOutput: # Error using pending block identifier return safe.retrieve_owners(block_identifier="latest") def get_valid_delegators( self, ethereum_client: EthereumClient, safe_address: ChecksumAddress, delegate: ChecksumAddress, ) -> List[ChecksumAddress]: """ :param ethereum_client: :param safe_address: :param delegate: :return: Valid delegators for a Safe. A delegate should be able to remove itself """ return self.get_safe_owners(ethereum_client, safe_address) + [delegate] def check_signature( self, ethereum_client: EthereumClient, safe_address: ChecksumAddress, signature: bytes, operation_hash: bytes, valid_delegators: List[ChecksumAddress], ) -> Optional[ChecksumAddress]: """ Checks signature and returns a valid owner if found, None otherwise :param ethereum_client: :param safe_address: :param signature: :param operation_hash: :param valid_delegators: :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") if 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 valid_delegators: if not safe_signature.is_valid(ethereum_client, safe_address): 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) safe_address = data["safe"] if not SafeContract.objects.filter(address=safe_address).exists(): raise ValidationError( f"Safe={safe_address} does not exist or it's still not indexed" ) signature = data["signature"] delegate = data["delegate"] # Delegate address to be added/removed ethereum_client = EthereumClientProvider() valid_delegators = self.get_valid_delegators(ethereum_client, safe_address, delegate) # Tries to find a valid delegator using multiple strategies for operation_hash in DelegateSignatureHelper.calculate_all_possible_hashes( delegate): delegator = self.check_signature( ethereum_client, safe_address, signature, operation_hash, valid_delegators, ) if delegator: break if not delegator: raise ValidationError("Signing owner is not an owner of the Safe") data["delegator"] = delegator return data
class DataDecoderSerializer(serializers.Serializer): data = HexadecimalField(allow_null=False, allow_blank=False, min_length=4) to = EthereumAddressField(allow_null=True, required=False)
class DelegateSerializer(DelegateSignatureCheckerMixin, serializers.Serializer): safe = EthereumAddressField(allow_null=True, required=False) delegate = EthereumAddressField() delegator = EthereumAddressField() signature = HexadecimalField(min_length=65) label = serializers.CharField(max_length=50) def get_safe_owners( self, ethereum_client: EthereumClient, safe_address: ChecksumAddress) -> List[ChecksumAddress]: safe = Safe(safe_address, ethereum_client) try: return safe.retrieve_owners(block_identifier="pending") except BadFunctionCallOutput: # Error using pending block identifier return safe.retrieve_owners(block_identifier="latest") def validate(self, data): super().validate(data) safe_address: Optional[ChecksumAddress] = data.get("safe") if (safe_address and not SafeContract.objects.filter( address=safe_address).exists()): raise ValidationError( f"Safe={safe_address} does not exist or it's still not indexed" ) signature = data["signature"] delegate = data["delegate"] # Delegate address to be added/removed delegator = data[ "delegator"] # Delegator giving permissions to delegate (signer) ethereum_client = EthereumClientProvider() if safe_address: # Valid delegators must be owners valid_delegators = self.get_safe_owners(ethereum_client, safe_address) if delegator not in valid_delegators: raise ValidationError( f"Provided delegator={delegator} is not an owner of Safe={safe_address}" ) # Tries to find a valid delegator using multiple strategies for operation_hash in DelegateSignatureHelper.calculate_all_possible_hashes( delegate): if self.check_delegate_signature(ethereum_client, signature, operation_hash, delegator): return data raise ValidationError( f"Signature does not match provided delegator={delegator}") def save(self, **kwargs): safe_address = self.validated_data["safe"] delegate = self.validated_data["delegate"] delegator = self.validated_data["delegator"] label = self.validated_data["label"] obj, _ = SafeContractDelegate.objects.update_or_create( safe_contract_id=safe_address, delegate=delegate, delegator=delegator, defaults={ "label": label, }, ) return obj
class SafeMultisigTransactionSerializer(SafeMultisigTxSerializerV1): contract_transaction_hash = Sha3HashField() sender = EthereumAddressField() # TODO Make signature mandatory signature = HexadecimalField( allow_null=True, required=False, min_length=65) # Signatures must be at least 65 bytes origin = serializers.CharField(max_length=200, allow_null=True, default=None) def validate(self, data): super().validate(data) ethereum_client = EthereumClientProvider() safe = Safe(data["safe"], ethereum_client) try: safe_version = safe.retrieve_version() except BadFunctionCallOutput as e: raise ValidationError( f"Could not get Safe version from blockchain, check contract exists on network " f"{ethereum_client.get_network().name}") from e except IOError: raise ValidationError( "Problem connecting to the ethereum node, please try again later" ) safe_tx = safe.build_multisig_tx( data["to"], data["value"], data["data"], data["operation"], data["safe_tx_gas"], data["base_gas"], data["gas_price"], data["gas_token"], data["refund_receiver"], safe_nonce=data["nonce"], safe_version=safe_version, ) contract_transaction_hash = safe_tx.safe_tx_hash # Check safe tx hash matches if contract_transaction_hash != data["contract_transaction_hash"]: raise ValidationError( f"Contract-transaction-hash={contract_transaction_hash.hex()} " f'does not match provided contract-tx-hash={data["contract_transaction_hash"].hex()}' ) # Check there's not duplicated tx with same `nonce` or same `safeTxHash` for the same Safe. # We allow duplicated if existing tx is not executed multisig_transactions = MultisigTransaction.objects.filter( safe=safe.address, nonce=data["nonce"]).executed() if multisig_transactions: for multisig_transaction in multisig_transactions: if multisig_transaction.safe_tx_hash == contract_transaction_hash.hex( ): raise ValidationError( f"Tx with safe-tx-hash={contract_transaction_hash.hex()} " f"for safe={safe.address} was already executed in " f"tx-hash={multisig_transaction.ethereum_tx_id}") raise ValidationError( f"Tx with nonce={safe_tx.safe_nonce} for safe={safe.address} " f"already executed in tx-hash={multisig_transactions[0].ethereum_tx_id}" ) # 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") except IOError: raise ValidationError( "Problem connecting to the ethereum node, please try again later" ) data["safe_owners"] = safe_owners delegates = SafeContractDelegate.objects.get_delegates_for_safe_and_owners( safe.address, safe_owners) allowed_senders = set(safe_owners) | delegates if not data["sender"] in allowed_senders: raise ValidationError( f'Sender={data["sender"]} is not an owner or delegate. ' f"Current owners={safe_owners}. Delegates={delegates}") signature_owners = [] # TODO Make signature mandatory signature = data.get("signature", b"") parsed_signatures = SafeSignature.parse_signature( signature, contract_transaction_hash) data["parsed_signatures"] = parsed_signatures # If there's at least one signature, transaction is trusted (until signatures are mandatory) data["trusted"] = bool(parsed_signatures) for safe_signature in parsed_signatures: owner = safe_signature.owner if not safe_signature.is_valid(ethereum_client, safe.address): raise ValidationError( f"Signature={safe_signature.signature.hex()} for owner={owner} is not valid" ) if owner in delegates and len(parsed_signatures) > 1: raise ValidationError( "Just one signature is expected if using delegates") if owner not in allowed_senders: raise ValidationError( f"Signer={owner} is not an owner or delegate. " f"Current owners={safe_owners}. Delegates={delegates}") if owner in signature_owners: raise ValidationError( f"Signature for owner={owner} is duplicated") signature_owners.append(owner) # TODO Make signature mandatory. len(signature_owners) must be >= 1 if signature_owners and data["sender"] not in signature_owners: raise ValidationError( f'Signature does not match sender={data["sender"]}. ' f"Calculated owners={signature_owners}") return data def save(self, **kwargs): safe_tx_hash = self.validated_data["contract_transaction_hash"] origin = self.validated_data["origin"] trusted = self.validated_data["trusted"] if not trusted: # Check user permission if (self.context and (request := self.context.get("request")) and (user := request.user)): trusted = user.has_perm("history.create_trusted")
class SafeAddressPredictionResponseSerializer(serializers.Serializer): safe = EthereumAddressField()
class SafeDelegateDeleteSerializer(serializers.Serializer): safe = EthereumAddressField() delegate = EthereumAddressField() signature = HexadecimalField(min_length=65) 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 TransactionGasTokenEstimationResponseSerializer(serializers.Serializer): base_gas = serializers.CharField() gas_price = serializers.CharField() gas_token = EthereumAddressField(allow_null=True, allow_zero_address=True)
class MasterCopyResponseSerializer(serializers.Serializer): address = EthereumAddressField() version = serializers.CharField()
class TokensWithBalanceSerializer(serializers.Serializer): token_address = EthereumAddressField() balance = serializers.IntegerField()
class SafeMultisigTransactionSerializer(SafeMultisigTxSerializerV1): contract_transaction_hash = Sha3HashField() sender = EthereumAddressField() signature = HexadecimalField(required=False) origin = serializers.CharField(max_length=100, allow_null=True, default=None) def validate(self, data): super().validate(data) ethereum_client = EthereumClientProvider() safe = Safe(data['safe'], ethereum_client) safe_tx = safe.build_multisig_tx(data['to'], data['value'], data['data'], data['operation'], data['safe_tx_gas'], data['base_gas'], data['gas_price'], data['gas_token'], data['refund_receiver'], safe_nonce=data['nonce']) contract_transaction_hash = safe_tx.safe_tx_hash # Check safe tx hash matches if contract_transaction_hash != data['contract_transaction_hash']: raise ValidationError( f'Contract-transaction-hash={contract_transaction_hash.hex()} ' f'does not match provided contract-tx-hash={data["contract_transaction_hash"].hex()}' ) # Check there's not duplicated tx with same `nonce` for the same Safe. # We allow duplicated if existing tx is not executed try: multisig_transaction: MultisigTransaction = MultisigTransaction.objects.exclude( ethereum_tx=None).exclude( safe_tx_hash=contract_transaction_hash).get( safe=safe.address, nonce=data['nonce']) if multisig_transaction.safe_tx_hash != contract_transaction_hash: raise ValidationError( f'Tx with nonce={safe_tx.safe_nonce} for safe={safe.address} already executed in ' f'tx-hash={multisig_transaction.ethereum_tx_id}') except MultisigTransaction.DoesNotExist: pass # Check owners and old owners, owner might be removed but that tx can still be signed by that owner if not safe.retrieve_is_owner(data['sender']): try: # TODO Fix this, we can use SafeStatus now if not safe.retrieve_is_owner( data['sender'], block_identifier=max( 0, ethereum_client.current_block_number - 20)): raise ValidationError('User is not an owner') except BadFunctionCallOutput: # If it didn't exist 20 blocks ago raise ValidationError('User is not an owner') signature = data.get('signature') if signature is not None: # TODO Support signatures with multiple owners if len(signature) != 65: raise ValidationError( 'Signatures with more than one owner still not supported') safe_signature = SafeSignature(signature, contract_transaction_hash) # TODO Support contract signatures and approved hashes if safe_signature.signature_type == SafeSignatureType.CONTRACT_SIGNATURE: raise ValidationError('Contract signatures not supported') elif safe_signature.signature_type == SafeSignatureType.APPROVED_HASH: # Index it automatically later del data['signature'] address = safe_signature.owner if address != data['sender']: raise ValidationError( f'Signature does not match sender={data["sender"]}. Calculated owner={address}' ) return data def save(self, **kwargs): multisig_transaction, _ = MultisigTransaction.objects.get_or_create( safe_tx_hash=self.validated_data['contract_transaction_hash'], defaults={ 'safe': self.validated_data['safe'], 'to': self.validated_data['to'], 'value': self.validated_data['value'], 'data': self.validated_data['data'] if self.validated_data['data'] else None, 'operation': self.validated_data['operation'], 'safe_tx_gas': self.validated_data['safe_tx_gas'], 'base_gas': self.validated_data['base_gas'], 'gas_price': self.validated_data['gas_price'], 'gas_token': self.validated_data['gas_token'], 'refund_receiver': self.validated_data['refund_receiver'], 'nonce': self.validated_data['nonce'], 'origin': self.validated_data['origin'], }) if self.validated_data.get('signature'): MultisigConfirmation.objects.get_or_create( multisig_transaction_hash=multisig_transaction.safe_tx_hash, owner=self.validated_data['sender'], defaults={ 'multisig_transaction': multisig_transaction, 'signature': self.validated_data.get('signature'), }) return multisig_transaction
class MasterCopyResponseSerializer(serializers.Serializer): address = EthereumAddressField() version = serializers.CharField() deployed_block_number = serializers.IntegerField(source='initial_block_number') last_indexed_block_number = serializers.IntegerField(source='tx_block_number')
class SafeCreationInfoResponseSerializer(serializers.Serializer): created = serializers.DateTimeField() transaction_hash = Sha3HashField() creator = EthereumAddressField()
class AnalyticsMultisigTxsBySafeResponseSerializer(serializers.Serializer): safe = EthereumAddressField() master_copy = EthereumAddressField() transactions = serializers.IntegerField()
class FirebaseDeviceSerializerWithOwnersResponseSerializer( FirebaseDeviceSerializer): owners_registered = serializers.ListField(allow_empty=True, child=EthereumAddressField()) owners_not_registered = serializers.ListField(allow_empty=True, child=EthereumAddressField())
class ContractSerializer(serializers.Serializer): address = EthereumAddressField() name = serializers.CharField() display_name = serializers.CharField() logo_uri = serializers.ImageField(source='logo') contract_abi = ContractAbiSerializer()
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)