Skip to content

Commit

Permalink
merge
Browse files Browse the repository at this point in the history
  • Loading branch information
gofman8 committed Oct 4, 2024
2 parents 983f7a0 + b9f2d01 commit b3b3583
Show file tree
Hide file tree
Showing 98 changed files with 2,761 additions and 1,318 deletions.
4 changes: 2 additions & 2 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,15 @@ repos:
hooks:
- id: isort
- repo: https://github.com/psf/black
rev: 24.2.0
rev: 24.4.2
hooks:
- id: black
- repo: https://github.com/PyCQA/flake8
rev: 7.0.0
hooks:
- id: flake8
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.5.0
rev: v4.6.0
hooks:
- id: check-added-large-files
- id: check-docstring-first
Expand Down
12 changes: 10 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,13 @@ a transaction that is pending to be sent to the blockchain.
- [Deploying the service](https://github.com/safe-global/safe-infrastructure)

## Setup for development
Use a virtualenv if possible:
Use a [virtualenv](https://docs.python.org/es/3/library/venv.html) if possible:

```bash
python -m venv venv
```

Then enter the virtualenv and install the dependencies:
Then enter the `virtualenv` and install the dependencies:

```bash
source venv/bin/activate
Expand All @@ -40,6 +40,14 @@ cp .env.dev .env
./run_tests.sh
```

To run the e2e tests, some environment variables are required:

```bash
export ETHEREUM_MAINNET_NODE="https://erigon-node-mainnet.dev/"
export ETHEREUM_4337_BUNDLER_URL="https://eth-sepolia.g.alchemy.com/v2/$API_KEY"
./run_tests.sh
```

## Setup for development using docker
```bash
docker-compose -f docker-compose.yml -f docker-compose.dev.yml up
Expand Down
12 changes: 8 additions & 4 deletions config/settings/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -268,10 +268,6 @@
"safe_transaction_service.history.tasks.retry_get_metadata_task",
{"queue": "tokens", "delivery_mode": "transient"},
),
(
"safe_transaction_service.history.tasks.send_webhook_task",
{"queue": "webhooks", "delivery_mode": "transient"},
),
(
"safe_transaction_service.events.tasks.send_event_to_queue_task",
{"queue": "webhooks", "delivery_mode": "transient"},
Expand Down Expand Up @@ -516,6 +512,14 @@
"TOKENS_ERC20_GET_BALANCES_BATCH", default=2_000
) # Number of tokens to get balances from in the same request. From 2_500 some nodes raise HTTP 413

# ENS
# ------------------------------------------------------------------------------
# See https://docs.ens.domains/web/subgraph for more details

ENS_SUBGRAPH_URL = env.str("ENS_SUBGRAPH_URL", default=None)
ENS_SUBGRAPH_API_KEY = env.str("ENS_SUBGRAPH_API_KEY", default=None)
ENS_SUBGRAPH_ID = env.str("ENS_SUBGRAPH_ID", default=None)

# Notifications
# ------------------------------------------------------------------------------
SLACK_API_WEBHOOK = env("SLACK_API_WEBHOOK", default=None)
Expand Down
6 changes: 3 additions & 3 deletions requirements-test.txt
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
-r requirements.txt
coverage==7.5.1
coverage==7.4.4
django-stubs==5.0.0
django-test-migrations==1.3.0
factory-boy==3.3.0
faker==25.1.0
mypy==1.9.0
faker==24.11.0
mypy==1.11.0
pytest==8.2.0
pytest-celery==1.0.0
pytest-django==4.8.0
Expand Down
25 changes: 12 additions & 13 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
asgiref==3.7.2
boto3==1.34.103
cachetools==5.3.3
boto3==1.34.144
cachetools==5.4.0
celery==5.4.0
django==5.0.4
django==5.0.7
django-cache-memoize==0.2.0
django-celery-beat==2.6.0
django-cors-headers==4.3.1
django-db-geventpool==4.0.2
django-cors-headers==4.4.0
django-db-geventpool==4.0.6
django-debug-toolbar
django-debug-toolbar-force
django-environ==0.11.2
Expand All @@ -16,8 +16,8 @@ django-imagekit==5.0.0
django-model-utils==4.5.1
django-redis==5.4.0
django-s3-storage==0.15.0
django-timezone-field==6.1.0
djangorestframework==3.15.1
django-timezone-field==7.0
djangorestframework==3.15.2
djangorestframework-camel-case==1.4.2
docutils==0.21.2
drf-yasg[validation]==1.21.7
Expand All @@ -28,11 +28,10 @@ hexbytes==0.3.1
hiredis==2.3.2
packaging>=21.0
pika==1.3.2
pillow==10.3.0
pillow==10.4.0
psycogreen==1.0.2
psycopg2==2.9.9
redis==5.0.4
requests==2.31.0
git+https://github.com/protofire/[email protected]#egg=safe-eth-py
# safe-eth-py[django]==6.0.0b29
web3==6.18.0
redis==5.0.7
requests==2.32.3
safe-eth-py[django]==6.0.0b35
web3==6.20.0
2 changes: 1 addition & 1 deletion run_tests.sh
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ set -euo pipefail
export DJANGO_SETTINGS_MODULE=config.settings.test
export DJANGO_DOT_ENV_FILE=.env.test
docker compose -f docker-compose.yml -f docker-compose.dev.yml build --force-rm db redis ganache rabbitmq
docker compose -f docker-compose.yml -f docker-compose.dev.yml up --no-start db redis ganache rabbitmq
docker compose -f docker-compose.yml -f docker-compose.dev.yml up --no-start --force-recreate db redis ganache rabbitmq
docker compose -f docker-compose.yml -f docker-compose.dev.yml start db redis ganache rabbitmq

python manage.py check
Expand Down
2 changes: 1 addition & 1 deletion safe_transaction_service/__init__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
__version__ = "5.0.0"
__version__ = "5.6.0"
__version_info__ = tuple(
int(num) if num.isdigit() else num
for num in __version__.replace("-", ".", 1).split(".")
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Generated by Django 5.0.4 on 2024-05-30 08:37

from django.db import migrations


class Migration(migrations.Migration):

dependencies = [
("account_abstraction", "0003_alter_useroperationreceipt_reason"),
]

operations = [
migrations.RenameField(
model_name="useroperation",
old_name="call_data_gas_limit",
new_name="call_gas_limit",
),
]
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
# Generated by Django 5.0.7 on 2024-07-19 12:53

from django.db import migrations

import gnosis.eth.django.models


class Migration(migrations.Migration):

dependencies = [
(
"account_abstraction",
"0004_rename_call_data_gas_limit_useroperation_call_gas_limit",
),
]

operations = [
migrations.AlterField(
model_name="safeoperation",
name="module_address",
field=gnosis.eth.django.models.EthereumAddressBinaryField(db_index=True),
),
migrations.AlterField(
model_name="safeoperationconfirmation",
name="owner",
field=gnosis.eth.django.models.EthereumAddressBinaryField(),
),
migrations.AlterField(
model_name="useroperation",
name="entry_point",
field=gnosis.eth.django.models.EthereumAddressBinaryField(db_index=True),
),
migrations.AlterField(
model_name="useroperation",
name="paymaster",
field=gnosis.eth.django.models.EthereumAddressBinaryField(
blank=True, db_index=True, null=True
),
),
migrations.AlterField(
model_name="useroperation",
name="sender",
field=gnosis.eth.django.models.EthereumAddressBinaryField(db_index=True),
),
]
57 changes: 44 additions & 13 deletions safe_transaction_service/account_abstraction/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,14 @@
from django.db import models
from django.db.models import Index

from eth_abi.packed import encode_packed
from hexbytes import HexBytes
from model_utils.models import TimeStampedModel

from gnosis.eth.account_abstraction import UserOperation as UserOperationClass
from gnosis.eth.account_abstraction import UserOperationMetadata
from gnosis.eth.django.models import (
EthereumAddressV2Field,
EthereumAddressBinaryField,
HexV2Field,
Keccak256Field,
Uint256Field,
Expand All @@ -36,21 +37,21 @@ class UserOperation(models.Model):
ethereum_tx = models.ForeignKey(
history_models.EthereumTx, on_delete=models.CASCADE, null=True, blank=True
)
sender = EthereumAddressV2Field(db_index=True)
sender = EthereumAddressBinaryField(db_index=True)
nonce = Uint256Field()
init_code = models.BinaryField(null=True, blank=True, editable=True)
call_data = models.BinaryField(null=True, blank=True, editable=True)
call_data_gas_limit = Uint256Field()
call_gas_limit = Uint256Field()
verification_gas_limit = Uint256Field()
pre_verification_gas = Uint256Field()
max_fee_per_gas = Uint256Field()
max_priority_fee_per_gas = Uint256Field()
paymaster = EthereumAddressV2Field(
paymaster = EthereumAddressBinaryField(
db_index=True, null=True, blank=True, editable=True
)
paymaster_data = models.BinaryField(null=True, blank=True, editable=True)
signature = models.BinaryField(null=True, blank=True, editable=True)
entry_point = EthereumAddressV2Field(db_index=True)
entry_point = EthereumAddressBinaryField(db_index=True)

class Meta:
indexes = [
Expand Down Expand Up @@ -83,19 +84,23 @@ def to_user_operation(self, add_tx_metadata: bool = False) -> UserOperationClass
else None
)

if not self.signature:
raise ValueError("Signature should not be empty")
signature = HexBytes(self.signature)

return UserOperationClass(
HexBytes(self.hash),
self.sender,
self.nonce,
HexBytes(self.init_code) if self.init_code else b"",
HexBytes(self.call_data) if self.call_data else b"",
self.call_data_gas_limit,
self.call_gas_limit,
self.verification_gas_limit,
self.pre_verification_gas,
self.max_fee_per_gas,
self.max_priority_fee_per_gas,
self.paymaster_and_data if self.paymaster_and_data else b"",
HexBytes(self.signature) if self.signature else b"",
signature,
self.entry_point,
user_operation_metadata,
)
Expand All @@ -105,9 +110,7 @@ def to_safe_operation(self) -> SafeOperationClass:
:return: SafeOperation built from UserOperation
:raises: ValueError
"""
if self.signature and bytes(self.signature):
return SafeOperationClass.from_user_operation(self.to_user_operation())
raise ValueError("Not enough information to build SafeOperation")
return SafeOperationClass.from_user_operation(self.to_user_operation())


class UserOperationReceipt(models.Model):
Expand All @@ -131,13 +134,40 @@ class SafeOperation(TimeStampedModel):
)
valid_after = models.DateTimeField(null=True) # Epoch uint48
valid_until = models.DateTimeField(null=True) # Epoch uint48
module_address = EthereumAddressV2Field(db_index=True)
module_address = EthereumAddressBinaryField(db_index=True)

def __str__(self) -> str:
return f"{HexBytes(self.hash).hex()} SafeOperation for user-operation={HexBytes(self.user_operation_id).hex()}"

def build_signature_prefix(self) -> bytes:
"""
`UserOperation` `signature` is used to store data for the `SafeOperation`
First 6 bytes of the signature are `SafeOperation` `valid_after` and next 6 are `valid_until`
Real user signature is after those 12 bytes
:return: 12 bytes of `valid` encoded
"""
return encode_packed(
["uint48"] * 2,
[
int(valid_date.timestamp()) if valid_date else 0
for valid_date in (
self.valid_after,
self.valid_until,
)
],
)

def build_signature(self) -> bytes:
return b"".join(
"""
`UserOperation` `signature` is used to store data for the `SafeOperation`
First 6 bytes of the signature are `SafeOperation` `valid_after` and next 6 are `valid_until`
Real user signature is after those 12 bytes
:return: 12 bytes of `valid` encoded following by the signature built from the confirmations
"""
valid_encoded = self.build_signature_prefix()
confirmations_signature = b"".join(
[
HexBytes(signature)
for _, signature in sorted(
Expand All @@ -146,6 +176,7 @@ def build_signature(self) -> bytes:
)
]
)
return valid_encoded + confirmations_signature


class SafeOperationConfirmation(TimeStampedModel):
Expand All @@ -154,7 +185,7 @@ class SafeOperationConfirmation(TimeStampedModel):
on_delete=models.CASCADE,
related_name="confirmations",
)
owner = EthereumAddressV2Field()
owner = EthereumAddressBinaryField()
signature = HexV2Field(null=True, default=None, max_length=SIGNATURE_LENGTH)
signature_type = models.PositiveSmallIntegerField(
choices=[(tag.value, tag.name) for tag in SafeSignatureType], db_index=True
Expand Down
Loading

0 comments on commit b3b3583

Please sign in to comment.