Skip to content

Commit

Permalink
Refactor update_price.py (#3)
Browse files Browse the repository at this point in the history
* Refactor update_price.py

* Add price network config, add sender execution endpoint

* Del price/networks.py, fix comment

* Add comment in .env.example

* Fix lint
  • Loading branch information
evgeny-stakewise authored Oct 31, 2024
1 parent c97b069 commit 3f4c3ea
Show file tree
Hide file tree
Showing 20 changed files with 885 additions and 571 deletions.
15 changes: 8 additions & 7 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,18 @@
# Fill values

# Common settings
EXECUTION_ENDPOINT=https://execution
NETWORK=holesky
HOT_WALLET_PRIVATE_KEY=0x2......

# Settings for update_price.py
# TARGET_RPC_URL=https://example.com
# TARGET_CHAIN=23
# TARGET_ADDRESS=0xbd335c16c94be8c4dd073ae376ddf78bec1858df
# PRICE_FEED=0xba74737a078c05500dd98c970909e4a3b90c35c6
# PRICE_FEED_SENDER=0xf7d4e7273e5015c96728a6b02f31c505ee184603
# NETWORK is used as sender network
# Target network is determined automatically based on sender network
# Supported sender networks: Sepolia (id 11155111), Mainnet (id 1)
# Target networks: Arbitrum Sepolia (id 421614), Arbitrum (id 42161)
# SENDER_EXECUTION_ENDPOINT=https://example.com
# TARGET_EXECUTION_ENDPOINT=https://example.com

# Settings for update_ltv.py
# NETWORK=holesky
# EXECUTION_ENDPOINT=https://execution
# GRAPH_API_URL=https://graph
# VAULT=0x...
705 changes: 345 additions & 360 deletions poetry.lock

Large diffs are not rendered by default.

4 changes: 3 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ package-mode = false

[tool.poetry.dependencies]
python = "^3.10"
python-dotenv = "==1.0.1"
gql = {extras = ["requests"], version = "==3.5.0"}
sw-utils = {git = "https://github.com/stakewise/sw-utils.git", rev = "v0.6.25"}
python-decouple = "==3.8"
Expand Down Expand Up @@ -80,3 +79,6 @@ exclude = '''
| dist
)/
'''

[tool.vulture]
exclude = ["networks.py"]
Empty file added src/common/__init__.py
Empty file.
22 changes: 22 additions & 0 deletions src/common/clients.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import logging

from eth_account import Account
from eth_account.signers.local import LocalAccount
from web3 import Web3
from web3.middleware import construct_sign_and_send_raw_middleware

from src.common.settings import HOT_WALLET_PRIVATE_KEY

logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)


def get_execution_client(endpoint: str, account: LocalAccount | None = None) -> Web3:
client = Web3(Web3.HTTPProvider(endpoint))
if account:
client.middleware_onion.add(construct_sign_and_send_raw_middleware(account))
return client


hot_wallet_account = Account().from_key(HOT_WALLET_PRIVATE_KEY)
logger.info('Wallet address: %s', hot_wallet_account.address)
26 changes: 26 additions & 0 deletions src/common/contracts.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import json
import logging

from eth_typing import ChecksumAddress
from web3 import Web3
from web3.contract.contract import ContractEvents, ContractFunctions

logger = logging.getLogger(__name__)


class ContractWrapper:
def __init__(self, abi_path: str, address: ChecksumAddress, client: Web3):
self.address = address
self.contract = client.eth.contract(address=address, abi=self._load_abi(abi_path))

def _load_abi(self, abi_path: str) -> dict:
with open(abi_path, encoding='utf-8') as f:
return json.load(f)

@property
def functions(self) -> ContractFunctions:
return self.contract.functions

@property
def events(self) -> ContractEvents:
return self.contract.events
100 changes: 100 additions & 0 deletions src/common/networks.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
from dataclasses import dataclass
from enum import Enum

from eth_typing import ChecksumAddress, HexAddress, HexStr
from web3 import Web3

EMPTY_ADDR_HEX = HexAddress(HexStr('0x' + '00' * 20))
ZERO_CHECKSUM_ADDRESS = Web3.to_checksum_address(EMPTY_ADDR_HEX) # noqa


class Network(Enum):
MAINNET = 'mainnet'
HOLESKY = 'holesky'
GNOSIS = 'gnosis'
CHIADO = 'chiado'
SEPOLIA = 'sepolia'


@dataclass
class PriceNetworkConfig:
# TARGET_CHAIN is not what eth_chainId returns.
# It is internal id used in PriceFeedSender contract.
TARGET_CHAIN: int
# PriceFeedReceiver contract address on target network
TARGET_ADDRESS: ChecksumAddress
# PriceFeed contract address on target network
TARGET_PRICE_FEED_CONTRACT_ADDRESS: ChecksumAddress
# PriceFeedSender contract address on sender network
PRICE_FEED_SENDER_CONTRACT_ADDRESS: ChecksumAddress


@dataclass
class NetworkConfig:
VAULT_USER_LTV_TRACKER_CONTRACT_ADDRESS: ChecksumAddress
PRICE_NETWORK_CONFIG: PriceNetworkConfig | None = None


NETWORKS: dict[Network, NetworkConfig] = {
Network.MAINNET: NetworkConfig(
VAULT_USER_LTV_TRACKER_CONTRACT_ADDRESS=Web3.to_checksum_address(
'0xe0Ae8B04922d6e3fA06c2496A94EF2875EFcC7BB'
),
PRICE_NETWORK_CONFIG=(
PriceNetworkConfig(
# TARGET_CHAIN is not what eth_chainId returns.
# It is internal id used in PriceFeedSender contract.
TARGET_CHAIN=23,
# PriceFeedReceiver contract address on Arbitrum
TARGET_ADDRESS=Web3.to_checksum_address(
'0xbd335c16c94be8c4dd073ae376ddf78bec1858df'
),
# PriceFeed contract address on Arbitrum
TARGET_PRICE_FEED_CONTRACT_ADDRESS=Web3.to_checksum_address(
'0xba74737a078c05500dd98c970909e4a3b90c35c6'
),
# PriceFeedSender contract address on Mainnet
PRICE_FEED_SENDER_CONTRACT_ADDRESS=Web3.to_checksum_address(
'0xf7d4e7273e5015c96728a6b02f31c505ee184603'
),
)
),
),
Network.HOLESKY: NetworkConfig(
VAULT_USER_LTV_TRACKER_CONTRACT_ADDRESS=Web3.to_checksum_address(
'0x8f48130b9b96B58035b4A9389eCDaBC00d59d0c8'
),
),
Network.GNOSIS: NetworkConfig(
VAULT_USER_LTV_TRACKER_CONTRACT_ADDRESS=Web3.to_checksum_address(
'0xdEa72c54f63470349CE2dC12f8232FE00241abE6'
),
),
Network.CHIADO: NetworkConfig(
VAULT_USER_LTV_TRACKER_CONTRACT_ADDRESS=Web3.to_checksum_address(
'0xe0Ae8B04922d6e3fA06c2496A94EF2875EFcC7BB'
),
),
Network.SEPOLIA: NetworkConfig(
VAULT_USER_LTV_TRACKER_CONTRACT_ADDRESS=ZERO_CHECKSUM_ADDRESS,
PRICE_NETWORK_CONFIG=(
PriceNetworkConfig(
# TARGET_CHAIN is not what eth_chainId returns.
# It is internal id used in PriceFeedSender contract.
TARGET_CHAIN=10003,
# PriceFeedReceiver contract address on Arbitrum Sepolia
TARGET_ADDRESS=Web3.to_checksum_address(
'0x744836a91f5151c6ef730eb7e07c232997debaaa'
),
# PriceFeed contract address on Arbitrum Sepolia
TARGET_PRICE_FEED_CONTRACT_ADDRESS=Web3.to_checksum_address(
'0x4026affabd9032bcc87fa05c02f088905f3dc09b'
),
# PriceFeedSender contract address on Sepolia
PRICE_FEED_SENDER_CONTRACT_ADDRESS=Web3.to_checksum_address(
'0xe572a8631a49ec4c334812bb692beecf934ac4e9'
),
)
),
),
}
9 changes: 9 additions & 0 deletions src/common/settings.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
from decouple import config

from src.common.networks import NETWORKS, Network

EXECUTION_ENDPOINT: str = config('EXECUTION_ENDPOINT', default='')
HOT_WALLET_PRIVATE_KEY: str = config('HOT_WALLET_PRIVATE_KEY')

NETWORK: Network = config('NETWORK', cast=Network)
network_config = NETWORKS[NETWORK]
18 changes: 5 additions & 13 deletions src/ltv/clients.py
Original file line number Diff line number Diff line change
@@ -1,25 +1,17 @@
import logging

import gql
from eth_account import Account
from gql.transport.requests import RequestsHTTPTransport
from web3 import Web3
from web3.middleware import construct_sign_and_send_raw_middleware

from .settings import (
EXECUTION_ENDPOINT,
GRAPH_API_TIMEOUT,
GRAPH_API_URL,
HOT_WALLET_PRIVATE_KEY,
)
from src.common.clients import get_execution_client, hot_wallet_account
from src.common.settings import EXECUTION_ENDPOINT

from .settings import GRAPH_API_TIMEOUT, GRAPH_API_URL

logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)

hot_wallet_account = Account().from_key(HOT_WALLET_PRIVATE_KEY)
execution_client = Web3(Web3.HTTPProvider(EXECUTION_ENDPOINT))
execution_client.middleware_onion.add(construct_sign_and_send_raw_middleware(hot_wallet_account))
logger.info('Wallet address: %s', hot_wallet_account.address)
execution_client = get_execution_client(EXECUTION_ENDPOINT, account=hot_wallet_account)


def get_graph_client() -> gql.Client:
Expand Down
32 changes: 8 additions & 24 deletions src/ltv/contracts.py
Original file line number Diff line number Diff line change
@@ -1,36 +1,19 @@
import json
import logging
from pathlib import Path

from eth_typing import ChecksumAddress
from hexbytes import HexBytes
from web3 import Web3
from web3.contract.contract import ContractEvents, ContractFunctions

from .clients import execution_client, hot_wallet_account
from .settings import network_config
from src.common.clients import hot_wallet_account
from src.common.contracts import ContractWrapper
from src.common.settings import network_config

from .clients import execution_client
from .typings import HarvestParams

logger = logging.getLogger(__name__)


class ContractWrapper:
def __init__(self, abi_path: str, address: ChecksumAddress):
self.address = address
self.contract = execution_client.eth.contract(address=address, abi=self._load_abi(abi_path))

def _load_abi(self, abi_path: str) -> dict:
current_dir = Path(__file__).parent
with open(current_dir / abi_path, encoding='utf-8') as f:
return json.load(f)

@property
def functions(self) -> ContractFunctions:
return self.contract.functions

@property
def events(self) -> ContractEvents:
return self.contract.events
ABI_DIR = 'src/ltv/abi'


class VaultUserLTVTrackerContract(ContractWrapper):
Expand Down Expand Up @@ -65,6 +48,7 @@ def update_vault_max_ltv_user(


vault_user_ltv_tracker_contract = VaultUserLTVTrackerContract(
abi_path='abi/IVaultUserLtvTracker.json',
abi_path=f'{ABI_DIR}/IVaultUserLtvTracker.json',
address=network_config.VAULT_USER_LTV_TRACKER_CONTRACT_ADDRESS,
client=execution_client,
)
44 changes: 0 additions & 44 deletions src/ltv/networks.py

This file was deleted.

15 changes: 4 additions & 11 deletions src/ltv/settings.py
Original file line number Diff line number Diff line change
@@ -1,16 +1,9 @@
from decouple import config
from eth_typing import ChecksumAddress
from web3 import Web3

from .networks import NETWORKS, Network

NETWORK = config('NETWORK', cast=Network)
EXECUTION_ENDPOINT = config('EXECUTION_ENDPOINT')
HOT_WALLET_PRIVATE_KEY = config('HOT_WALLET_PRIVATE_KEY')

VAULT = config('VAULT', cast=Web3.to_checksum_address)
VAULT: ChecksumAddress = config('VAULT', cast=Web3.to_checksum_address)

# graph
GRAPH_API_URL = config('GRAPH_API_URL')
GRAPH_API_TIMEOUT = config('GRAPH_API_TIMEOUT', default='10', cast=int)

network_config = NETWORKS[NETWORK]
GRAPH_API_URL: str = config('GRAPH_API_URL')
GRAPH_API_TIMEOUT: int = config('GRAPH_API_TIMEOUT', default='10', cast=int)
Empty file added src/price/__init__.py
Empty file.
Loading

0 comments on commit 3f4c3ea

Please sign in to comment.