lockdrop-simulation/tests/test_accrual_state.py
2025-08-04 14:10:17 +05:30

100 lines
4.8 KiB
Python

import unittest
from tabulate import tabulate
from base_test import BaseAllocationTest
class AccrualStateTest(BaseAllocationTest):
"""Test accrual state calculations"""
def test_accrual_state_calculation(self):
"""Test accrual state calculations after some blocks"""
print("\nACCRUAL STATE CALCULATIONS")
# Get latest block height for testing
test_block_height = self._get_latest_block_height_from_api()
if not test_block_height:
self.skipTest("Could not retrieve latest block height")
# Test first star and galaxy from each lock period (limit to avoid too many API calls)
test_points = []
for lock_period in sorted(self.points_by_duration.keys()):
duration_data = self.points_by_duration[lock_period]
if duration_data['star']:
star_data = duration_data['star']
zenith_addr = star_data['zenith_address']
if zenith_addr:
test_points.append((star_data['point'], zenith_addr, lock_period))
if duration_data['galaxy']:
galaxy_data = duration_data['galaxy']
zenith_addr = galaxy_data['zenith_address']
if zenith_addr:
test_points.append((galaxy_data['point'], zenith_addr, lock_period))
# Collect data for table
accrual_data = []
for point, zenith_addr, lock_period in test_points:
with self.subTest(point=point, block_height=test_block_height):
# Get allocation data first
allocation_data = self._get_point_allocation_from_api(zenith_addr, point)
self.assertIsNotNone(allocation_data, f"No allocation data for {point}")
unlock_schedule = allocation_data.get('unlock_schedule')
self.assertIsNotNone(unlock_schedule, f"No unlock_schedule for {point}")
# Get accrual state at test block height
accrual_state = self._get_point_accrual_state_from_api(zenith_addr, point, test_block_height)
self.assertIsNotNone(accrual_state, f"No accrual state for {point} at block {test_block_height}")
# Extract values for calculation
total_allocation = int(allocation_data['allocated_amount']['amount'])
initial_unlock_amount = int(unlock_schedule['initial_unlock_amount']['amount'])
unlock_blocks = int(unlock_schedule['unlock_blocks'])
api_total_unlocked = int(accrual_state['total_unlocked']['amount'])
last_unlock_block = int(accrual_state['last_unlock_block'])
# Calculate expected last unlock block using unlock_frequency
expected_last_unlock_block = (test_block_height // self.unlock_frequency_blocks) * self.unlock_frequency_blocks
# Assert on last unlock block
self.assertEqual(expected_last_unlock_block, last_unlock_block,
f"Last unlock block mismatch for {point} at block {test_block_height}: "
f"Expected={expected_last_unlock_block}, "
f"zenithd={last_unlock_block}, "
f"Diff={last_unlock_block - expected_last_unlock_block}")
# Calculate expected total_unlocked using expected last unlock block
# Formula: initial_unlock_amount + (expected_last_unlock_block * remaining_amount / unlock_blocks)
remaining_amount = total_allocation - initial_unlock_amount
expected_total_unlocked = initial_unlock_amount + (expected_last_unlock_block * remaining_amount // unlock_blocks)
difference = api_total_unlocked - expected_total_unlocked
accrual_data.append([
point,
f"{lock_period} years",
f"Block {test_block_height}",
f"Block {last_unlock_block}",
f"{expected_total_unlocked:,}",
f"{api_total_unlocked:,}",
f"{difference:+,}" if difference != 0 else "0"
])
self.assertEqual(expected_total_unlocked, api_total_unlocked,
f"Total unlocked mismatch for {point} at block {test_block_height}: "
f"Expected={expected_total_unlocked:,} $sZ, "
f"zenithd={api_total_unlocked:,} $sZ, "
f"Diff={difference:+,} $sZ")
# Print table
print(f"\nTotal Unlocked at Block {test_block_height}:")
headers = ["Point", "Lock Period", "Block Height", "Last Unlocked At", "Expected ($sZ)", "zenithd ($sZ)", "Difference"]
print(tabulate(accrual_data, headers=headers, tablefmt="grid"))
if __name__ == "__main__":
unittest.main(verbosity=2)