Skip to content

Conversation

@cyc60
Copy link
Contributor

@cyc60 cyc60 commented Jan 8, 2026

No description provided.

Signed-off-by: cyc60 <avsysoev60@gmail.com>
Copilot AI review requested due to automatic review settings January 8, 2026 21:41
@cyc60 cyc60 marked this pull request as draft January 8, 2026 21:41
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR adds a new command to update redeemable positions for users who have minted osToken shares. The command fetches allocator data from the graph, filters out leverage positions, calculates kept tokens (both in wallets and locked in protocols), determines redeemable amounts, and uploads the results to IPFS.

Key Changes:

  • Introduces the update_redeemable_positions command with supporting graph queries and API client
  • Adds ERC20 contract wrapper and OS_TOKEN_CONTRACT_ADDRESS configuration for each network
  • Expands IPFS configuration to support multiple upload clients (local, Infura, Pinata)

Reviewed changes

Copilot reviewed 11 out of 13 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
src/commands/internal/update_redeemable_positions.py Main command implementation to fetch, calculate, and upload redeemable positions
src/redeem/typings.py Data classes for Allocator and RedeemablePosition entities
src/redeem/graph.py Graph queries to fetch allocators and leverage position proxies
src/redeem/api_client.py API client to fetch locked osToken data from external protocol
src/common/contracts.py ERC20 contract wrapper for token balance queries
src/common/clients.py IPFS upload client builder supporting multiple providers
src/config/settings.py IPFS upload settings and increased graph timeout/page size defaults
src/config/networks.py OS_TOKEN_CONTRACT_ADDRESS added for each network
src/main.py Command registration in CLI
src/reward_splitter/graph.py Added pagination parameters to existing graph queries
src/common/abi/Erc20Token.json ERC20 ABI definition

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

cyc60 added 2 commits January 9, 2026 01:36
Signed-off-by: cyc60 <avsysoev60@gmail.com>
Signed-off-by: cyc60 <avsysoev60@gmail.com>
Copilot AI review requested due to automatic review settings January 12, 2026 11:00
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 13 out of 16 changed files in this pull request and generated 4 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +47 to +55
async def _fetch_json(self, url: str, params: dict | None = None) -> dict | list:
async with aiohttp.ClientSession() as session:
async with session.get(
url=url,
params=params,
headers={'user-agent': DEFAULT_USER_AGENT},
) as response:
response.raise_for_status()
return await response.json()
Copy link

Copilot AI Jan 12, 2026

Choose a reason for hiding this comment

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

Creating a new aiohttp.ClientSession for each request is inefficient. Create the session once in init or use a session pool to reuse connections across multiple API calls.

Copilot uses AI. Check for mistakes.
cyc60 added 2 commits January 12, 2026 22:30
Signed-off-by: cyc60 <avsysoev60@gmail.com>
Signed-off-by: cyc60 <avsysoev60@gmail.com>
Copilot AI review requested due to automatic review settings January 12, 2026 20:45
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 13 out of 16 changed files in this pull request and generated 1 comment.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

cyc60 added 4 commits January 13, 2026 14:08
Signed-off-by: cyc60 <avsysoev60@gmail.com>
Signed-off-by: cyc60 <avsysoev60@gmail.com>
Signed-off-by: cyc60 <avsysoev60@gmail.com>
Copilot AI review requested due to automatic review settings January 13, 2026 15:09
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 14 out of 18 changed files in this pull request and generated 3 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

cyc60 added 2 commits January 13, 2026 18:12
Signed-off-by: cyc60 <avsysoev60@gmail.com>
Signed-off-by: cyc60 <avsysoev60@gmail.com>
Copilot AI review requested due to automatic review settings January 13, 2026 16:52
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 14 out of 18 changed files in this pull request and generated 3 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

cyc60 added 2 commits January 13, 2026 19:55
Signed-off-by: cyc60 <avsysoev60@gmail.com>

# Conflicts:
#	poetry.lock
Signed-off-by: cyc60 <avsysoev60@gmail.com>
Copilot AI review requested due to automatic review settings January 13, 2026 16:58
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 14 out of 18 changed files in this pull request and generated 4 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

cyc60 and others added 2 commits January 13, 2026 21:23
Signed-off-by: cyc60 <avsysoev60@gmail.com>
Copilot AI review requested due to automatic review settings January 19, 2026 07:04
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 14 out of 18 changed files in this pull request and generated 2 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 18 out of 23 changed files in this pull request and generated 3 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

cyc60 added 2 commits January 20, 2026 12:58
Signed-off-by: cyc60 <avsysoev60@gmail.com>
Signed-off-by: cyc60 <avsysoev60@gmail.com>
Copilot AI review requested due to automatic review settings January 20, 2026 12:34
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 19 out of 24 changed files in this pull request and generated 1 comment.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Signed-off-by: cyc60 <avsysoev60@gmail.com>
@cyc60 cyc60 marked this pull request as ready for review January 21, 2026 07:38
Copilot AI review requested due to automatic review settings January 21, 2026 07:38
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 19 out of 24 changed files in this pull request and generated 6 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@tsudmi
Copy link
Member

tsudmi commented Jan 22, 2026

Code review

Found 2 issues:

  1. Missing OS_TOKEN_VAULT_CONTROLLER_CONTRACT_ADDRESS in NetworkConfig - The OsTokenVaultControllerContract class has settings_key = 'OS_TOKEN_VAULT_CONTROLLER_CONTRACT_ADDRESS', but this field is not defined in the NetworkConfig dataclass. When os_token_vault_controller_contract is instantiated without an explicit address and its methods are called via create_os_token_converter(), it will raise an AttributeError at runtime.

class OsTokenVaultControllerContract(ContractWrapper):
abi_path = 'abi/IOsTokenVaultController.json'
settings_key = 'OS_TOKEN_VAULT_CONTROLLER_CONTRACT_ADDRESS'

@dataclass
# pylint: disable-next=too-many-instance-attributes
class NetworkConfig(BaseNetworkConfig):
WALLET_BALANCE_SYMBOL: str
VAULT_BALANCE_SYMBOL: str
OS_TOKEN_BALANCE_SYMBOL: str
DEPOSIT_DATA_REGISTRY_CONTRACT_ADDRESS: ChecksumAddress
VALIDATORS_CHECKER_CONTRACT_ADDRESS: ChecksumAddress
CONSOLIDATION_CONTRACT_ADDRESS: ChecksumAddress
WITHDRAWAL_CONTRACT_ADDRESS: ChecksumAddress
OS_TOKEN_CONTRACT_ADDRESS: ChecksumAddress
OS_TOKEN_ARBITRUM_CONTRACT_ADDRESS: ChecksumAddress
WALLET_MIN_BALANCE: Wei
STAKEWISE_API_URL: str
STAKEWISE_GRAPH_ENDPOINT: str
RATED_API_URL: str
CONFIG_UPDATE_EVENT_BLOCK: BlockNumber
MAX_FEE_PER_GAS_GWEI: Gwei
MAX_VALIDATOR_BALANCE_GWEI: Gwei
SHARD_COMMITTEE_PERIOD: int
PENDING_PARTIAL_WITHDRAWALS_LIMIT: int
PENDING_CONSOLIDATIONS_LIMIT: int
MAX_WITHDRAWAL_REQUESTS_PER_BLOCK: int
EXCESS_EXECUTION_REQUESTS_STORAGE_SLOT: int
EXECUTION_REQUEST_COUNT_STORAGE_SLOT: int
MIN_EXECUTION_REQUEST_FEE: int
EXECUTION_REQUEST_FEE_UPDATE_FRACTION: int
EXECUTION_REQUEST_QUEUE_HEAD_STORAGE_SLOT: int
EXECUTION_REQUEST_QUEUE_TAIL_STORAGE_SLOT: int
EXECUTION_REQUEST_QUEUE_STORAGE_OFFSET: int
TARGET_WITHDRAWAL_REQUESTS_PER_BLOCK: int
TARGET_CONSOLIDATION_REQUESTS_PER_BLOCK: int
NODE_CONFIG: NodeConfig

  1. Missing close_clients() call - The update_redeemable_positions command calls setup_clients() but never calls close_clients() to clean up resources. Other commands (consolidate.py, exit_validators.py, recover.py, setup_remote_signer.py, start/base.py) follow the pattern of calling await close_clients() in a try/finally block to ensure proper resource cleanup.

async def main(arbitrum_endpoint: str | None, min_os_token_position_amount_gwei: Gwei) -> None:
"""
Fetch redeemable positions, calculate kept os token amounts and upload to IPFS.
"""
setup_logging()
await setup_clients()
block_number = await execution_client.eth.block_number
logger.info('Fetching allocators from the subgraph...')
allocators = await graph_get_allocators(block_number)
logger.info('Fetched %s allocators from the subgraph', len(allocators))
# filter boost proxy positions
leverage_positions = await graph_get_leverage_positions(block_number)
boost_proxies = {pos.proxy for pos in leverage_positions}
logger.info('Found %s proxy positions to exclude', len(boost_proxies))
allocators = [a for a in allocators if a.address not in boost_proxies]
# reduce boosted positions
logger.info('Fetching boosted positions from the subgraph...')
os_token_converter = await create_os_token_converter(block_number)
boost_ostoken_shares = await calculate_boost_ostoken_shares(
users={a.address for a in allocators},
leverage_positions=leverage_positions,
os_token_converter=os_token_converter,
)
allocators = _reduce_boosted_amount(allocators, boost_ostoken_shares)
# filter zero positions
min_minted_shares = Web3.to_wei(min_os_token_position_amount_gwei, 'gwei')
for allocator in allocators:
allocator.vault_shares = [
vault_share
for vault_share in allocator.vault_shares
if vault_share.minted_shares >= min_minted_shares
]
if not allocators:
logger.info('No allocators with minted shares above the threshold found, exiting...')
return
logger.info('Fetching kept tokens for %s addresses', len(allocators))
address_to_minted_shares = {a.address: a.total_shares for a in allocators}
kept_shares = await get_kept_shares(address_to_minted_shares, block_number, arbitrum_endpoint)
logger.info('Fetched kept tokens for %s addresses...', len(address_to_minted_shares))
redeemable_positions = create_redeemable_positions(allocators, kept_shares)
if not redeemable_positions:
logger.info('No redeemable positions to upload, exiting...')
return
total_redeemable = sum(p.amount for p in redeemable_positions)
logger.info(
'Created %s redeemable positions. Total redeemed %s amount: %s (%s %s)',
len(redeemable_positions),
settings.network_config.OS_TOKEN_BALANCE_SYMBOL,
total_redeemable,
round(Web3.from_wei(total_redeemable, 'ether'), 5),
settings.network_config.WALLET_BALANCE_SYMBOL,
)
click.confirm(
'Proceed with uploading redeemable positions to IPFS?',
default=True,
abort=True,
)
ipfs_upload_client = build_ipfs_upload_clients()
ipfs_hash = await ipfs_upload_client.upload_json([p.as_dict() for p in redeemable_positions])
click.echo(f'Redeemable position uploaded to IPFS: hash={ipfs_hash}')
# calculate merkle root
leaves = [r.merkle_leaf for r in redeemable_positions]
tree = StandardMerkleTree.of(leaves, ['address', 'address', 'uint160'])
logger.info('Generated Merkle Tree root: %s', tree.root)

🤖 Generated with Claude Code

- If this code review was useful, please react with 👍. Otherwise, react with 👎.

cyc60 added 2 commits January 22, 2026 17:32
Signed-off-by: cyc60 <avsysoev60@gmail.com>
Signed-off-by: cyc60 <avsysoev60@gmail.com>
Copilot AI review requested due to automatic review settings January 22, 2026 14:45
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 19 out of 24 changed files in this pull request and generated no new comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

cyc60 added 2 commits January 22, 2026 23:26
Signed-off-by: cyc60 <avsysoev60@gmail.com>
Signed-off-by: cyc60 <avsysoev60@gmail.com>
Copilot AI review requested due to automatic review settings January 26, 2026 10:04
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 19 out of 24 changed files in this pull request and generated 4 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

cyc60 added 2 commits January 26, 2026 13:26
Signed-off-by: cyc60 <avsysoev60@gmail.com>
Signed-off-by: cyc60 <avsysoev60@gmail.com>
Copilot AI review requested due to automatic review settings January 27, 2026 19:16
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 19 out of 24 changed files in this pull request and generated 1 comment.

Comments suppressed due to low confidence (1)

src/commands/internal/update_redeemable_positions.py:1

  • Corrected spelling of 'cliente' to 'client'.
import asyncio

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

cyc60 added 2 commits January 28, 2026 18:52
Signed-off-by: cyc60 <avsysoev60@gmail.com>
Signed-off-by: cyc60 <avsysoev60@gmail.com>
Copilot AI review requested due to automatic review settings January 28, 2026 16:10
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 19 out of 24 changed files in this pull request and generated 4 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Signed-off-by: cyc60 <avsysoev60@gmail.com>
'Fetching %s from Arbitrum wallet balances...',
settings.network_config.OS_TOKEN_BALANCE_SYMBOL,
)
arbitrum_endpoint = cast(str, arbitrum_endpoint)
Copy link
Contributor

Choose a reason for hiding this comment

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

(optional) You have 2 separate checks for arb contract address and arb endpoint. Btw they should be consistent, both empty or both non-empty.
Add NetworkConfig.ArbitrumConfig dataclass. Then you could check ArbitrumConfig is not None

@dataclass
class ArbitrumConfig:
    OS_TOKEN_CONTRACT_ADDRESS
    ENDPOINT: str

Comment on lines +360 to +364
if position.vault not in boosted_positions[position.user]:
boosted_positions[position.user][position.vault] = Wei(0)
boosted_positions[position.user][position.vault] = Wei(
boosted_positions[position.user][position.vault] + position_os_token_shares
)
Copy link
Contributor

Choose a reason for hiding this comment

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

Use tuples as keys. Start from defaultdict.

Suggested change
if position.vault not in boosted_positions[position.user]:
boosted_positions[position.user][position.vault] = Wei(0)
boosted_positions[position.user][position.vault] = Wei(
boosted_positions[position.user][position.vault] + position_os_token_shares
)
boosted_positions: defaultdict[tuple[ChecksumAddress, ChecksumAddress], Wei] = defaultdict(lambda: Wei(0))
for ...
boosted_positions[position.user, position.vault] = Wei(
boosted_positions[position.user, position.vault] + position_os_token_shares
)

vault_1 = faker.eth_address()
vault_2 = faker.eth_address()

# test zero allocators
Copy link
Contributor

Choose a reason for hiding this comment

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

Please split to test-cases. One function is one test-case. For this test and all tests below.

Copy link
Contributor

@evgeny-stakewise evgeny-stakewise left a comment

Choose a reason for hiding this comment

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

Added comments

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants