Skip to content
Open
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions constants/kodiak.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
from enum import Enum

KODIAK_API_URL = "https://backend.kodiak.finance"

class KodiakIslandAddress(Enum):
USDE = "0xE5A2ab5D2fb268E5fF43A5564e44c3309609aFF9"
SUSDE = "0xD5B6EA3544a51BfdDa7E6926BdF778339801dFe8"

class Tokens(Enum):
USDE = "0x5d3a1Ff2b6BAb83b63cd9AD0787074081a52ef34"
SUSDE = "0x211Cc4DD073734dA055fbF44a2b4667d5E5fE5d2"
4 changes: 4 additions & 0 deletions constants/summary_columns.py
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,10 @@ class SummaryColumn(Enum):
STRATA_MONEY_SENIOR = ("strata_srusde_pts", SummaryColumnType.ETHENA_PTS)
STRATA_MONEY_JUNIOR = ("strata_jrusde_pts", SummaryColumnType.ETHENA_PTS)

# Kodiak
KODIAK_USDE_PTS = ("kodiak_usde_pts", SummaryColumnType.ETHENA_PTS)
KODIAK_SUSDE_PTS = ("kodiak_susde_pts", SummaryColumnType.ETHENA_PTS)

def __init__(self, column_name: str, col_type: SummaryColumnType):
self.column_name = column_name
self.col_type = col_type
Expand Down
13 changes: 13 additions & 0 deletions integrations/integration_ids.py
Original file line number Diff line number Diff line change
Expand Up @@ -580,6 +580,19 @@ class IntegrationID(Enum):
Token.USDE,
)

# Kodiak
KODIAK_USDE = (
"kodiak_usde",
"Kodiak USDe",
Token.USDE
)

KODIAK_SUSDE = (
"kodiak_susde",
"Kodiak sUSDe",
Token.SUSDE
)

# Uniswap V4
UNISWAP_V4_POOL = (
"uniswap_v4_pool",
Expand Down
16 changes: 16 additions & 0 deletions integrations/kodiak_susde_integration.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
from constants.chains import Chain
from integrations.integration_ids import IntegrationID
from utils.kodiak import KodiakIntegration

if __name__ == "__main__":
integration = KodiakIntegration(
IntegrationID.KODIAK_SUSDE,
1,
Chain.BERACHAIN
)

print(integration.get_block_balances({
1: {
"0x0000000000000000000000000000000000000000": 100.0
}
}, [12886286, 1]))
16 changes: 16 additions & 0 deletions integrations/kodiak_usde_integration.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
from constants.chains import Chain
from integrations.integration_ids import IntegrationID
from utils.kodiak import KodiakIntegration

if __name__ == "__main__":
integration = KodiakIntegration(
IntegrationID.KODIAK_USDE,
1,
Chain.BERACHAIN
)

print(integration.get_block_balances({
1: {
"0x0000000000000000000000000000000000000000": 100.0
}
}, [12886286, 1]))
73 changes: 73 additions & 0 deletions utils/kodiak.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
from copy import deepcopy
import json
Copy link

Copilot AI Nov 10, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The json import is unused. It should be removed as response.json() is a method call on the response object and doesn't require importing the json module.

Suggested change
import json

Copilot uses AI. Check for mistakes.
from typing import Dict, List
from eth_typing import ChecksumAddress
import requests
from constants.chains import Chain
from integrations.integration import Integration
from integrations.integration_ids import IntegrationID
from constants.kodiak import KODIAK_API_URL, KodiakIslandAddress, Tokens

BASE_URL = f"{KODIAK_API_URL}/balances"

class KodiakIntegration(Integration):

def __init__(
self,
integration_id: IntegrationID,
start_block: int,
chain: Chain,
):
if chain not in [Chain.BERACHAIN]:
raise ValueError(f"KodiakIntegration not supported on chain {chain}")

if integration_id is IntegrationID.KODIAK_USDE:
self.island = KodiakIslandAddress.USDE
self.token = Tokens.USDE
elif integration_id is IntegrationID.KODIAK_SUSDE:
Copy link

Copilot AI Nov 10, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use == instead of is for enum comparison. While is may work due to Python's identity caching for some enum values, == is the correct and reliable way to compare enum members.

Suggested change
if integration_id is IntegrationID.KODIAK_USDE:
self.island = KodiakIslandAddress.USDE
self.token = Tokens.USDE
elif integration_id is IntegrationID.KODIAK_SUSDE:
if integration_id == IntegrationID.KODIAK_USDE:
self.island = KodiakIslandAddress.USDE
self.token = Tokens.USDE
elif integration_id == IntegrationID.KODIAK_SUSDE:

Copilot uses AI. Check for mistakes.
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link

Copilot AI Nov 10, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use == instead of is for enum comparison. While is may work due to Python's identity caching for some enum values, == is the correct and reliable way to compare enum members.

Suggested change
elif integration_id is IntegrationID.KODIAK_SUSDE:
elif integration_id == IntegrationID.KODIAK_SUSDE:

Copilot uses AI. Check for mistakes.
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

self.island = KodiakIslandAddress.SUSDE
self.token = Tokens.SUSDE
else:
raise ValueError(f"KodiakIntegration does not support integration ID {integration_id}")

super().__init__(
integration_id,
start_block,
chain
)

def get_block_balances(
self, cached_data: Dict[int, Dict[ChecksumAddress, float]], blocks: List[int]
) -> Dict[int, Dict[ChecksumAddress, float]]:
"""
Get the balances of all users at the given blocks
"""
base_url = f"{BASE_URL}/{self.token.value.lower()}"

result = {}
for block in blocks:
if block in cached_data:
result[block] = deepcopy(cached_data[block])
continue

params = {"excludeSources": "balance", "blockNumber": block}
response = requests.get(base_url, params=params)
Copy link

Copilot AI Nov 10, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing timeout parameter for HTTP request. Following the pattern in other integrations (e.g., zerolend_integration.py, echelon_integration.py), add a timeout parameter (e.g., timeout=10) to prevent the request from hanging indefinitely.

Suggested change
response = requests.get(base_url, params=params)
response = requests.get(base_url, params=params, timeout=10)

Copilot uses AI. Check for mistakes.
data = response.json()
Comment on lines +53 to +54
Copy link

Copilot AI Nov 10, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing error handling for API request failures. If the API returns an error status code or the request fails, response.json() could raise an exception, causing the entire batch processing to fail. Consider adding try-except blocks with appropriate error handling (similar to other integrations in the codebase), checking response status with response.raise_for_status(), and setting an empty dict for failed blocks to allow processing of other blocks to continue.

Suggested change
response = requests.get(base_url, params=params)
data = response.json()
try:
response = requests.get(base_url, params=params)
response.raise_for_status()
data = response.json()
except Exception as e:
# Optionally, log the error here
result[block] = {}
continue

Copilot uses AI. Check for mistakes.

block_data = {}
for entry in data:
user_address = ChecksumAddress(entry["id"])
value = 0.0
for balance_entry in entry["_sources"]:
if "extraData" in balance_entry:
extra_data = balance_entry["extraData"]
if extra_data.get("islandId") == self.island.value.lower():
value += float(balance_entry["value"])
elif balance_entry["source"] == "v3-position":
value += float(balance_entry["value"])

if value > 0:
block_data[user_address] = value
result[block] = block_data

return result
Loading