Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Hyperdrive 0 3 0 #1087

Merged
merged 22 commits into from
Nov 15, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
47b3c95
Updating abis
slundqui Nov 13, 2023
2cbe53c
Updating pyperdrive to v0.3.0
slundqui Nov 13, 2023
f0400f8
Adding hyperdrive target abis
slundqui Nov 13, 2023
78c29dc
Updating hypertypes
slundqui Nov 13, 2023
6d51730
Updating pool config from hypertypes
slundqui Nov 13, 2023
88f55b0
Using IERC4626 to get underlying yield source contract
slundqui Nov 13, 2023
bd9d2c6
Updating python side of pool config class
slundqui Nov 13, 2023
d30107d
Renaming long_exposure to exposure
slundqui Nov 13, 2023
8f066e7
Adjusting pool_config schema
slundqui Nov 13, 2023
f089bc6
Using IERC4626 to get yield source
slundqui Nov 13, 2023
d9b9b4c
Fixing deploy with new signatures
slundqui Nov 13, 2023
bf53237
Removing args from deployer calls
slundqui Nov 13, 2023
8c2a8e3
Using constant for zero address
slundqui Nov 14, 2023
a895c12
Adjusting start block of data pipeline due to new deploy
slundqui Nov 14, 2023
2d43180
Adjusting test expected values with new pool config
slundqui Nov 14, 2023
d584628
New underlying error when adding liquidity for more base than a walle…
slundqui Nov 14, 2023
56bd4e3
Fixed an issue with trades targeting nonzero withdrawal shares nettin…
slundqui Nov 14, 2023
a0f1424
Removing dead todo
slundqui Nov 14, 2023
2ac3a56
Fixing interment issues with building transaction by retrying
slundqui Nov 14, 2023
ccea3f0
Fixing race condition for building transactions in preview
slundqui Nov 14, 2023
7288d39
Removing IHyperdrive abi in favor of IERC4626Hyperdrive
slundqui Nov 15, 2023
a777d7b
Docstring
slundqui Nov 15, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion lib/agent0/bin/checkpoint_bot.py
Original file line number Diff line number Diff line change
Expand Up @@ -91,10 +91,11 @@ def main() -> None:
logging.info("Successfully funded the sender=%s.", sender.address)

# Get the Hyperdrive contract.
# TODO replace this with the hyperdrive interface
hyperdrive_abis = load_all_abis(eth_config.abi_dir)
addresses = fetch_hyperdrive_address_from_uri(os.path.join(eth_config.artifacts_uri, "addresses.json"))
hyperdrive_contract: Contract = web3.eth.contract(
abi=hyperdrive_abis["IHyperdrive"],
abi=hyperdrive_abis["IERC4626Hyperdrive"],
address=web3.to_checksum_address(addresses.mock_hyperdrive),
)

Expand Down
2 changes: 1 addition & 1 deletion lib/chainsync/chainsync/db/hyperdrive/interface.py
Original file line number Diff line number Diff line change
Expand Up @@ -286,7 +286,7 @@ def get_checkpoint_info(
This includes
- `share_price` : The share price of the first transaction in the checkpoint.
- `longSharePrice` : The weighted average of the share prices that all longs in the checkpoint were opened at.
- `long_exposure` : The amount of open longs for that checkpoint.
- `exposure` : The exposure for that checkpoint.

Arguments
---------
Expand Down
8 changes: 4 additions & 4 deletions lib/chainsync/chainsync/db/hyperdrive/schema.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,13 @@ class PoolConfig(Base):
__tablename__ = "pool_config"

contract_address: Mapped[str] = mapped_column(String, primary_key=True)
linker_factory: Mapped[Union[str, None]] = mapped_column(String, default=None)
# Ignoring linker_code_hash field
base_token: Mapped[Union[str, None]] = mapped_column(String, default=None)
initial_share_price: Mapped[Union[Decimal, None]] = mapped_column(FIXED_NUMERIC, default=None)
minimum_share_reserves: Mapped[Union[Decimal, None]] = mapped_column(FIXED_NUMERIC, default=None)
minimum_transaction_amount: Mapped[Union[Decimal, None]] = mapped_column(FIXED_NUMERIC, default=None)
precision_threshold: Mapped[Union[int, None]] = mapped_column(BigInteger, default=None)
position_duration: Mapped[Union[int, None]] = mapped_column(Integer, default=None)
checkpoint_duration: Mapped[Union[int, None]] = mapped_column(Integer, default=None)
time_stretch: Mapped[Union[Decimal, None]] = mapped_column(FIXED_NUMERIC, default=None)
Expand All @@ -39,10 +42,7 @@ class PoolConfig(Base):
curve_fee: Mapped[Union[Decimal, None]] = mapped_column(FIXED_NUMERIC, default=None)
flat_fee: Mapped[Union[Decimal, None]] = mapped_column(FIXED_NUMERIC, default=None)
governance_fee: Mapped[Union[Decimal, None]] = mapped_column(FIXED_NUMERIC, default=None)
oracle_size: Mapped[Union[int, None]] = mapped_column(Integer, default=None)
update_gap: Mapped[Union[int, None]] = mapped_column(Integer, default=None)
inv_time_stretch: Mapped[Union[Decimal, None]] = mapped_column(FIXED_NUMERIC, default=None)
update_gap: Mapped[Union[int, None]] = mapped_column(Integer, default=None)


class CheckpointInfo(Base):
Expand All @@ -53,7 +53,7 @@ class CheckpointInfo(Base):
block_number: Mapped[int] = mapped_column(BigInteger, primary_key=True)
timestamp: Mapped[datetime] = mapped_column(DateTime)
share_price: Mapped[Union[Decimal, None]] = mapped_column(FIXED_NUMERIC, default=None)
long_exposure: Mapped[Union[Decimal, None]] = mapped_column(FIXED_NUMERIC, default=None)
exposure: Mapped[Union[Decimal, None]] = mapped_column(FIXED_NUMERIC, default=None)


class PoolInfo(Base):
Expand Down
37 changes: 24 additions & 13 deletions lib/ethpy/ethpy/base/transactions.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,16 @@
READ_RETRY_COUNT = 5


# We define the function to check the exception to retry on
# for preview calls.
# This is the error we get when preview fails due to anvil
def _retry_preview_check(exc: Exception) -> bool:
return (
isinstance(exc, ContractPanicError)
and exc.args[0] == "Panic error 0x11: Arithmetic operation results in underflow or overflow."
)


def smart_contract_read(
contract: Contract,
function_name_or_signature: str,
Expand Down Expand Up @@ -147,27 +157,27 @@ def smart_contract_preview_transaction(
else:
function = contract.get_function_by_name(function_name_or_signature)(*fn_args, **fn_kwargs)

# We define the function to check the exception to retry on
# This is the error we get when preview fails due to anvil
def retry_preview_check(exc: Exception) -> bool:
return (
isinstance(exc, ContractPanicError)
and exc.args[0] == "Panic error 0x11: Arithmetic operation results in underflow or overflow."
)

# This is the additional transaction argument passed into function.call
# that may contain additional call arguments such as max_gas, nonce, etc.
transaction_kwargs = {"from": signer_address}

raw_txn = {}
# We build the raw transaction here in case of error, where we want to attach the raw txn to the crash report.
# Note that we don't call `build_transaction`
# since it adds the nonce to the transaction, and we ignore nonce in preview
# This is a best attempt at building a transaction for the preview call, because
# this function doesn't accept a block_number as an argument,
# so there's a race condition if a new trade comes in and this preview call is no longer valid
# Hence, we wrap this in a try/catch, and ignore if it fails
try:
# We build the raw transaction here in case of error. Note that we don't call `build_transaction`
# since it adds the nonce to the transaction, and we ignore nonce in preview
# Build transactions can fail, so we put this here in the try/catch
raw_txn = function.build_transaction({"from": signer_address})
except Exception: # pylint: disable=broad-except
pass

try:
return_values = retry_call(
READ_RETRY_COUNT,
retry_preview_check,
_retry_preview_check,
function.call,
transaction_kwargs,
block_identifier=block_number,
Expand Down Expand Up @@ -348,7 +358,8 @@ def build_transaction(
"nonce": nonce,
}
)
unsent_txn = func_handle.build_transaction(transaction_kwargs)
# Building transactions can also fail, so we add retry here
unsent_txn = retry_call(READ_RETRY_COUNT, _retry_preview_check, func_handle.build_transaction, transaction_kwargs)
return unsent_txn


Expand Down
9 changes: 5 additions & 4 deletions lib/ethpy/ethpy/hyperdrive/api/_mock_contract.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,12 @@ def _construct_pool_config(contract_pool_config: dict[str, Any]) -> PoolConfig:
"""
return PoolConfig(
baseToken=contract_pool_config["baseToken"],
linkerFactory=contract_pool_config["linkerFactory"],
linkerCodeHash=contract_pool_config["linkerCodeHash"],
initialSharePrice=contract_pool_config["initialSharePrice"],
minimumShareReserves=contract_pool_config["minimumShareReserves"],
minimumTransactionAmount=contract_pool_config["minimumTransactionAmount"],
precisionThreshold=contract_pool_config["precisionThreshold"],
positionDuration=contract_pool_config["positionDuration"],
checkpointDuration=contract_pool_config["checkpointDuration"],
timeStretch=contract_pool_config["timeStretch"],
Expand All @@ -36,8 +39,6 @@ def _construct_pool_config(contract_pool_config: dict[str, Any]) -> PoolConfig:
flat=contract_pool_config["fees"][1],
governance=contract_pool_config["fees"][2],
),
oracleSize=contract_pool_config["oracleSize"],
updateGap=contract_pool_config["updateGap"],
)


Expand Down Expand Up @@ -275,7 +276,7 @@ def _calc_max_long(pool_state: PoolState, budget: FixedPoint) -> FixedPoint:
_construct_pool_config(pool_state.contract_pool_config),
_construct_pool_info(pool_state.contract_pool_info),
str(budget.scaled_value),
checkpoint_exposure=str(pool_state.checkpoint.long_exposure.scaled_value),
checkpoint_exposure=str(pool_state.checkpoint.exposure.scaled_value),
maybe_max_iterations=None,
)
)
Expand All @@ -291,7 +292,7 @@ def _calc_max_short(pool_state: PoolState, budget: FixedPoint) -> FixedPoint:
pool_info=_construct_pool_info(pool_state.contract_pool_info),
budget=str(budget.scaled_value),
open_share_price=str(pool_state.pool_info.share_price.scaled_value),
checkpoint_exposure=str(pool_state.checkpoint.long_exposure.scaled_value),
checkpoint_exposure=str(pool_state.checkpoint.exposure.scaled_value),
maybe_conservative_price=None,
maybe_max_iterations=None,
)
Expand Down
8 changes: 2 additions & 6 deletions lib/ethpy/ethpy/hyperdrive/api/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -141,13 +141,9 @@ def __init__(
)
# Setup Hyperdrive and Yield (variable rate) contracts.
self.hyperdrive_contract: Contract = web3.eth.contract(
abi=abis["IHyperdrive"], address=web3.to_checksum_address(self.addresses.mock_hyperdrive)
abi=abis["IERC4626Hyperdrive"], address=web3.to_checksum_address(self.addresses.mock_hyperdrive)
)
# TODO: in the future we want to switch to a single IERC4626Hyperdrive ABI
slundqui marked this conversation as resolved.
Show resolved Hide resolved
data_provider_contract: Contract = web3.eth.contract(
abi=abis["ERC4626DataProvider"], address=web3.to_checksum_address(self.addresses.mock_hyperdrive)
)
self.yield_address = smart_contract_read(data_provider_contract, "pool")["value"]
self.yield_address = smart_contract_read(self.hyperdrive_contract, "pool")["value"]
self.yield_contract: Contract = web3.eth.contract(
abi=abis["MockERC4626"], address=web3.to_checksum_address(self.yield_address)
)
Expand Down
44 changes: 31 additions & 13 deletions lib/ethpy/ethpy/hyperdrive/deploy.py
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ def deploy_hyperdrive_from_factory(
# ERC20Mintable, MockERC4626, ForwarderFactory, ERC4626HyperdriveDeployer, ERC4626HyperdriveFactory
abis, bytecodes = load_all_abis(abi_folder, return_bytecode=True)
# Deploy the factory and base token contracts
base_token_contract, factory_contract = _deploy_hyperdrive_factory(
base_token_contract, factory_contract, pool_contract_addr = _deploy_hyperdrive_factory(
web3,
deploy_account_addr,
abis,
Expand All @@ -122,6 +122,7 @@ def deploy_hyperdrive_from_factory(
deploy_account,
initial_liquidity,
initial_fixed_rate,
pool_contract_addr,
pool_config,
factory_contract,
)
Expand All @@ -135,7 +136,7 @@ def deploy_hyperdrive_from_factory(
mock_hyperdrive=hyperdrive_checksum_address,
mock_hyperdrive_math=None,
),
hyperdrive_contract=web3.eth.contract(address=hyperdrive_checksum_address, abi=abis["IHyperdrive"]),
hyperdrive_contract=web3.eth.contract(address=hyperdrive_checksum_address, abi=abis["IERC4626Hyperdrive"]),
hyperdrive_factory_contract=factory_contract,
base_token_contract=base_token_contract,
)
Expand Down Expand Up @@ -197,7 +198,7 @@ def _deploy_hyperdrive_factory(
initial_variable_rate: FixedPoint,
pool_config: PoolConfig,
max_fees: Fees,
) -> tuple[Contract, Contract]:
) -> tuple[Contract, Contract, ChecksumAddress]:
slundqui marked this conversation as resolved.
Show resolved Hide resolved
"""Deploys the hyperdrive factory contract on the rpc_uri chain.

Arguments
Expand All @@ -220,8 +221,8 @@ def _deploy_hyperdrive_factory(

Returns
-------
(base_token_contract, factory_token_contract) : tuple[Contract, Contract]
Containing he deployed base token and factory contracts.
(base_token_contract, factory_token_contract, pool_contract_address) : tuple[Contract, Contract, ChecksumAddress]
Containing the deployed base token, factory, and the pool contracts/addresses.
"""
# args = [name, symbol, decimals, admin_addr, isCompetitionMode]
args = ["Base", "BASE", 18, ADDRESS_ZERO, False]
Expand Down Expand Up @@ -259,8 +260,20 @@ def _deploy_hyperdrive_factory(
abi=abis["ERC4626HyperdriveDeployer"],
bytecode=bytecodes["ERC4626HyperdriveDeployer"],
deploy_account_addr=deploy_account_addr,
args=[pool_contract_addr],
)
target_0_deployer_addr, _ = deploy_contract(
web3,
abi=abis["ERC4626Target0Deployer"],
bytecode=bytecodes["ERC4626Target0Deployer"],
deploy_account_addr=deploy_account_addr,
)
target_1_deployer_addr, _ = deploy_contract(
web3,
abi=abis["ERC4626Target1Deployer"],
bytecode=bytecodes["ERC4626Target1Deployer"],
deploy_account_addr=deploy_account_addr,
)

_, factory_contract = deploy_contract(
web3,
abi=abis["ERC4626HyperdriveFactory"],
Expand All @@ -270,19 +283,20 @@ def _deploy_hyperdrive_factory(
( # factory config
deploy_account_addr, # governance
deploy_account_addr, # hyperdriveGovernance
[], # defaultPausers (new address[](1))
deploy_account_addr, # feeCollector
_dataclass_to_tuple(pool_config.fees), # curve, flat, governance
_dataclass_to_tuple(max_fees), # max_curve, max_flat, max_governance
[], # defaultPausers (new address[](1))
deployer_contract_addr, # Hyperdrive deployer
target_0_deployer_addr,
target_1_deployer_addr,
forwarder_factory_contract_addr, # Linker factory
forwarder_factory_contract.functions.ERC20LINK_HASH().call(), # linkerCodeHash
),
deployer_contract_addr,
forwarder_factory_contract_addr,
forwarder_factory_contract.functions.ERC20LINK_HASH().call(),
pool_contract_addr,
[], # new address[](0)
],
)
return base_token_contract, factory_contract
return base_token_contract, factory_contract, pool_contract_addr


def _mint_and_approve(
Expand Down Expand Up @@ -338,6 +352,7 @@ def _deploy_and_initialize_hyperdrive_pool(
deploy_account: LocalAccount,
initial_liquidity: FixedPoint,
initial_fixed_rate: FixedPoint,
pool_contract_addr: ChecksumAddress,
slundqui marked this conversation as resolved.
Show resolved Hide resolved
pool_config: PoolConfig,
factory_contract: Contract,
) -> str:
Expand All @@ -353,6 +368,8 @@ def _deploy_and_initialize_hyperdrive_pool(
The amount of money to be provided by the `deploy_account` for initial pool liquidity.
initial_fixed_rate : FixedPoint
The fixed rate of the pool on initialization.
pool_contract_addr : ChecksumAddress
The address of the pool contract.
pool_config : PoolConfig
The configuration for initializing hyperdrive.
The type is generated from the Hyperdrive ABI using Pypechain.
Expand All @@ -366,10 +383,11 @@ def _deploy_and_initialize_hyperdrive_pool(
"""
fn_args = (
_dataclass_to_tuple(pool_config),
[], # new bytes[](0)
initial_liquidity.scaled_value,
initial_fixed_rate.scaled_value,
bytes(0), # new bytes(0)
[], # new bytes32[](0)
pool_contract_addr,
)
tx_receipt = smart_contract_transact(
web3, # web3
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,14 +48,9 @@ def get_web3_and_hyperdrive_contracts(
)
# set up hyperdrive contract
hyperdrive_contract: Contract = web3.eth.contract(
abi=abis["IHyperdrive"],
address=web3.to_checksum_address(contract_addresses.mock_hyperdrive),
abi=abis["IERC4626Hyperdrive"], address=web3.to_checksum_address(contract_addresses.mock_hyperdrive)
)

data_provider_contract: Contract = web3.eth.contract(
abi=abis["ERC4626DataProvider"], address=web3.to_checksum_address(contract_addresses.mock_hyperdrive)
)
yield_address = smart_contract_read(data_provider_contract, "pool")["value"]
yield_address = smart_contract_read(hyperdrive_contract, "pool")["value"]
yield_contract: Contract = web3.eth.contract(
abi=abis["MockERC4626"], address=web3.to_checksum_address(yield_address)
)
Expand Down
15 changes: 8 additions & 7 deletions lib/ethpy/ethpy/hyperdrive/transactions.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ class Checkpoint:
"""Checkpoint struct."""

share_price: FixedPoint
long_exposure: FixedPoint
exposure: FixedPoint


@dataclass
Expand All @@ -48,9 +48,12 @@ class PoolConfig:
"""PoolConfig struct."""

base_token: str
linker_factory: str
linker_code_hash: bytes
initial_share_price: FixedPoint
minimum_share_reserves: FixedPoint
minimum_transaction_amount: FixedPoint
precision_threshold: int
position_duration: int
checkpoint_duration: int
time_stretch: FixedPoint
Expand All @@ -59,8 +62,6 @@ class PoolConfig:
# TODO: Pyright:
# Declaration "fees" is obscured by a declaration of the same name here but not elsewhere
fees: Fees | Sequence # type: ignore
oracle_size: int
update_gap: int

def __post_init__(self):
if isinstance(self.fees, Sequence):
Expand Down Expand Up @@ -195,7 +196,7 @@ def convert_hyperdrive_checkpoint_types(checkpoint: dict[str, int]) -> Checkpoin
Returns
-------
Checkpoint
A dataclass containing the checkpoint share_price and long_exposure fields converted to FixedPoint.
A dataclass containing the checkpoint share_price and exposure fields converted to FixedPoint.
"""
return Checkpoint(**{camel_to_snake(key): FixedPoint(scaled_value=value) for key, value in checkpoint.items()})

Expand All @@ -217,9 +218,9 @@ def get_hyperdrive_contract(web3: Web3, abis: dict, addresses: HyperdriveAddress
Contract
The contract object returned from the query
"""
if "IHyperdrive" not in abis:
raise AssertionError("IHyperdrive ABI was not provided")
state_abi = abis["IHyperdrive"]
if "IERC4626Hyperdrive" not in abis:
raise AssertionError("IERC4626Hyperdrive ABI was not provided")
state_abi = abis["IERC4626Hyperdrive"]
# get contract instance of hyperdrive
hyperdrive_contract: Contract = web3.eth.contract(
address=address.to_checksum_address(addresses.mock_hyperdrive), abi=state_abi
Expand Down
Loading
Loading