示例#1
0
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)
示例#3
0
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)
示例#5
0
class SafeDelegateResponseSerializer(serializers.Serializer):
    delegate = EthereumAddressField()
    delegator = EthereumAddressField()
    label = serializers.CharField(max_length=50)
示例#6
0
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
示例#20
0
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()
示例#22
0
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)