Skip to content

Commit

Permalink
feat: implement blockchain transaction handling system
Browse files Browse the repository at this point in the history
  • Loading branch information
gianlucapagliara committed Jan 11, 2025
1 parent 6a09c1a commit c85db20
Show file tree
Hide file tree
Showing 6 changed files with 737 additions and 0 deletions.
Empty file.
79 changes: 79 additions & 0 deletions financepype/operations/transactions/events.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
from dataclasses import dataclass
from enum import Enum


class BlockchainTransactionEvent(Enum):
"""
Enumeration of possible blockchain transaction events.
Each event represents a specific state or transition in the transaction lifecycle:
- TransactionRejected (901): Transaction was rejected by the network
- TransactionBroadcasted (902): Transaction was successfully broadcast to the network
- TransactionConfirmed (903): Transaction was confirmed in a block
- TransactionFinalized (904): Transaction is considered final (enough confirmations)
- TransactionFailed (905): Transaction execution failed
- TransactionCancelled (906): Transaction was cancelled by the user
"""

TransactionRejected = 901
TransactionBroadcasted = 902
TransactionConfirmed = 903
TransactionFinalized = 904
TransactionFailed = 905
TransactionCancelled = 906


@dataclass
class TransactionEvent:
"""
Base class for all transaction events.
Attributes:
timestamp (float): Unix timestamp when the event occurred
client_operation_id (str): Unique identifier for the transaction operation
"""

timestamp: float
client_operation_id: str


@dataclass
class TransactionBroadcastedEvent(TransactionEvent):
"""Event emitted when a transaction is successfully broadcast to the blockchain network."""

pass


@dataclass
class TransactionConfirmedEvent(TransactionEvent):
"""Event emitted when a transaction is confirmed in a block on the blockchain."""

pass


@dataclass
class TransactionFinalizedEvent(TransactionEvent):
"""Event emitted when a transaction has received enough confirmations to be considered final."""

pass


@dataclass
class TransactionFailedEvent(TransactionEvent):
"""Event emitted when a transaction execution fails on the blockchain."""

pass


@dataclass
class TransactionRejectedEvent(TransactionEvent):
"""Event emitted when a transaction is rejected by the blockchain network."""

pass


@dataclass
class TransactionCancelledEvent(TransactionEvent):
"""Event emitted when a transaction is cancelled by the user before execution."""

pass
84 changes: 84 additions & 0 deletions financepype/operations/transactions/models.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
from enum import Enum
from typing import Any

from pydantic import BaseModel, ConfigDict, Field

from financepype.assets.blockchain import BlockchainAsset
from financepype.operations.fees import FeeImpactType, FeeType, OperationFee
from financepype.operators.blockchains.identifier import BlockchainIdentifier


class BlockchainTransactionState(Enum):
"""
Enumeration of possible blockchain transaction states.
States represent the current status of a transaction in its lifecycle:
- PENDING_BROADCAST: Transaction created but not yet sent to the network
- BROADCASTED: Transaction sent to the network, awaiting confirmation
- CONFIRMED: Transaction included in a block and confirmed
- FINALIZED: Transaction has enough confirmations to be considered immutable
- FAILED: Transaction execution failed on the blockchain
- REJECTED: Transaction rejected during simulation or by network
- CANCELLED: Transaction cancelled by the user before execution
"""

PENDING_BROADCAST = "pending" # Not processed by a provider
BROADCASTED = "broadcasted" # Sent to a provider, but not confirmed yet
CONFIRMED = "completed" # Confirmed on the blockchain
FINALIZED = "finalized" # Confirmed and unchangeable on the blockchain
FAILED = "failed" # Processed by a provider, but failed
REJECTED = "rejected" # Rejected in the simulation or by a provider
CANCELLED = "cancelled" # Cancelled by the user


class BlockchainTransactionFee(OperationFee):
"""
Represents the fee structure for a blockchain transaction.
Attributes:
asset (BlockchainAsset | None): The asset in which the fee is paid
fee_type (FeeType): Type of fee (always ABSOLUTE)
impact_type (FeeImpactType): How the fee impacts the transaction cost (always ADDED_TO_COSTS)
"""

asset: BlockchainAsset | None = None
fee_type: FeeType = Field(default=FeeType.ABSOLUTE, init=False)
impact_type: FeeImpactType = Field(default=FeeImpactType.ADDED_TO_COSTS, init=False)


class BlockchainTransactionReceipt(BaseModel):
"""
Immutable receipt of a processed blockchain transaction.
Attributes:
transaction_id (BlockchainIdentifier): Unique identifier of the transaction
data (Any): Additional transaction-specific data from the blockchain
"""

model_config = ConfigDict(frozen=True)

transaction_id: BlockchainIdentifier
data: Any


class BlockchainTransactionUpdate(BaseModel):
"""
Represents an update to a blockchain transaction's status.
Attributes:
update_timestamp (float): Unix timestamp of when the update occurred
client_transaction_id (str): Client-side identifier for the transaction
transaction_id (BlockchainIdentifier): Blockchain-specific transaction identifier
new_state (BlockchainTransactionState): Updated state of the transaction
receipt (BlockchainTransactionReceipt | None): Transaction receipt if available
explorer_link (str | None): URL to view transaction in blockchain explorer
other_data (dict[str, Any]): Additional transaction-specific data
"""

update_timestamp: float
client_transaction_id: str
transaction_id: BlockchainIdentifier
new_state: BlockchainTransactionState
receipt: BlockchainTransactionReceipt | None = None
explorer_link: str | None = None
other_data: dict[str, Any] = Field(default_factory=dict)
82 changes: 82 additions & 0 deletions financepype/operations/transactions/proposal.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
from abc import abstractmethod
from collections.abc import Callable
from typing import Any

from financepype.operations.proposal import OperationProposal
from financepype.operations.transactions.transaction import BlockchainTransaction
from financepype.owners.owner_id import OwnerIdentifier


class TransactionProposal(OperationProposal):
"""
A proposal for executing a blockchain transaction.
This class represents a proposed blockchain transaction that can be executed
through a specific wallet. It encapsulates all the necessary information
and validation logic required to execute the transaction.
Attributes:
owner_identifier (OwnerIdentifier): The wallet that will execute the transaction
executed_operation (BlockchainTransaction): The executed transaction (after execution)
"""

owner_identifier: OwnerIdentifier
executed_operation: BlockchainTransaction | None = None

# === Properties ===

@property
def can_be_executed(self) -> bool:
"""
Indicates whether the proposal is ready to be executed.
Returns:
bool: Always True for transaction proposals
"""
return True

@property
@abstractmethod
def execute_function(self) -> Callable[[], BlockchainTransaction]:
"""
The function that will be called to execute the transaction.
Returns:
Callable[[], BlockchainTransaction]: A function that creates and executes the transaction
"""
raise NotImplementedError

@property
@abstractmethod
def execute_kwargs(self) -> dict[str, Any]:
"""
The keyword arguments to pass to the execute function.
Returns:
dict[str, Any]: Dictionary of arguments needed to execute the transaction
"""
raise NotImplementedError

# === Execution ===

def execute(self) -> BlockchainTransaction:
"""
Executes the proposed transaction.
This method creates and executes a blockchain transaction using the
specified wallet and execution parameters.
Returns:
BlockchainTransaction: The executed transaction
Raises:
ValueError: If the proposal has already been executed or cannot be executed
"""
if self.executed:
raise ValueError("Proposal already executed.")

if not self.can_be_executed:
raise ValueError("Proposal not prepared to be executed.")

self.executed_operation = self.execute_function(**self.execute_kwargs)
return self.executed_operation
Loading

0 comments on commit c85db20

Please sign in to comment.