diff --git a/deploy/gnosis/.env.example b/deploy/gnosis/.env.example index 83fa924..1340263 100644 --- a/deploy/gnosis/.env.example +++ b/deploy/gnosis/.env.example @@ -77,10 +77,3 @@ postgres_db=graph-node POSTGRES_DB=graph-node POSTGRES_USER=graph POSTGRES_PASSWORD=strong-password - -############ -# Scroring # -############ -SCORING_GRAPH_NODE_URL=https://api.thegraph.com/subgraphs/name/stakewise/stakewise-gnosis -SCORING_ETH2_NODE_URL=http://eth2-node:5052 -SCORING_DATABASE_PATH=/data/operator.db diff --git a/deploy/gnosis/docker-compose.yml b/deploy/gnosis/docker-compose.yml index 351cf9b..afba410 100644 --- a/deploy/gnosis/docker-compose.yml +++ b/deploy/gnosis/docker-compose.yml @@ -15,8 +15,6 @@ volumes: driver: local lighthouse: driver: local - scoring: - driver: local networks: gnosis: @@ -26,7 +24,7 @@ networks: services: oracle: container_name: oracle_gnosis - image: europe-west4-docker.pkg.dev/stakewiselabs/public/oracle:v2.2.9 + image: europe-west4-docker.pkg.dev/stakewiselabs/public/oracle:v2.3.0 restart: always entrypoint: ["python"] command: ["oracle/oracle/main.py"] @@ -36,7 +34,7 @@ services: keeper: container_name: keeper_gnosis - image: europe-west4-docker.pkg.dev/stakewiselabs/public/oracle:v2.2.9 + image: europe-west4-docker.pkg.dev/stakewiselabs/public/oracle:v2.3.0 restart: always entrypoint: ["python"] command: ["oracle/keeper/main.py"] @@ -44,16 +42,6 @@ services: profiles: ["keeper"] networks: - gnosis - - scoring: - container_name: scoring_gnosis - image: europe-west4-docker.pkg.dev/stakewiselabs/public/operator-effectiveness:v0.0.6 - restart: always - env_file: [".env"] - volumes: - - scoring:/data - networks: - - gnosis prometheus: container_name: prometheus_gnosis @@ -81,7 +69,7 @@ services: graph-node: container_name: graph_node_gnosis - image: graphprotocol/graph-node:v0.25.0 + image: graphprotocol/graph-node:v0.25.2 restart: always env_file: [".env"] depends_on: ["postgres","ipfs"] @@ -102,7 +90,7 @@ services: subgraphs: container_name: subgraphs_gnosis - image: europe-west4-docker.pkg.dev/stakewiselabs/public/subgraphs:v1.1.1 + image: europe-west4-docker.pkg.dev/stakewiselabs/public/subgraphs:v1.2.0 command: > /bin/sh -c "until nc -vz graph-node 8020; do echo 'Waiting graph-node'; sleep 2; done && yarn build:gnosis @@ -117,7 +105,7 @@ services: ipfs: container_name: ipfs_gnosis - image: ipfs/go-ipfs:v0.10.0 + image: ipfs/go-ipfs:v0.12.1 restart: always env_file: [".env"] ulimits: @@ -131,7 +119,7 @@ services: openethereum: container_name: openethereum_gnosis - image: openethereum/openethereum:v3.3.3 + image: openethereum/openethereum:v3.3.4 restart: always command: - --chain=xdai @@ -147,10 +135,9 @@ services: aliases: - eth1-node - nethermind: container_name: nethermind_gnosis - image: nethermind/nethermind:1.12.4 + image: nethermind/nethermind:1.12.6 restart: always command: - --config=xdai @@ -168,7 +155,7 @@ services: lighthouse: container_name: lighthouse_gnosis - image: sigp/lighthouse:v2.1.2 + image: sigp/lighthouse:v2.1.5 restart: always command: - lighthouse diff --git a/deploy/goerli/.env.example b/deploy/goerli/.env.example index 0ad8fae..7a6e7ad 100644 --- a/deploy/goerli/.env.example +++ b/deploy/goerli/.env.example @@ -78,10 +78,3 @@ postgres_db=graph-node POSTGRES_DB=graph-node POSTGRES_USER=graph POSTGRES_PASSWORD=strong-password - -############ -# Scroring # -############ -SCORING_GRAPH_NODE_URL=https://api.thegraph.com/subgraphs/name/stakewise/stakewise-goerli -SCORING_ETH2_NODE_URL=http://eth2-node:5052 -SCORING_DATABASE_PATH=/data/operator.db diff --git a/deploy/goerli/docker-compose.yml b/deploy/goerli/docker-compose.yml index c9e0c48..3de8c0b 100644 --- a/deploy/goerli/docker-compose.yml +++ b/deploy/goerli/docker-compose.yml @@ -17,8 +17,6 @@ volumes: driver: local lighthouse: driver: local - scoring: - driver: local networks: goerli: @@ -28,7 +26,7 @@ networks: services: oracle: container_name: oracle_goerli - image: europe-west4-docker.pkg.dev/stakewiselabs/public/oracle:v2.2.9 + image: europe-west4-docker.pkg.dev/stakewiselabs/public/oracle:v2.3.0 restart: always entrypoint: ["python"] command: ["oracle/oracle/main.py"] @@ -38,7 +36,7 @@ services: keeper: container_name: keeper_goerli - image: europe-west4-docker.pkg.dev/stakewiselabs/public/oracle:v2.2.9 + image: europe-west4-docker.pkg.dev/stakewiselabs/public/oracle:v2.3.0 restart: always entrypoint: ["python"] command: ["oracle/keeper/main.py"] @@ -46,16 +44,6 @@ services: profiles: ["keeper"] networks: - goerli - - scoring: - container_name: scoring_goerli - image: europe-west4-docker.pkg.dev/stakewiselabs/public/operator-effectiveness:v0.0.6 - restart: always - env_file: [".env"] - volumes: - - scoring:/data - networks: - - goerli prometheus: container_name: prometheus_goerli @@ -104,7 +92,7 @@ services: subgraphs: container_name: subgraphs_goerli - image: europe-west4-docker.pkg.dev/stakewiselabs/public/subgraphs:v1.1.1 + image: europe-west4-docker.pkg.dev/stakewiselabs/public/subgraphs:v1.2.0 command: > /bin/sh -c "until nc -vz graph-node 8020; do echo 'Waiting graph-node'; sleep 2; done && yarn build:goerli @@ -154,7 +142,7 @@ services: erigon: container_name: erigon_goerli - image: thorax/erigon:v2022.01.03 + image: thorax/erigon:v2022.03.02 restart: always command: - erigon @@ -172,7 +160,7 @@ services: erigon-rpcdaemon: container_name: erigon_rpcdaemon_goerli - image: thorax/erigon:v2022.01.03 + image: thorax/erigon:v2022.03.02 restart: always command: - rpcdaemon @@ -212,7 +200,7 @@ services: lighthouse: container_name: lighthouse_goerli - image: sigp/lighthouse:v2.1.2 + image: sigp/lighthouse:v2.1.5 restart: always command: - lighthouse diff --git a/deploy/mainnet/.env.example b/deploy/mainnet/.env.example index 9864fa4..37e927b 100644 --- a/deploy/mainnet/.env.example +++ b/deploy/mainnet/.env.example @@ -18,7 +18,6 @@ IPFS_PINATA_SECRET_KEY= MAINNET_STAKEWISE_SUBGRAPH_URL=https://api.thegraph.com/subgraphs/name/stakewise/stakewise-mainnet MAINNET_ETHEREUM_SUBGRAPH_URL=https://api.thegraph.com/subgraphs/name/stakewise/ethereum-mainnet MAINNET_UNISWAP_V3_SUBGRAPH_URL=https://api.thegraph.com/subgraphs/name/stakewise/uniswap-v3-mainnet -MAINNET_RARI_FUSE_SUBGRAPH_URL=https://api.thegraph.com/subgraphs/name/stakewise/rari-fuse-mainnet # Ethereum private key # NB! You must use a different private key for every network @@ -79,10 +78,3 @@ postgres_db=graph-node POSTGRES_DB=graph-node POSTGRES_USER=graph POSTGRES_PASSWORD=strong-password - -############ -# Scroring # -############ -SCORING_GRAPH_NODE_URL=https://api.thegraph.com/subgraphs/name/stakewise/stakewise-mainnet -SCORING_ETH2_NODE_URL=http://eth2-node:5052 -SCORING_DATABASE_PATH=/data/operator.db diff --git a/deploy/mainnet/docker-compose.yml b/deploy/mainnet/docker-compose.yml index 4b50a82..6eb6306 100644 --- a/deploy/mainnet/docker-compose.yml +++ b/deploy/mainnet/docker-compose.yml @@ -17,8 +17,6 @@ volumes: driver: local lighthouse: driver: local - scoring: - driver: local networks: mainnet: @@ -28,7 +26,7 @@ networks: services: oracle: container_name: oracle_mainnet - image: europe-west4-docker.pkg.dev/stakewiselabs/public/oracle:v2.2.9 + image: europe-west4-docker.pkg.dev/stakewiselabs/public/oracle:v2.3.0 restart: always entrypoint: ["python"] command: ["oracle/oracle/main.py"] @@ -38,7 +36,7 @@ services: keeper: container_name: keeper_mainnet - image: europe-west4-docker.pkg.dev/stakewiselabs/public/oracle:v2.2.9 + image: europe-west4-docker.pkg.dev/stakewiselabs/public/oracle:v2.3.0 restart: always entrypoint: ["python"] command: ["oracle/keeper/main.py"] @@ -46,16 +44,6 @@ services: profiles: ["keeper"] networks: - mainnet - - scoring: - container_name: scoring_mainnet - image: europe-west4-docker.pkg.dev/stakewiselabs/public/operator-effectiveness:v0.0.6 - restart: always - env_file: [".env"] - volumes: - - scoring:/data - networks: - - mainnet prometheus: container_name: prometheus_mainnet @@ -104,7 +92,7 @@ services: subgraphs: container_name: subgraphs_mainnet - image: europe-west4-docker.pkg.dev/stakewiselabs/public/subgraphs:v1.1.1 + image: europe-west4-docker.pkg.dev/stakewiselabs/public/subgraphs:v1.2.0 command: > /bin/sh -c "until nc -vz graph-node 8020; do echo 'Waiting graph-node'; sleep 2; done && yarn build:mainnet @@ -151,7 +139,7 @@ services: erigon: container_name: erigon_mainnet - image: thorax/erigon:v2022.01.03 + image: thorax/erigon:v2022.03.02 restart: always command: - erigon @@ -170,7 +158,7 @@ services: erigon-rpcdaemon: container_name: erigon_rpcdaemon_mainnet - image: thorax/erigon:v2022.01.03 + image: thorax/erigon:v2022.03.02 restart: always command: - rpcdaemon @@ -208,7 +196,7 @@ services: lighthouse: container_name: lighthouse_mainnet - image: sigp/lighthouse:v2.1.2 + image: sigp/lighthouse:v2.1.5 restart: always command: - lighthouse diff --git a/deploy/perm_goerli/docker-compose.yml b/deploy/perm_goerli/docker-compose.yml index 85c8f89..858d90d 100644 --- a/deploy/perm_goerli/docker-compose.yml +++ b/deploy/perm_goerli/docker-compose.yml @@ -17,8 +17,6 @@ volumes: driver: local lighthouse: driver: local - scoring: - driver: local networks: perm_goerli: @@ -28,19 +26,17 @@ networks: services: oracle: container_name: oracle_perm_goerli - image: europe-west4-docker.pkg.dev/stakewiselabs/public/oracle:v2.2.9 + image: europe-west4-docker.pkg.dev/stakewiselabs/public/oracle:v2.3.0 restart: always entrypoint: ["python"] command: ["oracle/oracle/main.py"] env_file: [".env"] - volumes: - - scoring:/data networks: - perm_goerli keeper: container_name: keeper_perm_goerli - image: europe-west4-docker.pkg.dev/stakewiselabs/public/oracle:v2.2.9 + image: europe-west4-docker.pkg.dev/stakewiselabs/public/oracle:v2.3.0 restart: always entrypoint: ["python"] command: ["oracle/keeper/main.py"] @@ -75,7 +71,7 @@ services: graph-node: container_name: graph_node_perm_goerli - image: graphprotocol/graph-node:v0.25.0 + image: graphprotocol/graph-node:v0.25.2 restart: always env_file: [".env"] depends_on: ["postgres","ipfs"] @@ -96,7 +92,7 @@ services: subgraphs: container_name: subgraphs_perm_goerli - image: europe-west4-docker.pkg.dev/stakewiselabs/public/subgraphs:v1.1.1 + image: europe-west4-docker.pkg.dev/stakewiselabs/public/subgraphs:v1.2.0 command: > /bin/sh -c "until nc -vz graph-node 8020; do echo 'Waiting graph-node'; sleep 2; done && yarn build:perm_goerli @@ -111,7 +107,7 @@ services: ipfs: container_name: ipfs_perm_goerli - image: ipfs/go-ipfs:v0.10.0 + image: ipfs/go-ipfs:v0.12.1 restart: always env_file: [".env"] ulimits: @@ -125,7 +121,7 @@ services: geth: container_name: geth_perm_goerli - image: ethereum/client-go:v1.10.15 + image: ethereum/client-go:v1.10.16 restart: always command: - --goerli @@ -146,7 +142,7 @@ services: erigon: container_name: erigon_perm_goerli - image: thorax/erigon:v2022.01.03 + image: thorax/erigon:v2022.03.02 restart: always command: - erigon @@ -164,7 +160,7 @@ services: erigon-rpcdaemon: container_name: erigon_rpcdaemon_perm_goerli - image: thorax/erigon:v2022.01.03 + image: thorax/erigon:v2022.03.02 restart: always command: - rpcdaemon @@ -204,7 +200,7 @@ services: lighthouse: container_name: lighthouse_perm_goerli - image: sigp/lighthouse:v2.1.2 + image: sigp/lighthouse:v2.1.5 restart: always command: - lighthouse diff --git a/oracle/networks.py b/oracle/networks.py index fee50d7..6cb7939 100644 --- a/oracle/networks.py +++ b/oracle/networks.py @@ -28,10 +28,6 @@ f"{MAINNET_UPPER}_UNISWAP_V3_SUBGRAPH_URL", default="https://api.thegraph.com/subgraphs/name/stakewise/uniswap-v3-mainnet", ), - RARI_FUSE_SUBGRAPH_URL=config( - f"{MAINNET_UPPER}_RARI_FUSE_SUBGRAPH_URL", - default="https://api.thegraph.com/subgraphs/name/stakewise/rari-fuse-mainnet", - ), ETH2_ENDPOINT=config(f"{MAINNET_UPPER}_ETH2_ENDPOINT", default=""), SLOTS_PER_EPOCH=32, SECONDS_PER_SLOT=12, @@ -53,10 +49,6 @@ DISTRIBUTOR_FALLBACK_ADDRESS=Web3.toChecksumAddress( "0x144a98cb1CdBb23610501fE6108858D9B7D24934" ), - RARI_FUSE_POOL_ADDRESSES=[ - Web3.toChecksumAddress("0x18F49849D20Bc04059FE9d775df9a38Cd1f5eC9F"), - Web3.toChecksumAddress("0x83d534Ab1d4002249B0E6d22410b62CF31978Ca2"), - ], WITHDRAWAL_CREDENTIALS=HexStr( "0x0100000000000000000000002296e122c1a20fca3cac3371357bdad3be0df079" ), @@ -99,10 +91,6 @@ f"{GOERLI_UPPER}_UNISWAP_V3_SUBGRAPH_URL", default="https://api.thegraph.com/subgraphs/name/stakewise/uniswap-v3-goerli", ), - # TODO: update once rari fuse pools is deployed to goerli chain - RARI_FUSE_SUBGRAPH_URL=config( - f"{GOERLI_UPPER}_RARI_FUSE_SUBGRAPH_URL", default="" - ), ETH2_ENDPOINT=config(f"{GOERLI_UPPER}_ETH2_ENDPOINT", default=""), SLOTS_PER_EPOCH=32, SECONDS_PER_SLOT=12, @@ -124,7 +112,6 @@ DISTRIBUTOR_FALLBACK_ADDRESS=Web3.toChecksumAddress( "0x1867c96601bc5fE24F685d112314B8F3Fe228D5A" ), - RARI_FUSE_POOL_ADDRESSES=[], WITHDRAWAL_CREDENTIALS=HexStr( "0x010000000000000000000000040f15c6b5bfc5f324ecab5864c38d4e1eef4218" ), @@ -165,10 +152,6 @@ f"{PERM_GOERLI_UPPER}_UNISWAP_V3_SUBGRAPH_URL", default="", ), - # TODO: update once rari fuse pools is deployed to goerli chain - RARI_FUSE_SUBGRAPH_URL=config( - f"{PERM_GOERLI_UPPER}_RARI_FUSE_SUBGRAPH_URL", default="" - ), ETH2_ENDPOINT=config(f"{PERM_GOERLI_UPPER}_ETH2_ENDPOINT", default=""), SLOTS_PER_EPOCH=32, SECONDS_PER_SLOT=12, @@ -190,7 +173,6 @@ DISTRIBUTOR_FALLBACK_ADDRESS=Web3.toChecksumAddress( "0x66D6c253084d8d51c7CFfDb3C188A0b53D998a3d" ), - RARI_FUSE_POOL_ADDRESSES=[], WITHDRAWAL_CREDENTIALS=HexStr( "0x0100000000000000000000006dfc9682e3c3263758ad96e2b2ba9822167f81ee" ), @@ -234,15 +216,11 @@ UNISWAP_V3_SUBGRAPH_URL=config( f"{GNOSIS_CHAIN_UPPER}_UNISWAP_V3_SUBGRAPH_URL", default="" ), - RARI_FUSE_SUBGRAPH_URL=config( - f"{GNOSIS_CHAIN_UPPER}_RARI_FUSE_SUBGRAPH_URL", - default="", - ), ETH2_ENDPOINT=config(f"{GNOSIS_CHAIN_UPPER}_ETH2_ENDPOINT", default=""), SLOTS_PER_EPOCH=16, SECONDS_PER_SLOT=5, ORACLES_CONTRACT_ADDRESS=Web3.toChecksumAddress( - "0xd0f5ddaed2D8BaE1F451D2A11FFAe1806f2Ee1a5" + "0xa6D123620Ea004cc5158b0ec260E934bd45C78c1" ), MULTICALL_CONTRACT_ADDRESS=Web3.toChecksumAddress( "0xb5b692a88BDFc81ca69dcB1d924f59f0413A602a" @@ -251,15 +229,14 @@ "0xfdA94F056346d2320d4B5E468D6Ad099b2277746" ), REWARD_TOKEN_CONTRACT_ADDRESS=Web3.toChecksumAddress( - "0x6FFa613eD41585B1c1e517A78d140cFBD68be639" + "0x6aC78efae880282396a335CA2F79863A1e6831D4" ), STAKED_TOKEN_CONTRACT_ADDRESS=Web3.toChecksumAddress( - "0x9AEBd2322D3D5fB69324a3cFE380DD11Bc3694D2" + "0xA4eF9Da5BA71Cc0D2e5E877a910A37eC43420445" ), DISTRIBUTOR_FALLBACK_ADDRESS=Web3.toChecksumAddress( "0x8737f638E9af54e89ed9E1234dbC68B115CD169e" ), - RARI_FUSE_POOL_ADDRESSES=[], WITHDRAWAL_CREDENTIALS=HexStr( "0x010000000000000000000000fc9b67b6034f6b306ea9bd8ec1baf3efa2490394" ), @@ -289,6 +266,6 @@ ), SYNC_PERIOD=timedelta(days=1), IS_POA=True, - DEPOSIT_TOKEN_SYMBOL="mGNO", + DEPOSIT_TOKEN_SYMBOL="GNO", ), } diff --git a/oracle/oracle/clients.py b/oracle/oracle/clients.py index ca1c757..396345d 100644 --- a/oracle/oracle/clients.py +++ b/oracle/oracle/clients.py @@ -53,17 +53,6 @@ async def execute_ethereum_gql_query( return await session.execute(query, variable_values=variables) -@backoff.on_exception(backoff.expo, Exception, max_time=300, logger=gql_logger) -async def execute_rari_fuse_pools_gql_query( - network: str, query: DocumentNode, variables: Dict -) -> Dict: - """Executes GraphQL query.""" - subgraph_url = NETWORKS[network]["RARI_FUSE_SUBGRAPH_URL"] - transport = AIOHTTPTransport(url=subgraph_url) - async with Client(transport=transport, execute_timeout=EXECUTE_TIMEOUT) as session: - return await session.execute(query, variable_values=variables) - - @backoff.on_exception(backoff.expo, Exception, max_time=900) async def ipfs_fetch(ipfs_hash: str) -> Union[Dict[Any, Any], List[Dict[Any, Any]]]: """Tries to fetch IPFS hash from different sources.""" diff --git a/oracle/oracle/distributor/controller.py b/oracle/oracle/distributor/controller.py index 965e7cb..241f535 100644 --- a/oracle/oracle/distributor/controller.py +++ b/oracle/oracle/distributor/controller.py @@ -9,6 +9,7 @@ from oracle.settings import DISTRIBUTOR_VOTE_FILENAME from ..eth1 import submit_vote +from .distributor_tokens import get_distributor_redirects, get_distributor_tokens from .eth1 import ( get_disabled_stakers_reward_token_distributions, get_distributor_claimed_accounts, @@ -109,12 +110,18 @@ async def process(self, voting_params: DistributorVotingParameters) -> None: # calculate reward distributions with coroutines tasks = [] + distributor_tokens = await get_distributor_tokens(self.network, from_block) + distributor_redirects = await get_distributor_redirects( + self.network, from_block + ) for dist in all_distributions: distributor_rewards = DistributorRewards( network=self.network, uniswap_v3_pools=uniswap_v3_pools, from_block=dist["from_block"], to_block=dist["to_block"], + distributor_tokens=distributor_tokens, + distributor_redirects=distributor_redirects, reward_token=dist["reward_token"], uni_v3_token=dist["uni_v3_token"], ) diff --git a/oracle/oracle/distributor/distributor_tokens.py b/oracle/oracle/distributor/distributor_tokens.py new file mode 100644 index 0000000..2bf1a40 --- /dev/null +++ b/oracle/oracle/distributor/distributor_tokens.py @@ -0,0 +1,149 @@ +from typing import Dict, Set + +from ens.constants import EMPTY_ADDR_HEX +from eth_typing import BlockNumber, ChecksumAddress +from web3 import Web3 + +from oracle.oracle.clients import execute_sw_gql_query +from oracle.oracle.graphql_queries import ( + DISTRIBUTOR_REDIRECTS_QUERY, + DISTRIBUTOR_TOKEN_HOLDERS_QUERY, + DISTRIBUTOR_TOKENS_QUERY, +) + +from .types import Balances + + +async def get_distributor_redirects( + network: str, block_number: BlockNumber +) -> Dict[ChecksumAddress, ChecksumAddress]: + """Fetches distributor redirects.""" + last_id = "" + result: Dict = await execute_sw_gql_query( + network=network, + query=DISTRIBUTOR_REDIRECTS_QUERY, + variables=dict( + block_number=block_number, + last_id=last_id, + ), + ) + distributor_redirects_chunk = result.get("distributorRedirects", []) + distributor_redirects = distributor_redirects_chunk + + # accumulate chunks + while len(distributor_redirects_chunk) >= 1000: + last_id = distributor_redirects_chunk[-1]["id"] + result: Dict = await execute_sw_gql_query( + network=network, + query=DISTRIBUTOR_REDIRECTS_QUERY, + variables=dict( + block_number=block_number, + last_id=last_id, + ), + ) + distributor_redirects_chunk = result.get("distributorRedirects", []) + distributor_redirects.extend(distributor_redirects_chunk) + + redirects: Dict[ChecksumAddress, ChecksumAddress] = {} + for redirect in distributor_redirects: + redirected_from = Web3.toChecksumAddress(redirect["id"]) + redirected_to = Web3.toChecksumAddress(redirect["token"]["id"]) + redirects[redirected_from] = redirected_to + + return redirects + + +async def get_distributor_tokens( + network: str, block_number: BlockNumber +) -> Set[ChecksumAddress]: + """Fetches distributor tokens.""" + last_id = "" + result: Dict = await execute_sw_gql_query( + network=network, + query=DISTRIBUTOR_TOKENS_QUERY, + variables=dict( + block_number=block_number, + last_id=last_id, + ), + ) + distributor_tokens_chunk = result.get("distributorTokens", []) + distributor_tokens = distributor_tokens_chunk + + # accumulate chunks + while len(distributor_tokens_chunk) >= 1000: + last_id = distributor_tokens_chunk[-1]["id"] + result: Dict = await execute_sw_gql_query( + network=network, + query=DISTRIBUTOR_TOKENS_QUERY, + variables=dict( + block_number=block_number, + last_id=last_id, + ), + ) + distributor_tokens_chunk = result.get("distributorTokens", []) + distributor_tokens.extend(distributor_tokens_chunk) + + return set(Web3.toChecksumAddress(t["id"]) for t in distributor_tokens) + + +async def get_token_liquidity_points( + network: str, + token_address: ChecksumAddress, + from_block: BlockNumber, + to_block: BlockNumber, +) -> Balances: + """Fetches distributor token holders' balances.""" + lowered_token_address = token_address.lower() + last_id = "" + result: Dict = await execute_sw_gql_query( + network=network, + query=DISTRIBUTOR_TOKEN_HOLDERS_QUERY, + variables=dict( + token_address=lowered_token_address, + block_number=to_block, + last_id=last_id, + ), + ) + positions_chunk = result.get("distributorTokenHolders", []) + positions = positions_chunk + + # accumulate chunks of positions + while len(positions_chunk) >= 1000: + last_id = positions_chunk[-1]["id"] + result: Dict = await execute_sw_gql_query( + network=network, + query=DISTRIBUTOR_TOKEN_HOLDERS_QUERY, + variables=dict( + token_address=lowered_token_address, + block_number=to_block, + last_id=last_id, + ), + ) + positions_chunk = result.get("distributorTokenHolders", []) + positions.extend(positions_chunk) + + # process balances + points: Dict[ChecksumAddress, int] = {} + total_points = 0 + for position in positions: + account = Web3.toChecksumAddress(position["account"]) + if account == EMPTY_ADDR_HEX: + continue + + principal = int(position["amount"]) + prev_account_points = int(position["distributorPoints"]) + updated_at_block = BlockNumber(int(position["updatedAtBlock"])) + if from_block > updated_at_block: + updated_at_block = from_block + prev_account_points = 0 + + account_points = prev_account_points + ( + principal * (to_block - updated_at_block) + ) + if account_points <= 0: + continue + + points[account] = points.get(account, 0) + account_points + total_points += account_points + + return Balances(total_supply=total_points, balances=points) diff --git a/oracle/oracle/distributor/rari.py b/oracle/oracle/distributor/rari.py deleted file mode 100644 index 78a9d15..0000000 --- a/oracle/oracle/distributor/rari.py +++ /dev/null @@ -1,73 +0,0 @@ -from typing import Dict - -from ens.constants import EMPTY_ADDR_HEX -from eth_typing import BlockNumber, ChecksumAddress -from web3 import Web3 - -from oracle.oracle.clients import execute_rari_fuse_pools_gql_query -from oracle.oracle.graphql_queries import RARI_FUSE_POOLS_CTOKENS_QUERY - -from .types import Balances - - -async def get_rari_fuse_liquidity_points( - network: str, - ctoken_address: ChecksumAddress, - from_block: BlockNumber, - to_block: BlockNumber, -) -> Balances: - """Fetches Rari Fuse pool accounts balances.""" - lowered_ctoken_address = ctoken_address.lower() - last_id = "" - result: Dict = await execute_rari_fuse_pools_gql_query( - network=network, - query=RARI_FUSE_POOLS_CTOKENS_QUERY, - variables=dict( - ctoken_address=lowered_ctoken_address, - block_number=to_block, - last_id=last_id, - ), - ) - positions_chunk = result.get("accountCTokens", []) - positions = positions_chunk - - # accumulate chunks of positions - while len(positions_chunk) >= 1000: - last_id = positions_chunk[-1]["id"] - result: Dict = await execute_rari_fuse_pools_gql_query( - network=network, - query=RARI_FUSE_POOLS_CTOKENS_QUERY, - variables=dict( - ctoken_address=lowered_ctoken_address, - block_number=to_block, - last_id=last_id, - ), - ) - positions_chunk = result.get("accountCTokens", []) - positions.extend(positions_chunk) - - # process fuse pools balances - points: Dict[ChecksumAddress, int] = {} - total_points = 0 - for position in positions: - account = Web3.toChecksumAddress(position["account"]) - if account == EMPTY_ADDR_HEX: - continue - - principal = int(position["cTokenBalance"]) - prev_account_points = int(position["distributorPoints"]) - updated_at_block = BlockNumber(int(position["updatedAtBlock"])) - if from_block > updated_at_block: - updated_at_block = from_block - prev_account_points = 0 - - account_points = prev_account_points + ( - principal * (to_block - updated_at_block) - ) - if account_points <= 0: - continue - - points[account] = points.get(account, 0) + account_points - total_points += account_points - - return Balances(total_supply=total_points, balances=points) diff --git a/oracle/oracle/distributor/rewards.py b/oracle/oracle/distributor/rewards.py index c4b8d3d..3888007 100644 --- a/oracle/oracle/distributor/rewards.py +++ b/oracle/oracle/distributor/rewards.py @@ -1,13 +1,13 @@ import copy import logging -from typing import List, Set +from typing import Dict, List, Set from ens.constants import EMPTY_ADDR_HEX from eth_typing import BlockNumber, ChecksumAddress from oracle.networks import NETWORKS -from .rari import get_rari_fuse_liquidity_points +from .distributor_tokens import get_token_liquidity_points from .types import Balances, Rewards, UniswapV3Pools from .uniswap_v3 import ( get_uniswap_v3_liquidity_points, @@ -25,11 +25,13 @@ def __init__( uniswap_v3_pools: UniswapV3Pools, from_block: BlockNumber, to_block: BlockNumber, + distributor_tokens: Set[ChecksumAddress], + distributor_redirects: Dict[ChecksumAddress, ChecksumAddress], reward_token: ChecksumAddress, uni_v3_token: ChecksumAddress, ) -> None: self.network = network - self.rari_fuse_pool_addresses = NETWORKS[network]["RARI_FUSE_POOL_ADDRESSES"] + self.distributor_tokens = distributor_tokens self.distributor_fallback_address = NETWORKS[network][ "DISTRIBUTOR_FALLBACK_ADDRESS" ] @@ -42,6 +44,7 @@ def __init__( self.swise_token_contract_address = NETWORKS[network][ "SWISE_TOKEN_CONTRACT_ADDRESS" ] + self.distributor_redirects = distributor_redirects self.uni_v3_staked_token_pools = uniswap_v3_pools["staked_token_pools"] self.uni_v3_reward_token_pools = uniswap_v3_pools["reward_token_pools"] self.uni_v3_swise_pools = uniswap_v3_pools["swise_pools"] @@ -57,8 +60,7 @@ def is_supported_contract(self, contract_address: ChecksumAddress) -> bool: """Checks whether the provided contract address is supported.""" return ( contract_address in self.uni_v3_pools - or contract_address == self.swise_token_contract_address - or contract_address in self.rari_fuse_pool_addresses + or contract_address in self.distributor_tokens ) @staticmethod @@ -94,14 +96,21 @@ async def get_rewards( if reward <= 0: return {} + # apply redirect of rewards + visited = set() + if contract_address in self.distributor_redirects: + visited.add(contract_address) + contract_address = self.distributor_redirects[contract_address] + if self.is_supported_contract(contract_address): + visited.add(contract_address) return await self._get_rewards( contract_address=contract_address, total_reward=reward, - visited={contract_address}, + visited=visited, ) - # unknown allocation -> assign to the rescue address + # unknown allocation -> assign to the fallback address rewards: Rewards = {} self.add_value( rewards=rewards, @@ -176,13 +185,13 @@ async def get_balances(self, contract_address: ChecksumAddress) -> Balances: pool_address=contract_address, block_number=self.to_block, ) - elif contract_address in self.rari_fuse_pool_addresses: + elif contract_address in self.distributor_tokens: logger.info( - f"[{self.network}] Fetching Rari Fuse Pool liquidity points: pool={contract_address}" + f"[{self.network}] Fetching token holders liquidity points: token={contract_address}" ) - return await get_rari_fuse_liquidity_points( + return await get_token_liquidity_points( network=self.network, - ctoken_address=contract_address, + token_address=contract_address, from_block=self.from_block, to_block=self.to_block, ) @@ -203,7 +212,7 @@ async def _get_rewards( result = await self.get_balances(contract_address) total_supply = result["total_supply"] if total_supply <= 0: - # no recipients for the rewards -> assign reward to the rescue address + # no recipients for the rewards -> assign reward to the fallback address self.add_value( rewards=rewards, to=self.distributor_fallback_address, @@ -228,8 +237,13 @@ async def _get_rewards( if account_reward <= 0: continue + # apply redirect of rewards + if account in self.distributor_redirects: + visited = visited.union({account}) + account = self.distributor_redirects[account] + if account == contract_address or account in visited: - # failed to assign reward -> return it to rescue address + # failed to assign reward -> return it to fallback address self.add_value( rewards=rewards, to=self.distributor_fallback_address, diff --git a/oracle/oracle/graphql_queries.py b/oracle/oracle/graphql_queries.py index ae99aab..df62f3b 100644 --- a/oracle/oracle/graphql_queries.py +++ b/oracle/oracle/graphql_queries.py @@ -297,23 +297,64 @@ """ ) -RARI_FUSE_POOLS_CTOKENS_QUERY = gql( +DISTRIBUTOR_REDIRECTS_QUERY = gql( """ - query getCTokens( + query getDistributorRedirects( $block_number: Int - $ctoken_address: Bytes $last_id: ID ) { - accountCTokens( + distributorRedirects( block: { number: $block_number } - where: { ctoken: $ctoken_address, id_gt: $last_id } + where: { id_gt: $last_id } + first: 1000 + orderBy: id + orderDirection: asc + ) { + id + token { + id + } + } + } +""" +) + +DISTRIBUTOR_TOKENS_QUERY = gql( + """ + query getDistributorTokens( + $block_number: Int + $last_id: ID + ) { + distributorTokens( + block: { number: $block_number } + where: { id_gt: $last_id } + first: 1000 + orderBy: id + orderDirection: asc + ) { + id + } + } +""" +) + +DISTRIBUTOR_TOKEN_HOLDERS_QUERY = gql( + """ + query getDistributorTokenHolders( + $block_number: Int + $token_address: Bytes + $last_id: ID + ) { + distributorTokenHolders( + block: { number: $block_number } + where: { token: $token_address, id_gt: $last_id } first: 1000 orderBy: id orderDirection: asc ) { id account - cTokenBalance + amount distributorPoints updatedAtBlock } diff --git a/oracle/oracle/rewards/controller.py b/oracle/oracle/rewards/controller.py index 8ea4843..c7c35f5 100644 --- a/oracle/oracle/rewards/controller.py +++ b/oracle/oracle/rewards/controller.py @@ -9,7 +9,7 @@ from web3 import Web3 from web3.types import Timestamp, Wei -from oracle.networks import NETWORKS +from oracle.networks import GNOSIS_CHAIN, NETWORKS from oracle.oracle.eth1 import submit_vote from oracle.settings import REWARD_VOTE_FILENAME @@ -25,6 +25,9 @@ logger = logging.getLogger(__name__) w3 = Web3() +WAD = Web3.toWei(1, "ether") +MGNO_RATE = Web3.toWei(32, "ether") + class RewardsController(object): """Updates total rewards and activated validators number.""" @@ -118,6 +121,10 @@ async def process( Web3.toWei(validator["balance"], "gwei") - self.deposit_amount ) + if self.network == GNOSIS_CHAIN: + # apply GNO <-> mGNO exchange rate + total_rewards = Wei(int(total_rewards * WAD // MGNO_RATE)) + pretty_total_rewards = self.format_ether(total_rewards) logger.info( f"[{self.network}] Retrieved pool validator rewards: total={pretty_total_rewards}" diff --git a/pyproject.toml b/pyproject.toml index 028adb8..efb2357 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "oracle" -version = "2.2.9" +version = "2.3.0" description = "StakeWise Oracles are responsible for submitting off-chain data." authors = ["Dmitri Tsumak "] license = "AGPL-3.0-only"