Skip to content

Commit 545604d

Browse files
authored
Merge pull request #73 from BulbaSwap/main
feat: Add Bulbaswap LP positions adapter
2 parents a3fa115 + c209ac5 commit 545604d

File tree

2 files changed

+146
-0
lines changed

2 files changed

+146
-0
lines changed
Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
from typing import Dict, List, Optional, Set
2+
import requests
3+
from eth_typing import ChecksumAddress
4+
from web3 import Web3
5+
6+
from constants.chains import Chain
7+
from constants.summary_columns import SummaryColumn
8+
from integrations.cached_balances_integration import CachedBalancesIntegration
9+
from integrations.integration_ids import IntegrationID
10+
11+
class BulbaswapIntegration(CachedBalancesIntegration):
12+
"""Integration for tracking Bulbaswap LP positions."""
13+
14+
def __init__(
15+
self,
16+
integration_id: IntegrationID,
17+
start_block: int,
18+
chain: Chain = Chain.ETHEREUM,
19+
summary_cols: Optional[List[SummaryColumn]] = None,
20+
reward_multiplier: int = 1,
21+
balance_multiplier: int = 1,
22+
excluded_addresses: Optional[Set[ChecksumAddress]] = None,
23+
end_block: Optional[int] = None,
24+
ethereal_multiplier: int = 0,
25+
ethereal_multiplier_func: Optional[callable] = None,
26+
):
27+
super().__init__(
28+
integration_id,
29+
start_block,
30+
chain,
31+
summary_cols,
32+
reward_multiplier,
33+
balance_multiplier,
34+
excluded_addresses,
35+
end_block,
36+
ethereal_multiplier,
37+
ethereal_multiplier_func,
38+
)
39+
self.api_url = "https://api-dev.bulbaswap.io/v1/partner-tasks/ethena/positions"
40+
# Token addresses for USDE and sUSDE
41+
self.token_addresses = [
42+
"0x5d3a1ff2b6bab83b63cd9ad0787074081a52ef34", # USDe
43+
"0x211Cc4DD073734dA055fbF44a2b4667d5E5fE5d2", # sUSDe
44+
]
45+
self.page_size = 100 # Configurable page size
46+
47+
def get_block_balances(
48+
self, cached_data: Dict[int, Dict[ChecksumAddress, float]], blocks: List[int]
49+
) -> Dict[int, Dict[ChecksumAddress, float]]:
50+
"""Get user balances for specified blocks, using cached data when available.
51+
52+
Args:
53+
cached_data: Dictionary mapping block numbers to user balances at that block.
54+
blocks: List of block numbers to get balances for.
55+
56+
Returns:
57+
Dictionary mapping block numbers to user balances.
58+
"""
59+
result = {}
60+
61+
for block in blocks:
62+
# Skip if we already have data for this block
63+
if block in cached_data:
64+
result[block] = cached_data[block]
65+
continue
66+
67+
block_data = {}
68+
69+
try:
70+
# Fetch data for both token addresses
71+
for token_address in self.token_addresses:
72+
page = 0
73+
while True:
74+
# Get positions data with pagination
75+
response = requests.get(
76+
self.api_url,
77+
params={
78+
"tokenAddress": token_address,
79+
"blockNumber": block,
80+
"page": page,
81+
"limit": self.page_size
82+
}
83+
)
84+
data = response.json()
85+
86+
if data["code"] == 200 and data["data"]["status"] == 0:
87+
positions_data = data["data"]["data"]
88+
89+
# Process each user's positions
90+
for item in positions_data["items"]:
91+
user_address = Web3.to_checksum_address(item["userAddress"])
92+
93+
# Sum up liquidity for all pools
94+
total_liquidity = 0
95+
for pool_data in item["userPositions"].values():
96+
if float(pool_data["liquidityUSD"]) > 0:
97+
total_liquidity += float(pool_data["liquidityUSD"])
98+
99+
# Add or update user's total liquidity
100+
if total_liquidity > 0:
101+
if user_address in block_data:
102+
block_data[user_address] += total_liquidity
103+
else:
104+
block_data[user_address] = total_liquidity
105+
106+
# Check if we've processed all pages
107+
pagination = positions_data["pagination"]
108+
if page >= pagination["totalPages"] - 1:
109+
break
110+
page += 1
111+
else:
112+
print(f"Error in API response for block {block}, token {token_address}")
113+
break
114+
115+
except Exception as e:
116+
print(f"Error fetching data for block {block}: {str(e)}")
117+
block_data = {}
118+
119+
result[block] = block_data
120+
121+
return result
122+
123+
124+
if __name__ == "__main__":
125+
# Simple test
126+
integration = BulbaswapIntegration(
127+
integration_id=IntegrationID.BULBASWAP,
128+
start_block=15817416,
129+
chain=Chain.ETHEREUM,
130+
summary_cols=[SummaryColumn.TEMPLATE_PTS],
131+
reward_multiplier=1
132+
)
133+
134+
# Test with a specific block
135+
result = integration.get_block_balances(
136+
cached_data={},
137+
blocks=[15817416]
138+
)
139+
print("Block balances:", result)

integrations/integration_ids.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -469,6 +469,13 @@ class IntegrationID(Enum):
469469
# Venus
470470
VENUS_SUSDE = ("venus_susde", "Venus sUSDe", Token.SUSDE)
471471

472+
# Bulbaswap
473+
BULBASWAP = (
474+
"bulbaswap",
475+
"Bulbaswap",
476+
Token.USDE,
477+
)
478+
472479
def __init__(self, column_name: str, description: str, token: Token = Token.USDE):
473480
self.column_name = column_name
474481
self.description = description

0 commit comments

Comments
 (0)