Skip to content

Commit

Permalink
Minor dev exp updates
Browse files Browse the repository at this point in the history
  • Loading branch information
miohtama committed Jan 5, 2025
1 parent 0d4e047 commit 4155eba
Show file tree
Hide file tree
Showing 7 changed files with 84 additions and 32 deletions.
1 change: 1 addition & 0 deletions eth_defi/abi.py
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,7 @@ def get_deployed_contract(
:return:
`web3.contract.Contract` proxy
"""
assert isinstance(web3, Web3), f"Got {type(web3)} instead of Web3"
assert address, f"get_deployed_contract() address was None"

address = Web3.to_checksum_address(address)
Expand Down
5 changes: 4 additions & 1 deletion eth_defi/gas.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ def get_tx_gas_params(self) -> dict:
return {"gasPrice": self.legacy_gas_price}


def estimate_gas_fees(web3: Web3) -> GasPriceSuggestion:
def estimate_gas_price(web3: Web3) -> GasPriceSuggestion:
"""Get a good gas price for a transaction.
TODO: This is non-optimal, first draft implementation.
Expand Down Expand Up @@ -88,6 +88,9 @@ def estimate_gas_fees(web3: Web3) -> GasPriceSuggestion:
return GasPriceSuggestion(method=GasPriceMethod.legacy, legacy_gas_price=web3.eth.generate_gas_price())


# Legacy
estimate_gas_fees = estimate_gas_price

def apply_gas(tx: dict, suggestion: GasPriceSuggestion) -> dict:
"""Apply gas fees to a raw transaction dict.
Expand Down
30 changes: 29 additions & 1 deletion eth_defi/hotwallet.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
from web3._utils.contracts import prepare_transaction
from web3.contract.contract import ContractFunction

from eth_defi.gas import estimate_gas_fees, apply_gas
from eth_defi.gas import estimate_gas_fees, apply_gas, estimate_gas_price
from eth_defi.tx import decode_signed_transaction


Expand Down Expand Up @@ -242,6 +242,8 @@ def sign_bound_call_with_new_nonce(
self,
func: ContractFunction,
tx_params: dict | None = None,
web3: Web3 | None=None,
fill_gas_price=True,
) -> SignedTransactionWithNonce:
"""Signs a bound Web3 Contract call.
Expand All @@ -262,6 +264,18 @@ def sign_bound_call_with_new_nonce(
tx_gas_parameters = apply_gas({"gas": 100_000}, gas_estimation) # approve should not take more than 100k gas
signed_tx = hot_wallet.sign_bound_call_with_new_nonce(approve_call, tx_gas_parameters)
Another example that fills in gas price automatically (but not gas limit):
.. code-block:: python
bound_func = vault.settle_via_trading_strategy_module()
signed_tx_2 = self.hot_wallet.sign_bound_call_with_new_nonce(
bound_func,
tx_params={"gas": DEFAULT_LAGOON_SETTLE_GAS},
web3=web3,
fill_gas_price=True
)
See also
- :py:meth:`sign_transaction_with_new_nonce`
Expand All @@ -271,6 +285,15 @@ def sign_bound_call_with_new_nonce(
:param tx_params:
Transaction parameters like `gas`
:param web3:
Needed for gas price estimation
:param fill_gas_price:
Fill the gas price automatically.
:return:
A signed transaction with debugging details like used nonce.
"""
assert isinstance(func, ContractFunction)

Expand All @@ -284,6 +307,11 @@ def sign_bound_call_with_new_nonce(
if "chainId" not in tx_params:
tx_params["chainId"] = func.w3.eth.chain_id

if fill_gas_price:
assert web3, f"web3 instance must be given for automatic gas price fill"
gas_price_suggestion = estimate_gas_price(web3)
apply_gas(tx_params, gas_price_suggestion)

if original_tx_params is None:
# Use the default gas filler
tx = func.build_transaction(tx_params)
Expand Down
15 changes: 15 additions & 0 deletions eth_defi/lagoon/analysis.py
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,9 @@ class LagoonSettlementEvent:
#:
pending_redemptions_shares: Decimal

#: Balance of the underlying token (treasuty/reserve) at the end of the block
underlying_balance: Decimal

@property
def underlying(self) -> TokenDetails:
"""Get USDC."""
Expand Down Expand Up @@ -124,6 +127,15 @@ def get_serialiable_diagnostics_data(self) -> dict:
"pending_redemptions_shares": self.pending_redemptions_shares,
}

def get_underlying_diff(self) -> Decimal:
"""How much the underlying asset changed in the vault treasury"""
return self.deposited - self.redeemed

def get_underlying_balance(self) -> Decimal:
"""How much of treasury we are holding after this update"""
return self.underlying_balance


def analyse_vault_flow_in_settlement(
vault: LagoonVault,
tx_hash: HexBytes,
Expand Down Expand Up @@ -171,6 +183,8 @@ def analyse_vault_flow_in_settlement(
else:
share_price = Decimal(0)

underlying_balance = vault.underlying_token.fetch_balance_of(vault.safe_address, block_number)

return LagoonSettlementEvent(
chain_id=vault.chain_id,
tx_hash=tx_hash,
Expand All @@ -188,4 +202,5 @@ def analyse_vault_flow_in_settlement(
pending_redemptions_shares=pending_shares,
pending_redemptions_underlying=pending_shares * share_price,
share_price=share_price,
underlying_balance=underlying_balance,
)
6 changes: 6 additions & 0 deletions eth_defi/lagoon/vault.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,12 @@

logger = logging.getLogger(__name__)

#: How much gas we use for valuation post
DEFAULT_LAGOON_POST_VALUATION_GAS = 500_000

#: How much gas we use for valuation post
DEFAULT_LAGOON_SETTLE_GAS = 500_000


class LagoonVaultInfo(VaultInfo):
"""Capture information about Lagoon vault deployment."""
Expand Down
57 changes: 27 additions & 30 deletions eth_defi/token.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,13 +33,11 @@
#: `ValueError` is raised by Ganache
_call_missing_exceptions = (TransactionFailed, BadFunctionCallOutput, ValueError, ContractLogicError)


#: By default we cache 1024 token details using LRU.
#:
#:
DEFAULT_TOKEN_CACHE = cachetools.LRUCache(1024)


#: ERC-20 address, 0x prefixed string
TokenAddress: TypeAlias = str

Expand Down Expand Up @@ -123,7 +121,7 @@ def convert_to_decimals(self, raw_amount: int) -> Decimal:
assert details.convert_to_decimals(1) == Decimal("0.0000000000000001")
"""
return Decimal(raw_amount) / Decimal(10**self.decimals)
return Decimal(raw_amount) / Decimal(10 ** self.decimals)

def convert_to_raw(self, decimal_amount: Decimal) -> int:
"""Convert decimalised token amount to raw uint256.
Expand All @@ -137,7 +135,7 @@ def convert_to_raw(self, decimal_amount: Decimal) -> int:
assert details.convert_to_raw(1) == 1_000_000
"""
return int(decimal_amount * 10**self.decimals)
return int(decimal_amount * 10 ** self.decimals)

def fetch_balance_of(self, address: HexAddress | str, block_identifier="latest") -> Decimal:
"""Get an address token balance.
Expand All @@ -153,9 +151,9 @@ def fetch_balance_of(self, address: HexAddress | str, block_identifier="latest")
return self.convert_to_decimals(raw_amount)

def transfer(
self,
to: HexAddress | str,
amount: Decimal,
self,
to: HexAddress | str,
amount: Decimal,
) -> ContractFunction:
"""Prepare a ERC20.transfer() transaction with human-readable amount.
Expand All @@ -176,9 +174,9 @@ def transfer(
return self.contract.functions.transfer(to, raw_amount)

def approve(
self,
to: HexAddress | str,
amount: Decimal,
self,
to: HexAddress | str,
amount: Decimal,
) -> ContractFunction:
"""Prepare a ERC20.approve() transaction with human-readable amount.
Expand Down Expand Up @@ -230,12 +228,12 @@ class TokenDetailError(Exception):


def create_token(
web3: Web3,
deployer: str,
name: str,
symbol: str,
supply: int,
decimals: int = 18,
web3: Web3,
deployer: str,
name: str,
symbol: str,
supply: int,
decimals: int = 18,
) -> Contract:
"""Deploys a new ERC-20 token on local dev, testnet or mainnet.
Expand Down Expand Up @@ -280,22 +278,22 @@ def create_token(


def get_erc20_contract(
web3: Web3,
address: HexAddress,
contract_name="ERC20MockDecimals.json",
web3: Web3,
address: HexAddress,
contract_name="ERC20MockDecimals.json",
) -> Contract:
"""Wrap address as ERC-20 standard interface."""
return get_deployed_contract(web3, contract_name, address)


def fetch_erc20_details(
web3: Web3,
token_address: Union[HexAddress, str],
max_str_length: int = 256,
raise_on_error=True,
contract_name="ERC20MockDecimals.json",
cache: cachetools.Cache | None = DEFAULT_TOKEN_CACHE,
chain_id: int = None,
web3: Web3,
token_address: Union[HexAddress, str],
max_str_length: int = 256,
raise_on_error=True,
contract_name="ERC20MockDecimals.json",
cache: cachetools.Cache | None = DEFAULT_TOKEN_CACHE,
chain_id: int = None,
) -> TokenDetails:
"""Read token details from on-chain data.
Expand Down Expand Up @@ -441,18 +439,17 @@ def get_wrapped_native_token_address(chain_id: int):


#: Addresses of wrapped native token (WETH9) of different chains
WRAPPED_NATIVE_TOKEN: dict[int,HexAddress] = {
WRAPPED_NATIVE_TOKEN: dict[int, HexAddress] = {
# Mainnet
1: "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2",
# Base
8453: "0x4200000000000000000000000000000000000006",
}


#: Addresses of wrapped native token (WETH9) of different chains
USDC_NATIVE_TOKEN: dict[int,HexAddress] = {
USDC_NATIVE_TOKEN: dict[int, HexAddress] = {
# Mainnet
1: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
# Base
8453: "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
}
}
2 changes: 2 additions & 0 deletions tests/lagoon/test_lagoon_flow_analysis.py
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ def test_lagoon_deposit_redeem(
assert analysis.share_price == Decimal(1) # No share price yet, because valuation as done for the empty vault
assert analysis.deposit_events == 1
assert analysis.redeem_events == 0
assert analysis.underlying_balance == pytest.approx(Decimal(9))
assert usdc.fetch_balance_of(vault.silo_address) == pytest.approx(Decimal(0))
assert usdc.fetch_balance_of(vault.safe_address) == pytest.approx(Decimal(9))

Expand Down Expand Up @@ -132,6 +133,7 @@ def test_lagoon_deposit_redeem(
assert analysis.total_supply == pytest.approx(Decimal(11))
assert analysis.deposit_events == 1
assert analysis.redeem_events == 1
assert analysis.underlying_balance == pytest.approx(Decimal(11))

# No events, 3 USDC redemption held by vault smart contract, Safe helds 11 USDC
assert usdc.fetch_balance_of(vault.address) == pytest.approx(Decimal(3))
Expand Down

0 comments on commit 4155eba

Please sign in to comment.