Skip to content

Commit

Permalink
Update to support Hyperdrive 0.0.14 (#936)
Browse files Browse the repository at this point in the history
Co-authored-by: Sheng Lundquist <sheng@delv.tech>
  • Loading branch information
dpaiton and slundqui authored Oct 5, 2023
1 parent 405485f commit a1128a1
Show file tree
Hide file tree
Showing 31 changed files with 3,202 additions and 2,790 deletions.
2 changes: 2 additions & 0 deletions lib/chainsync/chainsync/db/hyperdrive/schema.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ class PoolConfig(Base):
baseToken: Mapped[Union[str, None]] = mapped_column(String, default=None)
initialSharePrice: Mapped[Union[Decimal, None]] = mapped_column(FIXED_NUMERIC, default=None)
minimumShareReserves: Mapped[Union[Decimal, None]] = mapped_column(FIXED_NUMERIC, default=None)
minimumTransactionAmount: Mapped[Union[Decimal, None]] = mapped_column(FIXED_NUMERIC, default=None)
positionDuration: Mapped[Union[int, None]] = mapped_column(Integer, default=None)
checkpointDuration: Mapped[Union[int, None]] = mapped_column(Integer, default=None)
timeStretch: Mapped[Union[Decimal, None]] = mapped_column(FIXED_NUMERIC, default=None)
Expand Down Expand Up @@ -70,6 +71,7 @@ class PoolInfo(Base):
bondReserves: Mapped[Union[Decimal, None]] = mapped_column(FIXED_NUMERIC, default=None)
lpTotalSupply: Mapped[Union[Decimal, None]] = mapped_column(FIXED_NUMERIC, default=None)
sharePrice: Mapped[Union[Decimal, None]] = mapped_column(FIXED_NUMERIC, default=None)
shareAdjustment: Mapped[Union[Decimal, None]] = mapped_column(FIXED_NUMERIC, default=None)
lpSharePrice: Mapped[Union[Decimal, None]] = mapped_column(FIXED_NUMERIC, default=None)
longExposure: Mapped[Union[Decimal, None]] = mapped_column(FIXED_NUMERIC, default=None)
longsOutstanding: Mapped[Union[Decimal, None]] = mapped_column(FIXED_NUMERIC, default=None)
Expand Down
148 changes: 76 additions & 72 deletions lib/ethpy/ethpy/hyperdrive/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -178,36 +178,37 @@ def spot_price(self) -> FixedPoint:
"""
self._ensure_current_state()
pool_config_str = PoolConfig(
base_token=self._contract_pool_config["baseToken"],
initial_share_price=str(self._contract_pool_config["initialSharePrice"]),
minimum_share_reserves=str(self._contract_pool_config["minimumShareReserves"]),
position_duration=str(self._contract_pool_config["positionDuration"]),
checkpoint_duration=str(self._contract_pool_config["checkpointDuration"]),
time_stretch=str(self._contract_pool_config["timeStretch"]),
baseToken=self._contract_pool_config["baseToken"],
initialSharePrice=str(self._contract_pool_config["initialSharePrice"]),
minimumShareReserves=str(self._contract_pool_config["minimumShareReserves"]),
minimumTransactionAmount=str(self._contract_pool_config["minimumTransactionAmount"]),
positionDuration=str(self._contract_pool_config["positionDuration"]),
checkpointDuration=str(self._contract_pool_config["checkpointDuration"]),
timeStretch=str(self._contract_pool_config["timeStretch"]),
governance=self._contract_pool_config["governance"],
fee_collector=self._contract_pool_config["feeCollector"],
fees=Fees(
feeCollector=self._contract_pool_config["feeCollector"],
Fees=Fees(
curve=str(self._contract_pool_config["fees"][0]),
flat=str(self._contract_pool_config["fees"][1]),
governance=str(self._contract_pool_config["fees"][2]),
),
oracle_size=str(self._contract_pool_config["oracleSize"]),
update_gap=str(self._contract_pool_config["updateGap"]),
oracleSize=str(self._contract_pool_config["oracleSize"]),
updateGap=str(self._contract_pool_config["updateGap"]),
)
pool_info_str = PoolInfo(
share_reserves=str(self._contract_pool_info["shareReserves"]),
bond_reserves=str(self._contract_pool_info["bondReserves"]),
lp_total_supply=str(self._contract_pool_info["lpTotalSupply"]),
share_price=str(self._contract_pool_info["sharePrice"]),
longs_outstanding=str(self._contract_pool_info["longsOutstanding"]),
long_average_maturity_time=str(self._contract_pool_info["longAverageMaturityTime"]),
shorts_outstanding=str(self._contract_pool_info["shortsOutstanding"]),
short_average_maturity_time=str(self._contract_pool_info["shortAverageMaturityTime"]),
short_base_volume="0", # TODO: remove this from Pyperdrive
withdrawal_shares_ready_to_withdraw=str(self._contract_pool_info["withdrawalSharesReadyToWithdraw"]),
withdrawal_shares_proceeds=str(self._contract_pool_info["withdrawalSharesProceeds"]),
lp_share_price=str(self._contract_pool_info["lpSharePrice"]),
long_exposure=str(self._contract_pool_info["longExposure"]),
shareReserves=str(self._contract_pool_info["shareReserves"]),
shareAdjustment=str(self._contract_pool_info["shareAdjustment"]),
bondReserves=str(self._contract_pool_info["bondReserves"]),
lpTotalSupply=str(self._contract_pool_info["lpTotalSupply"]),
sharePrice=str(self._contract_pool_info["sharePrice"]),
longsOutstanding=str(self._contract_pool_info["longsOutstanding"]),
longAverageMaturityTime=str(self._contract_pool_info["longAverageMaturityTime"]),
shortsOutstanding=str(self._contract_pool_info["shortsOutstanding"]),
shortAverageMaturityTime=str(self._contract_pool_info["shortAverageMaturityTime"]),
withdrawalSharesReadyToWithdraw=str(self._contract_pool_info["withdrawalSharesReadyToWithdraw"]),
withdrawalSharesProceeds=str(self._contract_pool_info["withdrawalSharesProceeds"]),
lpSharePrice=str(self._contract_pool_info["lpSharePrice"]),
longExposure=str(self._contract_pool_info["longExposure"]),
)
spot_price = pyperdrive.get_spot_price(pool_config_str, pool_info_str) # pylint: disable=no-member
return FixedPoint(scaled_value=int(spot_price))
Expand Down Expand Up @@ -587,39 +588,40 @@ def get_max_long(self, budget: FixedPoint) -> FixedPoint:
The maximum long as a FixedPoint representation of a Solidity uint256 value.
"""
self._ensure_current_state()
# pylint: disable=no-member
pool_config_str = PoolConfig(
base_token=self._contract_pool_config["baseToken"],
initial_share_price=str(self._contract_pool_config["initialSharePrice"]),
minimum_share_reserves=str(self._contract_pool_config["minimumShareReserves"]),
position_duration=str(self._contract_pool_config["positionDuration"]),
checkpoint_duration=str(self._contract_pool_config["checkpointDuration"]),
time_stretch=str(self._contract_pool_config["timeStretch"]),
baseToken=self._contract_pool_config["baseToken"],
initialSharePrice=str(self._contract_pool_config["initialSharePrice"]),
minimumShareReserves=str(self._contract_pool_config["minimumShareReserves"]),
minimumTransactionAmount=str(self._contract_pool_config["minimumTransactionAmount"]),
positionDuration=str(self._contract_pool_config["positionDuration"]),
checkpointDuration=str(self._contract_pool_config["checkpointDuration"]),
timeStretch=str(self._contract_pool_config["timeStretch"]),
governance=self._contract_pool_config["governance"],
fee_collector=self._contract_pool_config["feeCollector"],
fees=Fees(
feeCollector=self._contract_pool_config["feeCollector"],
Fees=Fees(
curve=str(self._contract_pool_config["fees"][0]),
flat=str(self._contract_pool_config["fees"][1]),
governance=str(self._contract_pool_config["fees"][2]),
),
oracle_size=str(self._contract_pool_config["oracleSize"]),
update_gap=str(self._contract_pool_config["updateGap"]),
oracleSize=str(self._contract_pool_config["oracleSize"]),
updateGap=str(self._contract_pool_config["updateGap"]),
)
pool_info_str = PoolInfo(
share_reserves=str(self._contract_pool_info["shareReserves"]),
bond_reserves=str(self._contract_pool_info["bondReserves"]),
lp_total_supply=str(self._contract_pool_info["lpTotalSupply"]),
share_price=str(self._contract_pool_info["sharePrice"]),
longs_outstanding=str(self._contract_pool_info["longsOutstanding"]),
long_average_maturity_time=str(self._contract_pool_info["longAverageMaturityTime"]),
shorts_outstanding=str(self._contract_pool_info["shortsOutstanding"]),
short_average_maturity_time=str(self._contract_pool_info["shortAverageMaturityTime"]),
short_base_volume="0", # TODO: remove this from Pyperdrive
withdrawal_shares_ready_to_withdraw=str(self._contract_pool_info["withdrawalSharesReadyToWithdraw"]),
withdrawal_shares_proceeds=str(self._contract_pool_info["withdrawalSharesProceeds"]),
lp_share_price=str(self._contract_pool_info["lpSharePrice"]),
long_exposure=str(self._contract_pool_info["longExposure"]),
shareReserves=str(self._contract_pool_info["shareReserves"]),
shareAdjustment=str(self._contract_pool_info["shareAdjustment"]),
bondReserves=str(self._contract_pool_info["bondReserves"]),
lpTotalSupply=str(self._contract_pool_info["lpTotalSupply"]),
sharePrice=str(self._contract_pool_info["sharePrice"]),
longsOutstanding=str(self._contract_pool_info["longsOutstanding"]),
longAverageMaturityTime=str(self._contract_pool_info["longAverageMaturityTime"]),
shortsOutstanding=str(self._contract_pool_info["shortsOutstanding"]),
shortAverageMaturityTime=str(self._contract_pool_info["shortAverageMaturityTime"]),
withdrawalSharesReadyToWithdraw=str(self._contract_pool_info["withdrawalSharesReadyToWithdraw"]),
withdrawalSharesProceeds=str(self._contract_pool_info["withdrawalSharesProceeds"]),
lpSharePrice=str(self._contract_pool_info["lpSharePrice"]),
longExposure=str(self._contract_pool_info["longExposure"]),
)
# pylint: disable=no-member
max_long = pyperdrive.get_max_long(
pool_config_str,
pool_info_str,
Expand All @@ -643,44 +645,46 @@ def get_max_short(self, budget: FixedPoint) -> FixedPoint:
The maximum long as a FixedPoint representation of a Solidity uint256 value.
"""
self._ensure_current_state()
# pylint: disable=no-member
pool_config_str = PoolConfig(
base_token=self._contract_pool_config["baseToken"],
initial_share_price=str(self._contract_pool_config["initialSharePrice"]),
minimum_share_reserves=str(self._contract_pool_config["minimumShareReserves"]),
position_duration=str(self._contract_pool_config["positionDuration"]),
checkpoint_duration=str(self._contract_pool_config["checkpointDuration"]),
time_stretch=str(self._contract_pool_config["timeStretch"]),
baseToken=self._contract_pool_config["baseToken"],
initialSharePrice=str(self._contract_pool_config["initialSharePrice"]),
minimumShareReserves=str(self._contract_pool_config["minimumShareReserves"]),
minimumTransactionAmount=str(self._contract_pool_config["minimumTransactionAmount"]),
positionDuration=str(self._contract_pool_config["positionDuration"]),
checkpointDuration=str(self._contract_pool_config["checkpointDuration"]),
timeStretch=str(self._contract_pool_config["timeStretch"]),
governance=self._contract_pool_config["governance"],
fee_collector=self._contract_pool_config["feeCollector"],
fees=Fees(
feeCollector=self._contract_pool_config["feeCollector"],
Fees=Fees(
curve=str(self._contract_pool_config["fees"][0]),
flat=str(self._contract_pool_config["fees"][1]),
governance=str(self._contract_pool_config["fees"][2]),
),
oracle_size=str(self._contract_pool_config["oracleSize"]),
update_gap=str(self._contract_pool_config["updateGap"]),
oracleSize=str(self._contract_pool_config["oracleSize"]),
updateGap=str(self._contract_pool_config["updateGap"]),
)
pool_info_str = PoolInfo(
share_reserves=str(self._contract_pool_info["shareReserves"]),
bond_reserves=str(self._contract_pool_info["bondReserves"]),
lp_total_supply=str(self._contract_pool_info["lpTotalSupply"]),
share_price=str(self._contract_pool_info["sharePrice"]),
longs_outstanding=str(self._contract_pool_info["longsOutstanding"]),
long_average_maturity_time=str(self._contract_pool_info["longAverageMaturityTime"]),
shorts_outstanding=str(self._contract_pool_info["shortsOutstanding"]),
short_average_maturity_time=str(self._contract_pool_info["shortAverageMaturityTime"]),
short_base_volume="0", # TODO: remove this from Pyperdrive
withdrawal_shares_ready_to_withdraw=str(self._contract_pool_info["withdrawalSharesReadyToWithdraw"]),
withdrawal_shares_proceeds=str(self._contract_pool_info["withdrawalSharesProceeds"]),
lp_share_price=str(self._contract_pool_info["lpSharePrice"]),
long_exposure=str(self._contract_pool_info["longExposure"]),
shareReserves=str(self._contract_pool_info["shareReserves"]),
shareAdjustment=str(self._contract_pool_info["shareAdjustment"]),
bondReserves=str(self._contract_pool_info["bondReserves"]),
lpTotalSupply=str(self._contract_pool_info["lpTotalSupply"]),
sharePrice=str(self._contract_pool_info["sharePrice"]),
longsOutstanding=str(self._contract_pool_info["longsOutstanding"]),
longAverageMaturityTime=str(self._contract_pool_info["longAverageMaturityTime"]),
shortsOutstanding=str(self._contract_pool_info["shortsOutstanding"]),
shortAverageMaturityTime=str(self._contract_pool_info["shortAverageMaturityTime"]),
withdrawalSharesReadyToWithdraw=str(self._contract_pool_info["withdrawalSharesReadyToWithdraw"]),
withdrawalSharesProceeds=str(self._contract_pool_info["withdrawalSharesProceeds"]),
lpSharePrice=str(self._contract_pool_info["lpSharePrice"]),
longExposure=str(self._contract_pool_info["longExposure"]),
)
# pylint: disable=no-member
max_short = pyperdrive.get_max_short(
pool_config_str,
pool_info_str,
str(budget.scaled_value),
pool_info_str.share_price,
pool_info_str.sharePrice,
checkpoint_exposure=str(self.latest_checkpoint["longExposure"].scaled_value),
maybe_conservative_price=None,
maybe_max_iterations=None,
)
Expand Down
2 changes: 1 addition & 1 deletion lib/ethpy/ethpy/hyperdrive/interface.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ def process_hyperdrive_pool_config(pool_config: dict[str, Any], hyperdrive_addre
The hyperdrive pool config with modified types.
"""
# convert values to FixedPoint
fixedpoint_keys = ["initialSharePrice", "minimumShareReserves", "timeStretch"]
fixedpoint_keys = ["initialSharePrice", "minimumShareReserves", "timeStretch", "minimumTransactionAmount"]
for key in pool_config:
if key in fixedpoint_keys:
pool_config[key] = FixedPoint(scaled_value=pool_config[key])
Expand Down
33 changes: 33 additions & 0 deletions lib/ethpy/ethpy/hyperdrive/interface_test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
"""Tests for hyperdrive/api.py"""
from __future__ import annotations

from typing import cast

from eth_typing import URI
from ethpy import EthConfig
from ethpy.base.transactions import smart_contract_read
from ethpy.hyperdrive.addresses import HyperdriveAddresses
from ethpy.test_fixtures.local_chain import LocalHyperdriveChain
from web3 import HTTPProvider

from .get_web3_and_hyperdrive_contracts import get_web3_and_hyperdrive_contracts

# pylint: disable=too-many-locals


class TestHyperdriveInterface:
"""Tests for the low-level hyperdrive interface functions."""

def test_get_pool_config(self, local_hyperdrive_chain: LocalHyperdriveChain):
"""Checks that the Hyperdrive smart contract function getPoolConfig works.
All arguments are fixtures.
"""
uri: URI | None = cast(HTTPProvider, local_hyperdrive_chain.web3.provider).endpoint_uri
rpc_uri = uri if uri else URI("http://localhost:8545")
abi_dir = "./packages/hyperdrive/src/abis"
hyperdrive_contract_addresses: HyperdriveAddresses = local_hyperdrive_chain.hyperdrive_contract_addresses
eth_config = EthConfig(artifacts_uri="not used", rpc_uri=rpc_uri, abi_dir=abi_dir)
_, _, hyperdrive_contract = get_web3_and_hyperdrive_contracts(eth_config, hyperdrive_contract_addresses)
pool_config = smart_contract_read(hyperdrive_contract, "getPoolConfig")
assert pool_config is not None
38 changes: 34 additions & 4 deletions lib/ethpy/ethpy/test_fixtures/deploy_hyperdrive.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
"""Functions to initialize hyperdrive using web3"""
from __future__ import annotations

from dataclasses import fields, is_dataclass
from typing import Any

from eth_account.account import Account
from eth_account.signers.local import LocalAccount
Expand All @@ -12,6 +16,7 @@
smart_contract_transact,
)
from fixedpointmath import FixedPoint
from hypertypes.IHyperdriveTypes import Fees, PoolConfig
from web3 import Web3
from web3.contract.contract import Contract

Expand All @@ -20,13 +25,35 @@
# initial hyperdrive conditions


def _dataclass_to_tuple(instance: Any) -> tuple:
"""Resursively convert the input Dataclass to a tuple.
Iterate over the fields of the dataclass and compiles them into a tuple.
Check if the type of a field is also a dataclass, and if so, recursively convert it to a tuple.
This method preserves the attribute ordering.
Arguments
---------
instance : dataclass
A dataclass, whose fields could contain other dataclasses.
Returns
-------
tuple
A nested tuple of all dataclass fields.
"""
if not is_dataclass(instance):
return instance
return tuple(_dataclass_to_tuple(getattr(instance, field.name)) for field in fields(instance))


# Following solidity implementation here, so matching function name
def _calculateTimeStretch(apr: int) -> int: # pylint: disable=invalid-name
"""Helper function mirroring solidity calculateTimeStretch
Arguments
---------
apr: int
apr : int
The scaled input apr
Returns
Expand Down Expand Up @@ -184,6 +211,7 @@ def deploy_and_initialize_hyperdrive(
# Initial hyperdrive settings
initial_share_price = FixedPoint(1).scaled_value
minimum_share_reserves = FixedPoint(10).scaled_value
minimum_transaction_amount = FixedPoint("0.001").scaled_value
position_duration = 604800 # 1 week
checkpoint_duration = 3600 # 1 hour
time_stretch = _calculateTimeStretch(FixedPoint("0.05").scaled_value)
Expand All @@ -204,22 +232,24 @@ def deploy_and_initialize_hyperdrive(

# Call factory to make hyperdrive market
# Some of these pool info configurations don't do anything, as the factory is overwriting them
pool_config = (
# Using the Pypechain generated HyperdriveTypes for PoolConfig to ensure the ordering & type safety
pool_config = PoolConfig(
base_token_contract.address,
initial_share_price,
minimum_share_reserves,
minimum_transaction_amount,
position_duration,
checkpoint_duration,
time_stretch,
deploy_account_addr, # governance, overwritten by factory
deploy_account_addr, # feeCollector, overwritten by factory
(0, 0, 0), # fees, overwritten by factory
Fees(0, 0, 0), # fees, overwritten by factory
oracle_size, # oracleSize
update_gap,
)
# Function arguments
fn_args = (
pool_config,
_dataclass_to_tuple(pool_config),
[], # new bytes[](0)
initial_contribution,
initial_fixed_rate, # fixedRate
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,5 @@ sh scripts/build_type_files.sh path/to/ABIs num_chars_per_line
You can then pip install the package from the project root with

```bash
pip install -e lib/hyperdrive_types
pip install -e lib/hypertypes[base]
```
Loading

1 comment on commit a1128a1

@vercel
Copy link

@vercel vercel bot commented on a1128a1 Oct 5, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.