-
Notifications
You must be signed in to change notification settings - Fork 21
Process redeemer #664
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: v5-release
Are you sure you want to change the base?
Process redeemer #664
Changes from 69 commits
7e82e70
488c6bc
fac0134
cc6010f
743f179
f314a01
096b2ea
bf5d407
baf4d2f
6c85b19
503c959
eb4f195
6600e0e
14465fc
8437f4b
248a9be
5d4eea4
dc38e91
73899a3
89bd7fa
cb7308d
625c0ba
c455880
1e861b2
06e8f81
32546bb
921dcd2
ff10073
1e71e0f
93b2f65
6d6b0fc
dc7f915
0ec716f
52f2ce1
5b266cd
0f689b0
526af08
06d7c71
27cb7c5
69e0b73
2dff8eb
5d9a0ff
bfb8866
8c6f6d2
fd0ca47
1838d66
079cc80
211e09e
3d7a509
8087439
0e8e1db
9490f74
8b2014c
2eb4378
44dac79
5c116cb
97b3b06
8bef13b
03102ed
774c62b
f28c964
f4874ac
a5efdbe
bb35ee1
c434029
37dae63
acf74df
22e846c
1899d73
1f32663
8c97ae3
88dcdfb
8c2a97c
1d8c5d9
a4d4608
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Large diffs are not rendered by default.
Large diffs are not rendered by default.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,38 +1,67 @@ | ||
| from typing import cast | ||
|
|
||
| from hexbytes import HexBytes | ||
| from sw_utils.networks import GNO_NETWORKS | ||
| from web3 import Web3 | ||
| from web3.types import BlockNumber, Wei | ||
| from web3.types import BlockNumber, ChecksumAddress, Wei | ||
|
|
||
| from src.common.clients import ipfs_fetch_client | ||
| from src.common.contracts import VaultContract, keeper_contract | ||
| from src.common.typings import HarvestParams | ||
| from src.config.settings import settings | ||
|
|
||
|
|
||
| async def get_harvest_params(block_number: BlockNumber | None = None) -> HarvestParams | None: | ||
| if not await keeper_contract.can_harvest(settings.vault, block_number): | ||
| return None | ||
| async def get_harvest_params( | ||
| vault: ChecksumAddress, block_number: BlockNumber | None = None | ||
| ) -> HarvestParams | None: | ||
| """Get harvest params for a single vault.""" | ||
| result = await get_multiple_harvest_params([vault], block_number) | ||
| return result[vault] | ||
|
|
||
|
|
||
| async def get_multiple_harvest_params( | ||
| vaults: list[ChecksumAddress], block_number: BlockNumber | None = None | ||
| ) -> dict[ChecksumAddress, HarvestParams | None]: | ||
| """Get harvest params for multiple vaults. | ||
|
|
||
| Checks can_harvest for all vaults first, then fetches IPFS data only | ||
| if at least one vault is harvestable. | ||
| """ | ||
| if not vaults: | ||
| return {} | ||
|
|
||
| last_rewards = await keeper_contract.get_last_rewards_update(block_number) | ||
| if last_rewards is None: | ||
| return None | ||
| return {vault: None for vault in vaults} | ||
|
Comment on lines
+25
to
+35
|
||
|
|
||
| harvestable_vaults: list[ChecksumAddress] = [ | ||
| vault for vault in vaults if await keeper_contract.can_harvest(vault, block_number) | ||
| ] | ||
cyc60 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| if not harvestable_vaults: | ||
| return {vault: None for vault in vaults} | ||
|
|
||
|
Comment on lines
33
to
+43
|
||
| ipfs_data = cast(dict, await ipfs_fetch_client.fetch_json(last_rewards.ipfs_hash)) | ||
|
|
||
| results: dict[ChecksumAddress, HarvestParams | None] = {vault: None for vault in vaults} | ||
| for vault in harvestable_vaults: | ||
| vault_contract = VaultContract(vault) | ||
| results[vault] = await _extract_harvest_params( | ||
| vault_contract=vault_contract, | ||
| ipfs_data=ipfs_data, | ||
| rewards_root=last_rewards.rewards_root, | ||
| ) | ||
|
|
||
| vault_contract = VaultContract(settings.vault) | ||
| harvest_params = await _fetch_harvest_params_from_ipfs( | ||
| vault_contract=vault_contract, | ||
| ipfs_hash=last_rewards.ipfs_hash, | ||
| rewards_root=last_rewards.rewards_root, | ||
| ) | ||
| return harvest_params | ||
| return results | ||
|
|
||
|
|
||
| async def _fetch_harvest_params_from_ipfs( | ||
| vault_contract: VaultContract, ipfs_hash: str, rewards_root: bytes | ||
| async def _extract_harvest_params( | ||
| vault_contract: VaultContract, ipfs_data: dict, rewards_root: bytes | ||
| ) -> HarvestParams | None: | ||
| ipfs_data = await ipfs_fetch_client.fetch_json(ipfs_hash) | ||
| """Extract harvest params for a single vault from pre-fetched IPFS data.""" | ||
| mev_escrow = await vault_contract.mev_escrow() | ||
|
|
||
| for vault_data in ipfs_data['vaults']: # type: ignore | ||
| for vault_data in ipfs_data['vaults']: | ||
| if vault_contract.contract_address != Web3.to_checksum_address(vault_data['vault']): | ||
| continue | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,38 @@ | ||
| from unittest.mock import AsyncMock, MagicMock, patch | ||
|
|
||
| from eth_typing import BlockNumber | ||
| from web3 import Web3 | ||
|
|
||
| from src.common.harvest import get_multiple_harvest_params | ||
|
|
||
| HARVEST_MODULE = 'src.common.harvest' | ||
|
|
||
| VAULT_1 = Web3.to_checksum_address('0x' + '11' * 20) | ||
| VAULT_2 = Web3.to_checksum_address('0x' + '22' * 20) | ||
| OWNER_1 = Web3.to_checksum_address('0x' + '33' * 20) | ||
| OWNER_2 = Web3.to_checksum_address('0x' + '44' * 20) | ||
|
|
||
|
|
||
| class TestGetMultipleHarvestParams: | ||
| async def test_empty_vaults(self) -> None: | ||
| result = await get_multiple_harvest_params([], BlockNumber(100)) | ||
| assert result == {} | ||
|
|
||
| async def test_no_last_rewards(self) -> None: | ||
| with patch(f'{HARVEST_MODULE}.keeper_contract') as mock_keeper: | ||
| mock_keeper.get_last_rewards_update = AsyncMock(return_value=None) | ||
| result = await get_multiple_harvest_params([VAULT_1], BlockNumber(100)) | ||
| assert result == {VAULT_1: None} | ||
|
|
||
| async def test_cannot_harvest(self) -> None: | ||
| mock_last_rewards = MagicMock() | ||
| mock_last_rewards.ipfs_hash = 'QmTest' | ||
| with ( | ||
| patch(f'{HARVEST_MODULE}.keeper_contract') as mock_keeper, | ||
| patch(f'{HARVEST_MODULE}.ipfs_fetch_client') as mock_ipfs, | ||
| ): | ||
| mock_keeper.get_last_rewards_update = AsyncMock(return_value=mock_last_rewards) | ||
| mock_keeper.can_harvest = AsyncMock(return_value=False) | ||
| mock_ipfs.fetch_json = AsyncMock(return_value={}) | ||
| result = await get_multiple_harvest_params([VAULT_1, VAULT_2], BlockNumber(100)) | ||
cyc60 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| assert result == {VAULT_1: None, VAULT_2: None} | ||
cyc60 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
Uh oh!
There was an error while loading. Please reload this page.