Skip to content

Commit fb7235c

Browse files
committed
refactor: credit ratio -> price & credit history filters
1 parent 2f169d7 commit fb7235c

File tree

6 files changed

+212
-32
lines changed

6 files changed

+212
-32
lines changed
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
"""rename credit_history ratio column to price
2+
3+
Revision ID: e1f2a3b4c5d6
4+
Revises: d0e1f2a3b4c5
5+
Create Date: 2025-12-01 00:00:00.000000
6+
7+
"""
8+
from alembic import op
9+
import sqlalchemy as sa
10+
11+
12+
# revision identifiers, used by Alembic.
13+
revision = 'e1f2a3b4c5d6'
14+
down_revision = 'd0e1f2a3b4c5'
15+
branch_labels = None
16+
depends_on = None
17+
18+
19+
def upgrade() -> None:
20+
# First, add the new price column
21+
op.add_column('credit_history', sa.Column('price', sa.DECIMAL(), nullable=True))
22+
23+
# Add the new bonus_amount column
24+
op.add_column('credit_history', sa.Column('bonus_amount', sa.BigInteger(), nullable=True))
25+
26+
# Transform data: price calculation depends on payment token
27+
# For ALEPH token: price = 1 / (ratio * 0.8)
28+
# For other tokens: price = 1/ratio
29+
# Only update rows where payment_method is NOT 'credit_expense' or 'credit_transfer'
30+
# and where ratio is not null and not zero
31+
connection = op.get_bind()
32+
connection.execute(sa.text("""
33+
UPDATE credit_history
34+
SET price = CASE
35+
WHEN token = 'ALEPH' THEN ROUND(1.0 / (ratio * 0.8), 18)
36+
ELSE ROUND(1.0 / ratio, 18)
37+
END
38+
WHERE ratio IS NOT NULL
39+
AND ratio != 0
40+
AND (payment_method IS NULL
41+
OR (payment_method != 'credit_expense' AND payment_method != 'credit_transfer'))
42+
"""))
43+
44+
# Update bonus_amount for ALEPH token records
45+
connection.execute(sa.text("""
46+
UPDATE credit_history
47+
SET bonus_amount = TRUNC(amount * 0.20)
48+
WHERE token = 'ALEPH'
49+
AND amount IS NOT NULL
50+
"""))
51+
52+
# Drop the old ratio column
53+
op.drop_column('credit_history', 'ratio')
54+
55+
56+
def downgrade() -> None:
57+
# Add back the ratio column
58+
op.add_column('credit_history', sa.Column('ratio', sa.DECIMAL(), nullable=True))
59+
60+
# Transform data back: reverse price calculation depends on payment token
61+
# For ALEPH token: ratio = (1/price) / 0.8
62+
# For other tokens: ratio = 1/price
63+
connection = op.get_bind()
64+
connection.execute(sa.text("""
65+
UPDATE credit_history
66+
SET ratio = CASE
67+
WHEN token = 'ALEPH' THEN ROUND((1.0 / price) / 0.8, 18)
68+
ELSE ROUND(1.0 / price, 18)
69+
END
70+
WHERE price IS NOT NULL
71+
AND price != 0
72+
AND (payment_method IS NULL
73+
OR (payment_method != 'credit_expense' AND payment_method != 'credit_transfer'))
74+
"""))
75+
76+
# Drop the price column
77+
op.drop_column('credit_history', 'price')
78+
79+
# Drop the bonus_amount column
80+
op.drop_column('credit_history', 'bonus_amount')

src/aleph/db/accessors/balances.py

Lines changed: 73 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -408,8 +408,8 @@ def _bulk_insert_credit_history(
408408
cursor = conn.cursor()
409409

410410
# Column specification for credit history
411-
# Include the new message_timestamp field
412-
copy_columns = "address, amount, credit_ref, credit_index, message_timestamp, last_update, ratio, tx_hash, expiration_date, token, chain, origin, provider, origin_ref, payment_method"
411+
# Include the new message_timestamp and bonus_amount fields
412+
copy_columns = "address, amount, credit_ref, credit_index, message_timestamp, last_update, price, bonus_amount, tx_hash, expiration_date, token, chain, origin, provider, origin_ref, payment_method"
413413

414414
csv_credit_history = StringIO("\n".join(csv_rows))
415415
cursor.copy_expert(
@@ -453,7 +453,7 @@ def update_credit_balances_distribution(
453453
"""
454454
Updates credit balances for distribution messages (aleph_credit_distribution).
455455
456-
Distribution messages include all fields like ratio, tx_hash, provider,
456+
Distribution messages include all fields like price, tx_hash, provider,
457457
payment_method, token, chain, and expiration_date.
458458
"""
459459

@@ -463,7 +463,7 @@ def update_credit_balances_distribution(
463463
for index, credit_entry in enumerate(credits_list):
464464
address = credit_entry["address"]
465465
amount = abs(int(credit_entry["amount"]))
466-
ratio = Decimal(credit_entry["ratio"])
466+
price = Decimal(credit_entry["price"])
467467
tx_hash = credit_entry["tx_hash"]
468468
provider = credit_entry["provider"]
469469

@@ -472,6 +472,7 @@ def update_credit_balances_distribution(
472472
origin = credit_entry.get("origin", "")
473473
origin_ref = credit_entry.get("ref", "")
474474
payment_method = credit_entry.get("payment_method", "")
475+
bonus_amount = credit_entry.get("bonus_amount", "")
475476

476477
# Convert expiration timestamp to datetime
477478

@@ -482,7 +483,7 @@ def update_credit_balances_distribution(
482483
)
483484

484485
csv_rows.append(
485-
f"{address};{amount};{message_hash};{index};{message_timestamp};{last_update};{ratio};{tx_hash};{expiration_date or ''};{token};{chain};{origin};{provider};{origin_ref};{payment_method}"
486+
f"{address};{amount};{message_hash};{index};{message_timestamp};{last_update};{price};{bonus_amount or ''};{tx_hash};{expiration_date or ''};{token};{chain};{origin};{provider};{origin_ref};{payment_method}"
486487
)
487488

488489
_bulk_insert_credit_history(session, csv_rows)
@@ -500,7 +501,7 @@ def update_credit_balances_expense(
500501
Expense messages have negative amounts and can include:
501502
- execution_id (mapped to origin)
502503
- node_id (mapped to tx_hash)
503-
- price (mapped to ratio)
504+
- price (mapped to price)
504505
- time (skipped for now)
505506
- ref (mapped to origin_ref)
506507
"""
@@ -516,11 +517,11 @@ def update_credit_balances_expense(
516517
# Map new fields
517518
origin = credit_entry.get("execution_id", "")
518519
tx_hash = credit_entry.get("node_id", "")
519-
ratio = credit_entry.get("price", "")
520+
price = credit_entry.get("price", "")
520521
# Skip time field for now
521522

522523
csv_rows.append(
523-
f"{address};{amount};{message_hash};{index};{message_timestamp};{last_update};{ratio};{tx_hash};;;;{origin};ALEPH;{origin_ref};credit_expense"
524+
f"{address};{amount};{message_hash};{index};{message_timestamp};{last_update};{price};;{tx_hash};;;;{origin};ALEPH;{origin_ref};credit_expense"
524525
)
525526

526527
_bulk_insert_credit_history(session, csv_rows)
@@ -562,15 +563,15 @@ def update_credit_balances_transfer(
562563

563564
# Add positive entry for recipient (origin = sender, provider = ALEPH, payment_method = credit_transfer)
564565
csv_rows.append(
565-
f"{recipient_address};{amount};{message_hash};{index};{message_timestamp};{last_update};;;{expiration_date or ''};;;{sender_address};ALEPH;;credit_transfer"
566+
f"{recipient_address};{amount};{message_hash};{index};{message_timestamp};{last_update};;;;{expiration_date or ''};;;{sender_address};ALEPH;;credit_transfer"
566567
)
567568
index += 1
568569

569570
# Add negative entry for sender (unless sender is in whitelisted addresses)
570571
# (origin = recipient, provider = ALEPH, payment_method = credit_transfer)
571572
if sender_address not in whitelisted_addresses:
572573
csv_rows.append(
573-
f"{sender_address};{-amount};{message_hash};{index};{message_timestamp};{last_update};;;;;;{recipient_address};ALEPH;;credit_transfer"
574+
f"{sender_address};{-amount};{message_hash};{index};{message_timestamp};{last_update};;;;;;;{recipient_address};ALEPH;;credit_transfer"
574575
)
575576
index += 1
576577

@@ -602,6 +603,13 @@ def get_address_credit_history(
602603
address: str,
603604
page: int = 1,
604605
pagination: int = 0,
606+
tx_hash: Optional[str] = None,
607+
token: Optional[str] = None,
608+
chain: Optional[str] = None,
609+
provider: Optional[str] = None,
610+
origin: Optional[str] = None,
611+
origin_ref: Optional[str] = None,
612+
payment_method: Optional[str] = None,
605613
) -> Sequence[AlephCreditHistoryDb]:
606614
"""
607615
Get paginated credit history entries for a specific address, ordered from newest to oldest.
@@ -611,6 +619,13 @@ def get_address_credit_history(
611619
address: Address to get credit history for
612620
page: Page number (starts at 1)
613621
pagination: Number of entries per page (0 for all entries)
622+
tx_hash: Filter by transaction hash
623+
token: Filter by token
624+
chain: Filter by chain
625+
provider: Filter by provider
626+
origin: Filter by origin
627+
origin_ref: Filter by origin reference
628+
payment_method: Filter by payment method
614629
615630
Returns:
616631
List of credit history entries ordered by message_timestamp desc
@@ -621,6 +636,22 @@ def get_address_credit_history(
621636
.order_by(AlephCreditHistoryDb.message_timestamp.desc())
622637
)
623638

639+
# Apply filters
640+
if tx_hash is not None:
641+
query = query.where(AlephCreditHistoryDb.tx_hash == tx_hash)
642+
if token is not None:
643+
query = query.where(AlephCreditHistoryDb.token == token)
644+
if chain is not None:
645+
query = query.where(AlephCreditHistoryDb.chain == chain)
646+
if provider is not None:
647+
query = query.where(AlephCreditHistoryDb.provider == provider)
648+
if origin is not None:
649+
query = query.where(AlephCreditHistoryDb.origin == origin)
650+
if origin_ref is not None:
651+
query = query.where(AlephCreditHistoryDb.origin_ref == origin_ref)
652+
if payment_method is not None:
653+
query = query.where(AlephCreditHistoryDb.payment_method == payment_method)
654+
624655
if pagination > 0:
625656
query = query.offset((page - 1) * pagination).limit(pagination)
626657

@@ -630,21 +661,51 @@ def get_address_credit_history(
630661
def count_address_credit_history(
631662
session: DbSession,
632663
address: str,
664+
tx_hash: Optional[str] = None,
665+
token: Optional[str] = None,
666+
chain: Optional[str] = None,
667+
provider: Optional[str] = None,
668+
origin: Optional[str] = None,
669+
origin_ref: Optional[str] = None,
670+
payment_method: Optional[str] = None,
633671
) -> int:
634672
"""
635-
Count total credit history entries for a specific address.
673+
Count total credit history entries for a specific address with optional filters.
636674
637675
Args:
638676
session: Database session
639677
address: Address to count credit history for
678+
tx_hash: Filter by transaction hash
679+
token: Filter by token
680+
chain: Filter by chain
681+
provider: Filter by provider
682+
origin: Filter by origin
683+
origin_ref: Filter by origin reference
684+
payment_method: Filter by payment method
640685
641686
Returns:
642-
Total number of credit history entries for the address
687+
Total number of credit history entries for the address matching the filters
643688
"""
644689
query = select(func.count(AlephCreditHistoryDb.credit_ref)).where(
645690
AlephCreditHistoryDb.address == address
646691
)
647692

693+
# Apply filters
694+
if tx_hash is not None:
695+
query = query.where(AlephCreditHistoryDb.tx_hash == tx_hash)
696+
if token is not None:
697+
query = query.where(AlephCreditHistoryDb.token == token)
698+
if chain is not None:
699+
query = query.where(AlephCreditHistoryDb.chain == chain)
700+
if provider is not None:
701+
query = query.where(AlephCreditHistoryDb.provider == provider)
702+
if origin is not None:
703+
query = query.where(AlephCreditHistoryDb.origin == origin)
704+
if origin_ref is not None:
705+
query = query.where(AlephCreditHistoryDb.origin_ref == origin_ref)
706+
if payment_method is not None:
707+
query = query.where(AlephCreditHistoryDb.payment_method == payment_method)
708+
648709
return session.execute(query).scalar_one()
649710

650711

src/aleph/db/models/balances.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,8 @@ class AlephCreditHistoryDb(Base):
4949

5050
address: str = Column(String, nullable=False, index=True)
5151
amount: int = Column(BigInteger, nullable=False)
52-
ratio: Optional[Decimal] = Column(DECIMAL, nullable=True)
52+
price: Optional[Decimal] = Column(DECIMAL, nullable=True)
53+
bonus_amount: Optional[int] = Column(BigInteger, nullable=True)
5354
tx_hash: Optional[str] = Column(String, nullable=True)
5455
token: Optional[str] = Column(String, nullable=True)
5556
chain: Optional[str] = Column(String, nullable=True)

src/aleph/schemas/api/accounts.py

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -123,13 +123,34 @@ class GetAccountCreditHistoryQueryParams(BaseModel):
123123
page: int = Field(
124124
default=DEFAULT_PAGE, ge=1, description="Offset in pages. Starts at 1."
125125
)
126+
tx_hash: Optional[str] = Field(
127+
default=None, description="Filter by transaction hash"
128+
)
129+
token: Optional[str] = Field(
130+
default=None, description="Filter by token"
131+
)
132+
chain: Optional[str] = Field(
133+
default=None, description="Filter by chain"
134+
)
135+
provider: Optional[str] = Field(
136+
default=None, description="Filter by provider"
137+
)
138+
origin: Optional[str] = Field(
139+
default=None, description="Filter by origin"
140+
)
141+
origin_ref: Optional[str] = Field(
142+
default=None, description="Filter by origin reference"
143+
)
144+
payment_method: Optional[str] = Field(
145+
default=None, description="Filter by payment method"
146+
)
126147

127148

128149
class CreditHistoryResponseItem(BaseModel):
129150
model_config = ConfigDict(from_attributes=True)
130151

131152
amount: int
132-
ratio: Optional[Decimal] = None
153+
price: Optional[Decimal] = None
133154
tx_hash: Optional[str] = None
134155
token: Optional[str] = None
135156
chain: Optional[str] = None

src/aleph/web/controllers/accounts.py

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -234,19 +234,36 @@ async def get_account_credit_history(request: web.Request) -> web.Response:
234234
address=address,
235235
page=query_params.page,
236236
pagination=query_params.pagination,
237+
tx_hash=query_params.tx_hash,
238+
token=query_params.token,
239+
chain=query_params.chain,
240+
provider=query_params.provider,
241+
origin=query_params.origin,
242+
origin_ref=query_params.origin_ref,
243+
payment_method=query_params.payment_method,
237244
)
238245

239246
if not credit_history_entries:
240247
raise web.HTTPNotFound(text="No credit history found for this address")
241248

242-
total_entries = count_address_credit_history(session=session, address=address)
249+
total_entries = count_address_credit_history(
250+
session=session,
251+
address=address,
252+
tx_hash=query_params.tx_hash,
253+
token=query_params.token,
254+
chain=query_params.chain,
255+
provider=query_params.provider,
256+
origin=query_params.origin,
257+
origin_ref=query_params.origin_ref,
258+
payment_method=query_params.payment_method,
259+
)
243260

244261
# Convert to response items
245262
history_adapter = TypeAdapter(list[CreditHistoryResponseItem])
246263
credit_history_list = [
247264
{
248265
"amount": entry.amount,
249-
"ratio": entry.ratio,
266+
"price": entry.price,
250267
"tx_hash": entry.tx_hash,
251268
"token": entry.token,
252269
"chain": entry.chain,

0 commit comments

Comments
 (0)