-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge branch 'main' into circulating-supply
- Loading branch information
Showing
11 changed files
with
1,423 additions
and
167 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
[flake8] | ||
max-line-length = 100 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,18 +1,51 @@ | ||
name: pre-commit | ||
name: CI Workflow | ||
|
||
on: [push] | ||
on: [push, pull_request] | ||
|
||
jobs: | ||
|
||
# Linting with Pre-commit | ||
lint: | ||
runs-on: ubuntu-latest | ||
|
||
steps: | ||
- uses: actions/checkout@v3 | ||
|
||
- name: Setup Python 3.10.4 | ||
- name: Setup Python 3.12.6 | ||
uses: actions/setup-python@v4 | ||
with: | ||
python-version: 3.10.4 | ||
python-version: 3.12.6 | ||
|
||
# Run pre-commit hooks (e.g., black, flake8, isort) | ||
- uses: pre-commit/[email protected] | ||
|
||
|
||
# Test runs | ||
tests: | ||
runs-on: ubuntu-latest | ||
timeout-minutes: 30 | ||
strategy: | ||
fail-fast: false | ||
|
||
steps: | ||
- uses: actions/checkout@v3 | ||
|
||
# install poetry to allow cacheing | ||
- name: Install poetry | ||
run: pipx install poetry | ||
|
||
- name: Setup Python 3.12.6 | ||
uses: actions/setup-python@v5 | ||
with: | ||
python-version: 3.12.6 | ||
cache: 'poetry' | ||
- name: Install Requirements | ||
run: | | ||
poetry config virtualenvs.in-project true | ||
poetry install | ||
- name: Run Tests | ||
env: | ||
ETH_RPC_URL: ${{ secrets.ETH_RPC_URL }} | ||
ETHERSCAN_API_KEY: ${{ secrets.ETHERSCAN_API_KEY }} | ||
run: | | ||
poetry run pytest -n auto |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,53 @@ | ||
.idea | ||
__pycache__ | ||
# Byte-compiled / optimized / DLL files | ||
__pycache__/ | ||
*.py[cod] | ||
*$py.class | ||
|
||
# Distribution / packaging | ||
.Python | ||
build/ | ||
dist/ | ||
*.egg-info/ | ||
.eggs/ | ||
*.egg | ||
*.wheel | ||
MANIFEST | ||
|
||
# Unit test / coverage reports | ||
.tox/ | ||
.nox/ | ||
.coverage | ||
coverage.xml | ||
*.cover | ||
*.py,cover | ||
.pytest_cache/ | ||
htmlcov/ | ||
|
||
# Environments | ||
.env | ||
.venv | ||
env/ | ||
venv/ | ||
ENV/ | ||
env.bak/ | ||
venv.bak/ | ||
|
||
# Jupyter Notebook | ||
.ipynb_checkpoints/ | ||
|
||
# Pyre type checker | ||
.pyre/ | ||
|
||
# macOS specific files | ||
.DS_Store | ||
|
||
# IDE files | ||
.idea/ | ||
.vscode/ | ||
|
||
# Log files | ||
*.log | ||
|
||
# Temporary files | ||
*.tmp | ||
*.swp |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,143 @@ | ||
# pragma version ~=0.4 | ||
|
||
""" | ||
@title Time Weighted Average (TWA) Calculator Vyper Module | ||
@notice This contract stores snapshots of a tracked value at specific timestamps and computes the Time Weighted Average (TWA) over a defined time window. | ||
@dev | ||
- Snapshots Storage: Stores snapshots of a tracked value along with their timestamps in a dynamic array, | ||
ensuring snapshots are only added if a minimum time interval (`min_snapshot_dt_seconds`) has passed since the last snapshot. | ||
- TWA Calculation: Computes the TWA by iterating over the stored snapshots in reverse chronological order. | ||
It uses the trapezoidal rule to calculate the weighted average of the tracked value over the specified time window (`twa_window`). | ||
- Functions: | ||
- `store_snapshot`: Internal function to store a new snapshot of the tracked value if the minimum time interval has passed. | ||
!!!Wrapper must be implemented in importing contract. | ||
- `compute_twa`: External view function that calculates and returns the TWA based on the stored snapshots. | ||
- `get_len_snapshots`: External view function that returns the total number of snapshots stored. | ||
- **Usage**: Ideal for tracking metrics like staked supply rates, token prices, or any other value that changes over time and requires averaging over a period. | ||
""" | ||
|
||
MAX_SNAPSHOTS: constant(uint256) = 10**18 # 31.7 billion years if snapshot every second | ||
|
||
snapshots: public(DynArray[Snapshot, MAX_SNAPSHOTS]) | ||
min_snapshot_dt_seconds: public(uint256) # Minimum time between snapshots in seconds | ||
twa_window: public(uint256) # Time window in seconds for TWA calculation | ||
last_snapshot_timestamp: public(uint256) # Timestamp of the last snapshot (assigned in RewardsHandler) | ||
|
||
|
||
struct Snapshot: | ||
tracked_value: uint256 # In 1e18 precision | ||
timestamp: uint256 | ||
|
||
|
||
@deploy | ||
def __init__(_twa_window: uint256, _min_snapshot_dt_seconds: uint256): | ||
self.twa_window = _twa_window # one week (in seconds) | ||
self.min_snapshot_dt_seconds = _min_snapshot_dt_seconds # >=1s to prevent spamming | ||
|
||
|
||
@internal | ||
def store_snapshot(_value: uint256): | ||
""" | ||
@notice Stores a snapshot of the tracked value. | ||
@param _value The value to store. | ||
""" | ||
if self.last_snapshot_timestamp + self.min_snapshot_dt_seconds <= block.timestamp: | ||
self.last_snapshot_timestamp = block.timestamp | ||
self.snapshots.append( | ||
Snapshot(tracked_value=_value, timestamp=block.timestamp) | ||
) # store the snapshot into the DynArray | ||
|
||
|
||
@external | ||
@view | ||
def get_len_snapshots() -> uint256: | ||
""" | ||
@notice Returns the number of snapshots stored. | ||
@return Number of snapshots. | ||
""" | ||
return len(self.snapshots) | ||
|
||
|
||
@external | ||
@view | ||
def compute_twa() -> uint256: | ||
""" | ||
@notice External endpoint for _compute() function. | ||
""" | ||
return self.compute() | ||
|
||
|
||
@internal | ||
def adjust_twa_window(_new_window: uint256): | ||
""" | ||
@notice Adjusts the TWA window. | ||
@param _new_window The new TWA window in seconds. | ||
@dev Only callable by the importing contract. | ||
""" | ||
self.twa_window = _new_window | ||
|
||
|
||
@internal | ||
def adjust_min_snapshot_dt_seconds(_new_dt_seconds: uint256): | ||
""" | ||
@notice Adjusts the minimum snapshot time interval. | ||
@param _new_dt_seconds The new minimum snapshot time interval in seconds. | ||
@dev Only callable by the importing contract. | ||
""" | ||
self.min_snapshot_dt_seconds = _new_dt_seconds | ||
|
||
|
||
@internal | ||
@view | ||
def compute() -> uint256: | ||
""" | ||
@notice Computes the TWA over the specified time window by iterating backwards over the snapshots. | ||
@return The TWA for tracked value over the self.twa_window (10**18 decimals precision). | ||
""" | ||
num_snapshots: uint256 = len(self.snapshots) | ||
if num_snapshots == 0: | ||
return 0 | ||
|
||
time_window_start: uint256 = block.timestamp - self.twa_window | ||
|
||
total_weighted_tracked_value: uint256 = 0 | ||
total_time: uint256 = 0 | ||
|
||
# Iterate backwards over all snapshots | ||
index_array_end: uint256 = num_snapshots - 1 | ||
for i: uint256 in range(0, num_snapshots, bound=MAX_SNAPSHOTS): # i from 0 to (num_snapshots-1) | ||
i_backwards: uint256 = index_array_end - i | ||
current_snapshot: Snapshot = self.snapshots[i_backwards] | ||
next_snapshot: Snapshot = current_snapshot | ||
if i != 0: # If not the first iteration, get the next snapshot | ||
next_snapshot = self.snapshots[i_backwards + 1] | ||
|
||
interval_start: uint256 = current_snapshot.timestamp | ||
# Adjust interval start if it is before the time window start | ||
if interval_start < time_window_start: | ||
interval_start = time_window_start | ||
|
||
interval_end: uint256 = 0 | ||
if i == 0: # First iteration - we are on the last snapshot (i_backwards = num_snapshots - 1) | ||
# For the last snapshot, interval end is block.timestamp | ||
interval_end = block.timestamp | ||
else: | ||
# For other snapshots, interval end is the timestamp of the next snapshot | ||
interval_end = next_snapshot.timestamp | ||
|
||
if interval_end <= time_window_start: | ||
break | ||
|
||
time_delta: uint256 = interval_end - interval_start | ||
|
||
# Interpolation using the trapezoidal rule | ||
averaged_tracked_value: uint256 = (current_snapshot.tracked_value + next_snapshot.tracked_value) // 2 | ||
|
||
# Accumulate weighted rate and time | ||
total_weighted_tracked_value += averaged_tracked_value * time_delta | ||
total_time += time_delta | ||
|
||
assert total_time > 0, "Zero total time!" | ||
twa: uint256 = total_weighted_tracked_value // total_time | ||
|
||
return twa |
This file was deleted.
Oops, something went wrong.
Oops, something went wrong.