Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor delegate endpoint signatures to use EIP712 #2001

Merged
merged 9 commits into from
Apr 24, 2024
1 change: 1 addition & 0 deletions .env.dev
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,4 @@ ETHEREUM_TRACING_NODE_URL=
ETH_L2_NETWORK=1
REDIS_URL=redis://redis:6379/0
CELERY_BROKER_URL=amqp://guest:guest@rabbitmq/
CSRF_TRUSTED_ORIGINS="http://localhost:8000"
5 changes: 5 additions & 0 deletions config/settings/local.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,11 @@

REDIS_URL = env.str("REDIS_URL")

# SECURITY
# ------------------------------------------------------------------------------
# https://docs.djangoproject.com/en/dev/ref/middleware/#x-content-type-options-nosniff
CSRF_TRUSTED_ORIGINS = env.list("CSRF_TRUSTED_ORIGINS", default=[])

# CACHES
# ------------------------------------------------------------------------------
# https://docs.djangoproject.com/en/dev/ref/settings/#caches
Expand Down
2 changes: 1 addition & 1 deletion config/settings/production.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@
SECURE_CONTENT_TYPE_NOSNIFF = env.bool(
"DJANGO_SECURE_CONTENT_TYPE_NOSNIFF", default=True
)
# https://docs.djangoproject.com/en/3.2/ref/settings/#csrf-trusted-origins
# https://docs.djangoproject.com/en/5.0/ref/settings/#csrf-trusted-origins
CSRF_TRUSTED_ORIGINS = env.list("CSRF_TRUSTED_ORIGINS", default=[])

# SSO (tested with https://github.com/buzzfeed/sso)
Expand Down
91 changes: 72 additions & 19 deletions safe_transaction_service/history/helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -80,31 +80,49 @@ def calculate_hash(
return eip712_encode_hash(payload)


class DelegateSignatureHelper(TemporarySignatureHelper):
class DelegateSignatureHelperV2(TemporarySignatureHelper):
@classmethod
def calculate_hash(
cls,
address: ChecksumAddress,
eth_sign: bool = False,
delegate_address: ChecksumAddress,
chain_id: int,
previous_totp: bool = False,
) -> bytes:
) -> Hash32:
"""
Builds a EIP712 object and calculates its hash

:param delegate_address:
:param chain_id:
:param previous_totp:
:return: Hash for the EIP712 generated object from the provided parameters
"""
totp = cls.calculate_totp(previous=previous_totp)
message = address + str(totp)
if eth_sign:
return keccak(
text="\x19Ethereum Signed Message:\n" + str(len(message)) + message
)
else:
return keccak(text=message)

@classmethod
def calculate_all_possible_hashes(cls, delegate: ChecksumAddress) -> List[bytes]:
return [
cls.calculate_hash(delegate),
cls.calculate_hash(delegate, eth_sign=True),
cls.calculate_hash(delegate, previous_totp=True),
cls.calculate_hash(delegate, eth_sign=True, previous_totp=True),
]
payload = {
"types": {
"EIP712Domain": [
{"name": "name", "type": "string"},
{"name": "version", "type": "string"},
{"name": "chainId", "type": "uint256"},
],
"Delegate": [
{"name": "delegateAddress", "type": "bytes32"},
{"name": "totp", "type": "uint256"},
],
},
"primaryType": "Delegate",
"domain": {
"name": "Safe Transaction Service",
"version": "1.0",
"chainId": chain_id,
},
"message": {
"delegateAddress": delegate_address,
"totp": totp,
},
}

return eip712_encode_hash(payload)


def is_valid_unique_transfer_id(unique_transfer_id: str) -> bool:
Expand Down Expand Up @@ -143,3 +161,38 @@ def add_tokens_to_transfers(transfers: TransferDict) -> TransferDict:
for transfer in transfers:
transfer["token"] = tokens.get(transfer["token_address"])
return transfers


# Deprecated ---------------------------------------------------------------


class DelegateSignatureHelper(TemporarySignatureHelper):
"""
.. deprecated:: 4.38.0
Deprecated in favour of DelegateSignatureHelperV2
"""

@classmethod
def calculate_hash(
cls,
address: ChecksumAddress,
eth_sign: bool = False,
previous_totp: bool = False,
) -> bytes:
totp = cls.calculate_totp(previous=previous_totp)
message = address + str(totp)
if eth_sign:
return keccak(
text="\x19Ethereum Signed Message:\n" + str(len(message)) + message
)
else:
return keccak(text=message)

@classmethod
def calculate_all_possible_hashes(cls, delegate: ChecksumAddress) -> List[bytes]:
return [
cls.calculate_hash(delegate),
cls.calculate_hash(delegate, eth_sign=True),
cls.calculate_hash(delegate, previous_totp=True),
cls.calculate_hash(delegate, eth_sign=True, previous_totp=True),
]
Loading
Loading