diff --git a/bridge_indexer/handlers/bridge_matcher.py b/bridge_indexer/handlers/bridge_matcher.py index 1c5027d..4226f53 100644 --- a/bridge_indexer/handlers/bridge_matcher.py +++ b/bridge_indexer/handlers/bridge_matcher.py @@ -1,32 +1,48 @@ from datetime import timedelta -from bridge_indexer.models import BridgeDepositTransaction -from bridge_indexer.models import BridgeWithdrawTransaction -from bridge_indexer.models import EtherlinkDepositEvent -from bridge_indexer.models import EtherlinkWithdrawEvent -from bridge_indexer.models import TezosDepositEvent -from bridge_indexer.models import TezosWithdrawEvent +from bridge_indexer.models import BridgeDepositOperation +from bridge_indexer.models import BridgeOperation +from bridge_indexer.models import BridgeOperationType +from bridge_indexer.models import BridgeWithdrawOperation +from bridge_indexer.models import EtherlinkDepositOperation +from bridge_indexer.models import EtherlinkWithdrawOperation +from bridge_indexer.models import TezosDepositOperation +from bridge_indexer.models import TezosWithdrawOperation + +LAYERS_TIMESTAMP_GAP = timedelta(minutes=5) class BridgeMatcher: @staticmethod async def check_pending_tezos_deposits(): - qs = TezosDepositEvent.filter(bridge_deposits__isnull=True) + qs = TezosDepositOperation.filter(bridge_deposits__isnull=True) async for l1_deposit in qs: - await BridgeDepositTransaction.create(l1_transaction=l1_deposit) + bridge_deposit = await BridgeDepositOperation.create(l1_transaction=l1_deposit) + await BridgeOperation.create( + id=bridge_deposit.id, + type=BridgeOperationType.deposit, + l1_account=l1_deposit.l1_account, + l2_account=l1_deposit.l2_account, + ) @staticmethod async def check_pending_etherlink_withdrawals(): - qs = EtherlinkWithdrawEvent.filter(bridge_withdrawals__isnull=True) + qs = EtherlinkWithdrawOperation.filter(bridge_withdrawals__isnull=True) async for l2_withdrawal in qs: - await BridgeWithdrawTransaction.create(l2_transaction=l2_withdrawal) + bridge_withdrawal = await BridgeWithdrawOperation.create(l2_transaction=l2_withdrawal) + await BridgeOperation.create( + id=bridge_withdrawal.id, + type=BridgeOperationType.withdrawal, + l1_account=l2_withdrawal.l1_account, + l2_account=l2_withdrawal.l2_account, + ) @staticmethod async def check_pending_etherlink_deposits(): - qs = EtherlinkDepositEvent.filter(bridge_deposits__isnull=True).order_by('level', 'transaction_index') + qs = EtherlinkDepositOperation.filter(bridge_deposits__isnull=True).order_by('level', 'transaction_index') async for l2_deposit in qs: bridge_deposit = ( - await BridgeDepositTransaction.filter( + await BridgeDepositOperation.filter( l2_transaction=None, l1_transaction__inbox_message_id=l2_deposit.inbox_message_id, ) @@ -39,7 +55,14 @@ async def check_pending_etherlink_deposits(): bridge_deposit.l2_transaction = l2_deposit await bridge_deposit.save() - qs = EtherlinkDepositEvent.filter( + bridge_operation = await BridgeOperation.get(id=bridge_deposit.id) + bridge_operation.is_completed = True + bridge_operation.is_successful = l2_deposit.l2_token is not None + await bridge_operation.save() + + @staticmethod + async def check_pending_etherlink_xtz_deposits(): + qs = EtherlinkDepositOperation.filter( bridge_deposits__isnull=True, inbox_message_id__isnull=True, l2_token_id='xtz', @@ -47,7 +70,7 @@ async def check_pending_etherlink_deposits(): async for l2_deposit in qs: await l2_deposit.fetch_related('l2_token', 'l2_token__ticket') bridge_deposit = ( - await BridgeDepositTransaction.filter( + await BridgeDepositOperation.filter( l2_transaction=None, l1_transaction__inbox_message_id__gt=0, l1_transaction__ticket=l2_deposit.l2_token.ticket, @@ -69,11 +92,16 @@ async def check_pending_etherlink_deposits(): bridge_deposit.l2_transaction = l2_deposit await bridge_deposit.save() + bridge_operation = await BridgeOperation.get(id=bridge_deposit.id) + bridge_operation.is_completed = True + bridge_operation.is_successful = l2_deposit.l2_token is not None + await bridge_operation.save() + @staticmethod async def check_pending_tezos_withdrawals(): - qs = TezosWithdrawEvent.filter(bridge_withdrawals__isnull=True).order_by('level') + qs = TezosWithdrawOperation.filter(bridge_withdrawals__isnull=True).order_by('level') async for l1_withdrawal in qs: - bridge_withdrawal = await BridgeWithdrawTransaction.filter( + bridge_withdrawal = await BridgeWithdrawOperation.filter( l1_transaction=None, l2_transaction__outbox_message_id=l1_withdrawal.outbox_message_id, ).first() @@ -84,6 +112,11 @@ async def check_pending_tezos_withdrawals(): bridge_withdrawal.l1_transaction = l1_withdrawal await bridge_withdrawal.save() + bridge_operation = await BridgeOperation.get(id=bridge_withdrawal.id) + bridge_operation.is_completed = True + bridge_operation.is_successful = True + await bridge_operation.save() + @staticmethod async def check_pending_transactions(): await BridgeMatcher.check_pending_tezos_deposits() diff --git a/bridge_indexer/handlers/etherlink/on_deposit.py b/bridge_indexer/handlers/etherlink/on_deposit.py index 65d25f6..aeb7d98 100644 --- a/bridge_indexer/handlers/etherlink/on_deposit.py +++ b/bridge_indexer/handlers/etherlink/on_deposit.py @@ -8,7 +8,7 @@ from bridge_indexer.handlers.bridge_matcher import BridgeMatcher from bridge_indexer.handlers.rollup_message import InboxMessageService -from bridge_indexer.models import EtherlinkDepositEvent +from bridge_indexer.models import EtherlinkDepositOperation from bridge_indexer.models import EtherlinkToken from bridge_indexer.models import TezosTicket from bridge_indexer.types.kernel.evm_events.deposit import Deposit @@ -44,7 +44,7 @@ async def on_deposit( inbox_message = await InboxMessageService.find_by_index(event.payload.inbox_level, event.payload.inbox_msg_id, ctx) - await EtherlinkDepositEvent.create( + await EtherlinkDepositOperation.create( timestamp=datetime.fromtimestamp(event.data.timestamp, tz=timezone.utc), level=event.data.level, address=event.data.address[-40:], diff --git a/bridge_indexer/handlers/etherlink/on_withdraw.py b/bridge_indexer/handlers/etherlink/on_withdraw.py index 9e95673..0f0fe9f 100644 --- a/bridge_indexer/handlers/etherlink/on_withdraw.py +++ b/bridge_indexer/handlers/etherlink/on_withdraw.py @@ -1,14 +1,14 @@ +from dipdup.context import HandlerContext +from dipdup.models import Index +from dipdup.models import IndexStatus +from dipdup.models.evm_subsquid import SubsquidEvent from tortoise.exceptions import DoesNotExist from bridge_indexer.handlers.bridge_matcher import BridgeMatcher from bridge_indexer.handlers.rollup_message import OutboxMessageService from bridge_indexer.models import EtherlinkToken -from bridge_indexer.models import EtherlinkWithdrawEvent +from bridge_indexer.models import EtherlinkWithdrawOperation from bridge_indexer.types.kernel.evm_events.withdrawal import Withdrawal -from dipdup.context import HandlerContext -from dipdup.models import Index -from dipdup.models import IndexStatus -from dipdup.models.evm_subsquid import SubsquidEvent async def on_withdraw( @@ -33,7 +33,7 @@ async def on_withdraw( ) return - await EtherlinkWithdrawEvent.create( + await EtherlinkWithdrawOperation.create( timestamp=event.data.timestamp, level=event.data.level, address=event.data.address[-40:], diff --git a/bridge_indexer/handlers/etherlink/on_xtz_deposit.py b/bridge_indexer/handlers/etherlink/on_xtz_deposit.py index c532353..1bd8577 100644 --- a/bridge_indexer/handlers/etherlink/on_xtz_deposit.py +++ b/bridge_indexer/handlers/etherlink/on_xtz_deposit.py @@ -1,7 +1,6 @@ from datetime import datetime from datetime import timezone -from bridge_indexer.models import TezosTicket from dipdup.context import HandlerContext from dipdup.models import Index from dipdup.models import IndexStatus @@ -9,8 +8,9 @@ from dipdup.models.evm_subsquid import SubsquidTransactionData from bridge_indexer.handlers.bridge_matcher import BridgeMatcher -from bridge_indexer.models import EtherlinkDepositEvent +from bridge_indexer.models import EtherlinkDepositOperation from bridge_indexer.models import EtherlinkToken +from bridge_indexer.models import TezosTicket async def on_xtz_deposit( @@ -31,7 +31,7 @@ async def on_xtz_deposit( etherlink_token = await EtherlinkToken.get(id='xtz') tezos_ticket = await TezosTicket.get(token_id='xtz') - await EtherlinkDepositEvent.create( + await EtherlinkDepositOperation.create( timestamp=datetime.fromtimestamp(transaction.timestamp, tz=timezone.utc), level=transaction.level, address=transaction.from_[-40:], diff --git a/bridge_indexer/handlers/tezos/on_rollup_call.py b/bridge_indexer/handlers/tezos/on_rollup_call.py index 315a8da..c0e8abc 100644 --- a/bridge_indexer/handlers/tezos/on_rollup_call.py +++ b/bridge_indexer/handlers/tezos/on_rollup_call.py @@ -11,7 +11,7 @@ from bridge_indexer.handlers.bridge_matcher import BridgeMatcher from bridge_indexer.handlers.rollup_message import InboxMessageService -from bridge_indexer.models import TezosDepositEvent +from bridge_indexer.models import TezosDepositOperation from bridge_indexer.models import TezosTicket from bridge_indexer.models import TezosToken from bridge_indexer.types.rollup.tezos_parameters.default import LL @@ -116,7 +116,7 @@ async def on_rollup_call( inbox_message = await InboxMessageService.match_transaction_with_inbox(default.data, ctx) - await TezosDepositEvent.create( + await TezosDepositOperation.create( timestamp=default.data.timestamp, level=default.data.level, operation_hash=default.data.hash, diff --git a/bridge_indexer/handlers/tezos/on_rollup_execute.py b/bridge_indexer/handlers/tezos/on_rollup_execute.py index 77f5e62..357a1cb 100644 --- a/bridge_indexer/handlers/tezos/on_rollup_execute.py +++ b/bridge_indexer/handlers/tezos/on_rollup_execute.py @@ -7,7 +7,7 @@ from bridge_indexer.handlers.bridge_matcher import BridgeMatcher from bridge_indexer.handlers.rollup_message import OutboxMessageService -from bridge_indexer.models import TezosWithdrawEvent +from bridge_indexer.models import TezosWithdrawOperation from bridge_indexer.types.output_proof.output_proof import OutputProofData from bridge_indexer.types.ticketer.tezos_parameters.withdraw import WithdrawParameter from bridge_indexer.types.ticketer.tezos_storage import TicketerStorage @@ -51,7 +51,7 @@ async def on_rollup_execute( ) return - await TezosWithdrawEvent.create( + await TezosWithdrawOperation.create( timestamp=execute.data.timestamp, level=execute.data.level, operation_hash=execute.data.hash, diff --git a/bridge_indexer/models/__init__.py b/bridge_indexer/models/__init__.py index f815868..04b6340 100644 --- a/bridge_indexer/models/__init__.py +++ b/bridge_indexer/models/__init__.py @@ -1,11 +1,17 @@ import uuid +from enum import Enum from dipdup import fields from dipdup.models import Model from tortoise import ForeignKeyFieldInstance -class BlockchainAbstractOperation(Model): +class DatetimeModelMixin: + created_at = fields.DatetimeField(index=True, auto_now_add=True) + updated_at = fields.DatetimeField(index=True, auto_now=True) + + +class AbstractBlockchainOperation(Model): class Meta: abstract = True @@ -58,7 +64,7 @@ class Meta: ) -class RollupCommitment(Model): +class RollupCommitment(DatetimeModelMixin, Model): class Meta: table = 'rollup_commitment' model = 'models.RollupCommitment' @@ -76,7 +82,7 @@ class Meta: outbox_messages: fields.ReverseRelation['RollupOutboxMessage'] -class AbstractRollupMessage(Model): +class AbstractRollupMessage(DatetimeModelMixin, Model): class Meta: abstract = True unique_together = ( @@ -99,8 +105,8 @@ class Meta: parameter = fields.JSONField() payload = fields.TextField(null=True) - l1_deposits: fields.ReverseRelation['TezosDepositEvent'] - l2_deposits: fields.ReverseRelation['EtherlinkDepositEvent'] + l1_deposits: fields.ReverseRelation['TezosDepositOperation'] + l2_deposits: fields.ReverseRelation['EtherlinkDepositOperation'] class RollupOutboxMessage(AbstractRollupMessage): @@ -120,11 +126,11 @@ class Meta: null=True, ) - l1_withdrawals: fields.ReverseRelation['TezosWithdrawEvent'] - l2_withdrawals: fields.ReverseRelation['EtherlinkWithdrawEvent'] + l1_withdrawals: fields.ReverseRelation['TezosWithdrawOperation'] + l2_withdrawals: fields.ReverseRelation['EtherlinkWithdrawOperation'] -class TezosAbstractOperation(BlockchainAbstractOperation): +class AbstractTezosOperation(AbstractBlockchainOperation): class Meta: abstract = True @@ -136,10 +142,10 @@ class Meta: target = fields.CharField(max_length=36) -class TezosDepositEvent(TezosAbstractOperation): +class TezosDepositOperation(AbstractTezosOperation): class Meta: table = 'l1_deposit' - model = 'models.TezosDepositEvent' + model = 'models.TezosDepositOperation' l1_account = fields.CharField(max_length=36) l2_account = fields.CharField(max_length=40) @@ -156,13 +162,13 @@ class Meta: unique=True, ) - bridge_deposits: fields.ReverseRelation['BridgeDepositTransaction'] + bridge_deposits: fields.ReverseRelation['BridgeDepositOperation'] -class TezosWithdrawEvent(TezosAbstractOperation): +class TezosWithdrawOperation(AbstractTezosOperation): class Meta: table = 'l1_withdrawal' - model = 'models.TezosWithdrawEvent' + model = 'models.TezosWithdrawOperation' outbox_message: ForeignKeyFieldInstance[RollupOutboxMessage] = fields.ForeignKeyField( model_name=RollupOutboxMessage.Meta.model, @@ -171,10 +177,10 @@ class Meta: unique=True, ) - bridge_withdrawals: fields.ReverseRelation['BridgeWithdrawTransaction'] + bridge_withdrawals: fields.ReverseRelation['BridgeWithdrawOperation'] -class EtherlinkAbstractEvent(BlockchainAbstractOperation): +class AbstractEtherlinkOperation(AbstractBlockchainOperation): class Meta: abstract = True @@ -184,10 +190,10 @@ class Meta: address = fields.CharField(max_length=40) -class EtherlinkDepositEvent(EtherlinkAbstractEvent): +class EtherlinkDepositOperation(AbstractEtherlinkOperation): class Meta: table = 'l2_deposit' - model = 'models.EtherlinkDepositEvent' + model = 'models.EtherlinkDepositOperation' l2_account = fields.CharField(max_length=40) l2_token: ForeignKeyFieldInstance[EtherlinkToken] = fields.ForeignKeyField( @@ -210,13 +216,13 @@ class Meta: null=True, ) - bridge_deposits: fields.ReverseRelation['BridgeDepositTransaction'] + bridge_deposits: fields.ReverseRelation['BridgeDepositOperation'] -class EtherlinkWithdrawEvent(EtherlinkAbstractEvent): +class EtherlinkWithdrawOperation(AbstractEtherlinkOperation): class Meta: table = 'l2_withdrawal' - model = 'models.EtherlinkWithdrawEvent' + model = 'models.EtherlinkWithdrawOperation' l2_account = fields.CharField(max_length=40) l1_account = fields.CharField(max_length=36) @@ -239,51 +245,80 @@ class Meta: unique=True, ) - bridge_withdrawals: fields.ReverseRelation['BridgeWithdrawTransaction'] + bridge_withdrawals: fields.ReverseRelation['BridgeWithdrawOperation'] -class BridgeDepositTransaction(Model): +class AbstractBridgeOperation(DatetimeModelMixin, Model): class Meta: - table = 'bridge_deposit' - model = 'models.BridgeDepositTransaction' + abstract = True + ordering = ['-created_at'] id = fields.UUIDField(pk=True) - l1_transaction: ForeignKeyFieldInstance[TezosDepositEvent] = fields.ForeignKeyField( - model_name=TezosDepositEvent.Meta.model, + + +class BridgeOperationType(Enum): + deposit: str = 'deposit' + withdrawal: str = 'withdrawal' + + +class BridgeOperation(AbstractBridgeOperation): + class Meta: + table = 'bridge_operation' + model = 'models.BridgeOperation' + + l1_account = fields.CharField(max_length=36, index=True) + l2_account = fields.CharField(max_length=40, index=True) + type = fields.EnumField(enum_type=BridgeOperationType, index=True) + is_completed = fields.BooleanField(default=False, index=True) + is_successful = fields.BooleanField(default=False, index=True) + + +class BridgeDepositOperation(AbstractBridgeOperation): + class Meta: + table = 'bridge_deposit' + model = 'models.BridgeDepositOperation' + + l1_transaction: ForeignKeyFieldInstance[TezosDepositOperation] = fields.ForeignKeyField( + model_name=TezosDepositOperation.Meta.model, source_field='l1_transaction_id', to_field='id', unique=True, ) - l2_transaction: ForeignKeyFieldInstance[EtherlinkDepositEvent] = fields.ForeignKeyField( - model_name=EtherlinkDepositEvent.Meta.model, + l2_transaction: ForeignKeyFieldInstance[EtherlinkDepositOperation] = fields.ForeignKeyField( + model_name=EtherlinkDepositOperation.Meta.model, source_field='l2_transaction_id', to_field='id', null=True, unique=True, ) - updated_at = fields.DatetimeField(index=True, auto_now=True) -class BridgeWithdrawTransaction(Model): +class BridgeWithdrawOperation(AbstractBridgeOperation): class Meta: table = 'bridge_withdrawal' - model = 'models.BridgeWithdrawTransaction' + model = 'models.BridgeWithdrawOperation' - id = fields.UUIDField(pk=True) - l1_transaction: ForeignKeyFieldInstance[TezosWithdrawEvent] = fields.ForeignKeyField( - model_name=TezosWithdrawEvent.Meta.model, + l1_transaction: ForeignKeyFieldInstance[TezosWithdrawOperation] = fields.ForeignKeyField( + model_name=TezosWithdrawOperation.Meta.model, source_field='l1_transaction_id', to_field='id', null=True, unique=True, ) - l2_transaction: ForeignKeyFieldInstance[EtherlinkWithdrawEvent] = fields.ForeignKeyField( - model_name=EtherlinkWithdrawEvent.Meta.model, + l2_transaction: ForeignKeyFieldInstance[EtherlinkWithdrawOperation] = fields.ForeignKeyField( + model_name=EtherlinkWithdrawOperation.Meta.model, source_field='l2_transaction_id', to_field='id', unique=True, ) - updated_at = fields.DatetimeField(index=True, auto_now=True) + + +class BridgeTransaction(Model): + class Meta: + table = 'bridge_transaction' + model = 'models.BridgeTransaction' + + id = fields.UUIDField(pk=True) class EtherlinkTokenHolder(Model):