Skip to content

Commit

Permalink
Add support for rari fuse pools
Browse files Browse the repository at this point in the history
  • Loading branch information
tsudmi committed Jan 10, 2022
1 parent d13a87c commit 660f96c
Show file tree
Hide file tree
Showing 12 changed files with 160 additions and 18 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ docker-compose -f docker-compose.yml up -d
| AWS_SECRET_ACCESS_KEY | The AWS secret access key used to make the oracle vote public | Yes | - |
| STAKEWISE_SUBGRAPH_URL | The StakeWise subgraph URL | No | https://api.thegraph.com/subgraphs/name/stakewise/stakewise-mainnet |
| UNISWAP_V3_SUBGRAPH_URL | The Uniswap V3 subgraph URL | No | https://api.thegraph.com/subgraphs/name/stakewise/uniswap-v3-mainnet |
| RARI_FUSE_SUBGRAPH_URL | Rari Capital Fuse subgraph URL | No | https://api.thegraph.com/subgraphs/name/stakewise/rari-fuse-mainnet |
| ETHEREUM_SUBGRAPH_URL | The Ethereum subgraph URL | No | https://api.thegraph.com/subgraphs/name/stakewise/ethereum-mainnet |
| ORACLE_PROCESS_INTERVAL | How long to wait before processing again (in seconds) | No | 180 |
| ETH1_CONFIRMATION_BLOCKS | The required number of ETH1 confirmation blocks used to fetch the data | No | 15 |
Expand Down
1 change: 1 addition & 0 deletions deploy/.env.example
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ AWS_SECRET_ACCESS_KEY=""
# STAKEWISE_SUBGRAPH_URL=http://graph-node:8000/subgraphs/name/stakewise/stakewise
# UNISWAP_V3_SUBGRAPH_URL=http://graph-node:8000/subgraphs/name/stakewise/uniswap-v3
# ETHEREUM_SUBGRAPH_URL=http://graph-node:8000/subgraphs/name/stakewise/ethereum
# RARI_FUSE_SUBGRAPH_URL=http://graph-node:8000/subgraphs/name/stakewise/rari-fuse

# KEEPER
WEB3_ENDPOINT=""
Expand Down
4 changes: 2 additions & 2 deletions oracle/keeper/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -210,7 +210,7 @@ def wait_for_transaction(tx_hash: HexBytes) -> None:
transaction_hash=tx_hash, timeout=TRANSACTION_TIMEOUT, poll_latency=5
)
confirmation_block: BlockNumber = receipt["blockNumber"] + ETH1_CONFIRMATION_BLOCKS
current_block: BlockNumber = web3_client.eth.block_number
current_block: BlockNumber = web3_client.eth.to_block
while confirmation_block > current_block:
logger.info(
f"Waiting for {confirmation_block - current_block} confirmation blocks..."
Expand All @@ -219,7 +219,7 @@ def wait_for_transaction(tx_hash: HexBytes) -> None:

receipt = web3_client.eth.get_transaction_receipt(tx_hash)
confirmation_block = receipt["blockNumber"] + ETH1_CONFIRMATION_BLOCKS
current_block = web3_client.eth.block_number
current_block = web3_client.eth.to_block


@backoff.on_exception(backoff.expo, Exception, max_time=900)
Expand Down
11 changes: 11 additions & 0 deletions oracle/oracle/clients.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
ETHEREUM_SUBGRAPH_URL,
IPFS_FETCH_ENDPOINTS,
IPFS_PIN_ENDPOINTS,
RARI_FUSE_SUBGRAPH_URL,
STAKEWISE_SUBGRAPH_URL,
UNISWAP_V3_SUBGRAPH_URL,
)
Expand Down Expand Up @@ -58,6 +59,16 @@ async def execute_ethereum_gql_query(query: DocumentNode, variables: Dict) -> Di
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(
query: DocumentNode, variables: Dict
) -> Dict:
"""Executes GraphQL query."""
transport = AIOHTTPTransport(url=RARI_FUSE_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."""
Expand Down
13 changes: 10 additions & 3 deletions oracle/oracle/distributor/controller.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,11 @@ async def process(self, voting_params: DistributorVotingParameters) -> None:
current_nonce = voting_params["rewards_nonce"]

# skip submitting vote if too early or vote has been already submitted
if to_block <= last_updated_at_block or self.last_to_block == to_block:
if (
to_block <= last_updated_at_block
or self.last_to_block == to_block
or from_block >= to_block
):
return

logger.info(
Expand All @@ -71,6 +75,7 @@ async def process(self, voting_params: DistributorVotingParameters) -> None:
disabled_stakers_distributions = (
await get_disabled_stakers_reward_eth_distributions(
distributor_reward=voting_params["distributor_reward"],
from_block=from_block,
to_block=to_block,
)
)
Expand Down Expand Up @@ -102,7 +107,8 @@ async def process(self, voting_params: DistributorVotingParameters) -> None:
for dist in all_distributions:
distributor_rewards = DistributorRewards(
uniswap_v3_pools=uniswap_v3_pools,
block_number=dist["block_number"],
from_block=dist["from_block"],
to_block=dist["to_block"],
reward_token=dist["reward_token"],
uni_v3_token=dist["uni_v3_token"],
swise_holders=swise_holders,
Expand Down Expand Up @@ -131,7 +137,8 @@ async def process(self, voting_params: DistributorVotingParameters) -> None:
swise_holders_rewards = await DistributorRewards(
uniswap_v3_pools=uniswap_v3_pools,
swise_holders=swise_holders,
block_number=to_block,
from_block=from_block,
to_block=to_block,
uni_v3_token=SWISE_TOKEN_CONTRACT_ADDRESS,
reward_token=REWARD_ETH_TOKEN_CONTRACT_ADDRESS,
).get_rewards(SWISE_TOKEN_CONTRACT_ADDRESS, left_reward)
Expand Down
5 changes: 3 additions & 2 deletions oracle/oracle/distributor/eth1.py
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ async def get_periodic_allocations(

@backoff.on_exception(backoff.expo, Exception, max_time=900)
async def get_disabled_stakers_reward_eth_distributions(
distributor_reward: Wei, to_block: BlockNumber
distributor_reward: Wei, from_block: BlockNumber, to_block: BlockNumber
) -> Distributions:
"""Fetches disabled stakers reward ETH distributions based on their staked ETH balances."""
if distributor_reward <= 0:
Expand Down Expand Up @@ -149,7 +149,8 @@ async def get_disabled_stakers_reward_eth_distributions(

distribution = Distribution(
contract=staker_address,
block_number=to_block,
from_block=from_block,
to_block=to_block,
uni_v3_token=STAKED_ETH_TOKEN_CONTRACT_ADDRESS,
reward_token=REWARD_ETH_TOKEN_CONTRACT_ADDRESS,
reward=reward,
Expand Down
70 changes: 70 additions & 0 deletions oracle/oracle/distributor/rari.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
from typing import Dict

import backoff
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 ..graphql_queries import RARI_FUSE_POOLS_CTOKENS_QUERY
from .types import Balances


@backoff.on_exception(backoff.expo, Exception, max_time=900)
async def get_rari_fuse_liquidity_points(
ctoken_address: ChecksumAddress, from_block: BlockNumber, to_block: BlockNumber
) -> Balances:
"""Fetches Rari Fuse pools."""
lowered_ctoken_address = ctoken_address.lower()
last_id = ""
result: Dict = await execute_rari_fuse_pools_gql_query(
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(
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)
28 changes: 21 additions & 7 deletions oracle/oracle/distributor/rewards.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,13 @@

from oracle.oracle.settings import (
DISTRIBUTOR_FALLBACK_ADDRESS,
RARI_FUSE_POOL_ADDRESSES,
REWARD_ETH_TOKEN_CONTRACT_ADDRESS,
STAKED_ETH_TOKEN_CONTRACT_ADDRESS,
SWISE_TOKEN_CONTRACT_ADDRESS,
)

from .rari import get_rari_fuse_liquidity_points
from .types import Balances, Rewards, UniswapV3Pools
from .uniswap_v3 import (
get_uniswap_v3_liquidity_points,
Expand All @@ -26,7 +28,8 @@ class DistributorRewards(object):
def __init__(
self,
uniswap_v3_pools: UniswapV3Pools,
block_number: BlockNumber,
from_block: BlockNumber,
to_block: BlockNumber,
reward_token: ChecksumAddress,
uni_v3_token: ChecksumAddress,
swise_holders: Balances,
Expand All @@ -37,7 +40,8 @@ def __init__(
self.uni_v3_pools = self.uni_v3_swise_pools.union(
self.uni_v3_staked_eth_pools
).union(self.uni_v3_reward_eth_pools)
self.block_number = block_number
self.from_block = from_block
self.to_block = to_block
self.uni_v3_token = uni_v3_token
self.reward_token = reward_token
self.swise_holders = swise_holders
Expand All @@ -47,6 +51,7 @@ def is_supported_contract(self, contract_address: ChecksumAddress) -> bool:
return (
contract_address in self.uni_v3_pools
or contract_address == SWISE_TOKEN_CONTRACT_ADDRESS
or contract_address in RARI_FUSE_POOL_ADDRESSES
)

@staticmethod
Expand Down Expand Up @@ -110,7 +115,7 @@ async def get_balances(self, contract_address: ChecksumAddress) -> Balances:
return await get_uniswap_v3_single_token_balances(
pool_address=contract_address,
token=STAKED_ETH_TOKEN_CONTRACT_ADDRESS,
block_number=self.block_number,
block_number=self.to_block,
)
elif (
self.uni_v3_token == REWARD_ETH_TOKEN_CONTRACT_ADDRESS
Expand All @@ -120,7 +125,7 @@ async def get_balances(self, contract_address: ChecksumAddress) -> Balances:
return await get_uniswap_v3_single_token_balances(
pool_address=contract_address,
token=REWARD_ETH_TOKEN_CONTRACT_ADDRESS,
block_number=self.block_number,
block_number=self.to_block,
)
elif (
self.uni_v3_token == SWISE_TOKEN_CONTRACT_ADDRESS
Expand All @@ -130,7 +135,7 @@ async def get_balances(self, contract_address: ChecksumAddress) -> Balances:
return await get_uniswap_v3_single_token_balances(
pool_address=contract_address,
token=SWISE_TOKEN_CONTRACT_ADDRESS,
block_number=self.block_number,
block_number=self.to_block,
)
elif (
self.uni_v3_token == EMPTY_ADDR_HEX
Expand All @@ -143,15 +148,24 @@ async def get_balances(self, contract_address: ChecksumAddress) -> Balances:
tick_lower=-887220,
tick_upper=887220,
pool_address=contract_address,
block_number=self.block_number,
block_number=self.to_block,
)
elif contract_address in self.uni_v3_pools:
logger.info(
f"Fetching Uniswap V3 liquidity points: pool={contract_address}"
)
return await get_uniswap_v3_liquidity_points(
pool_address=contract_address,
block_number=self.block_number,
block_number=self.to_block,
)
elif contract_address in RARI_FUSE_POOL_ADDRESSES:
logger.info(
f"Fetching Rari Fuse Pool liquidity points: pool={contract_address}"
)
return await get_rari_fuse_liquidity_points(
ctoken_address=contract_address,
from_block=self.from_block,
to_block=self.to_block,
)
elif contract_address == SWISE_TOKEN_CONTRACT_ADDRESS:
logger.info("Distributing rewards to SWISE holders")
Expand Down
3 changes: 2 additions & 1 deletion oracle/oracle/distributor/types.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@ class DistributorVotingParameters(TypedDict):

class Distribution(TypedDict):
contract: ChecksumAddress
block_number: BlockNumber
from_block: BlockNumber
to_block: BlockNumber
uni_v3_token: ChecksumAddress
reward_token: ChecksumAddress
reward: int
Expand Down
8 changes: 5 additions & 3 deletions oracle/oracle/distributor/uniswap_v3.py
Original file line number Diff line number Diff line change
Expand Up @@ -125,24 +125,26 @@ async def get_uniswap_v3_distributions(
if reward > 0:
distribution = Distribution(
contract=pool_address,
block_number=BlockNumber(start + interval),
from_block=start,
to_block=BlockNumber(start + interval),
reward_token=allocation["reward_token"],
reward=reward,
uni_v3_token=EMPTY_ADDR_HEX,
)
distributions.append(distribution)
break

start += BLOCKS_INTERVAL
if interval_reward > 0:
distribution = Distribution(
contract=pool_address,
block_number=start,
from_block=start,
to_block=BlockNumber(start + BLOCKS_INTERVAL),
reward_token=allocation["reward_token"],
reward=interval_reward,
uni_v3_token=EMPTY_ADDR_HEX,
)
distributions.append(distribution)
start += BLOCKS_INTERVAL

return distributions

Expand Down
24 changes: 24 additions & 0 deletions oracle/oracle/graphql_queries.py
Original file line number Diff line number Diff line change
Expand Up @@ -297,6 +297,30 @@
"""
)

RARI_FUSE_POOLS_CTOKENS_QUERY = gql(
"""
query getCTokens(
$block_number: Int
$ctoken_address: Bytes
$last_id: ID
) {
accountCTokens(
block: { number: $block_number }
where: { ctoken: $ctoken_address, id_gt: $last_id }
first: 1000
orderBy: id
orderDirection: asc
) {
id
account
cTokenBalance
distributorPoints
updatedAtBlock
}
}
"""
)

DISTRIBUTOR_CLAIMED_ACCOUNTS_QUERY = gql(
"""
query getDistributorClaims($merkle_root: Bytes, $last_id: ID) {
Expand Down
10 changes: 10 additions & 0 deletions oracle/oracle/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,10 @@
DISTRIBUTOR_FALLBACK_ADDRESS = Web3.toChecksumAddress(
"0x144a98cb1CdBb23610501fE6108858D9B7D24934"
)
RARI_FUSE_POOL_ADDRESSES = [
Web3.toChecksumAddress("0x18F49849D20Bc04059FE9d775df9a38Cd1f5eC9F"),
Web3.toChecksumAddress("0x83d534Ab1d4002249B0E6d22410b62CF31978Ca2"),
]
WITHDRAWAL_CREDENTIALS: HexStr = HexStr(
"0x0100000000000000000000002296e122c1a20fca3cac3371357bdad3be0df079"
)
Expand All @@ -78,6 +82,10 @@
"ETHEREUM_SUBGRAPH_URL",
default="https://api.thegraph.com/subgraphs/name/stakewise/ethereum-mainnet",
)
RARI_FUSE_SUBGRAPH_URL = config(
"RARI_FUSE_SUBGRAPH_URL",
default="https://api.thegraph.com/subgraphs/name/stakewise/rari-fuse-mainnet",
)
elif NETWORK == GOERLI:
SYNC_PERIOD = timedelta(hours=1)
SWISE_TOKEN_CONTRACT_ADDRESS = Web3.toChecksumAddress(
Expand All @@ -92,6 +100,7 @@
DISTRIBUTOR_FALLBACK_ADDRESS = Web3.toChecksumAddress(
"0x1867c96601bc5fE24F685d112314B8F3Fe228D5A"
)
RARI_FUSE_POOL_ADDRESSES = []
WITHDRAWAL_CREDENTIALS: HexStr = HexStr(
"0x010000000000000000000000040f15c6b5bfc5f324ecab5864c38d4e1eef4218"
)
Expand All @@ -107,3 +116,4 @@
"ETHEREUM_SUBGRAPH_URL",
default="https://api.thegraph.com/subgraphs/name/stakewise/ethereum-goerli",
)
RARI_FUSE_SUBGRAPH_URL = ""

0 comments on commit 660f96c

Please sign in to comment.