Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
6 changes: 3 additions & 3 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -44,11 +44,11 @@ tests:
poetry run pytest tests -vv --reruns 3 --reruns-delay 3

fmt:
poetry run black tests derive_client examples
poetry run isort tests derive_client examples
poetry run ruff format tests derive_client examples
poetry run ruff check tests derive_client examples --fix

lint:
poetry run flake8 tests derive_client examples
poetry run ruff check tests derive_client examples

all: fmt lint tests

Expand Down
23 changes: 0 additions & 23 deletions derive_client/_bridge/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,6 @@ class BridgeClient:
"""

def __init__(self, env: Environment, account: Account, wallet: Address, logger: Logger):

if not env == Environment.PROD:
raise RuntimeError(f"Bridging is not supported in the {env.name} environment.")

Expand Down Expand Up @@ -175,7 +174,6 @@ async def verify_owner(self):
)

def get_deposit_helper(self, chain_id: ChainID) -> AsyncContract:

match chain_id:
case ChainID.ARBITRUM:
address = ARBITRUM_DEPOSIT_WRAPPER
Expand Down Expand Up @@ -204,7 +202,6 @@ def _make_bridge_context(
currency: Currency,
remote_chain_id: ChainID,
) -> BridgeContext:

is_deposit = direction == Direction.DEPOSIT

if is_deposit:
Expand Down Expand Up @@ -246,7 +243,6 @@ def _make_bridge_context(
return context

def _get_context(self, state: PreparedBridgeTx | BridgeTxResult) -> BridgeContext:

direction = Direction.WITHDRAW if state.source_chain == ChainID.DERIVE else Direction.DEPOSIT
remote_chain_id = state.target_chain if direction == Direction.WITHDRAW else state.source_chain
context = self._make_bridge_context(
Expand All @@ -261,7 +257,6 @@ def _resolve_socket_route(
self,
context: BridgeContext,
) -> tuple[MintableTokenData | NonMintableTokenData, Address]:

currency = context.currency
src_chain, tgt_chain = context.source_chain, context.target_chain

Expand Down Expand Up @@ -289,7 +284,6 @@ async def _prepare_tx(
fee_in_token: int,
context: BridgeContext,
) -> PreparedBridgeTx:

onchain_decimals: int = await context.source_token.functions.decimals().call()
if onchain_decimals != (expected_decimals := CURRENCY_DECIMALS[context.currency]):
raise RuntimeError(
Expand Down Expand Up @@ -330,7 +324,6 @@ async def prepare_deposit(
currency: Currency,
chain_id: ChainID,
) -> IOResult[PreparedBridgeTx, Exception]:

if currency is Currency.ETH:
raise NotImplementedError("ETH deposits are not implemented.")

Expand All @@ -355,7 +348,6 @@ async def prepare_withdrawal(
currency: Currency,
chain_id: ChainID,
) -> IOResult[PreparedBridgeTx, Exception]:

if currency is Currency.ETH:
raise NotImplementedError("ETH withdrawals are not implemented.")

Expand All @@ -375,14 +367,12 @@ async def prepare_withdrawal(

@future_safe
async def submit_bridge_tx(self, prepared_tx: PreparedBridgeTx) -> IOResult[BridgeTxResult, Exception]:

tx_result = await self._send_bridge_tx(prepared_tx=prepared_tx)

return tx_result

@future_safe
async def poll_bridge_progress(self, tx_result: BridgeTxResult) -> IOResult[BridgeTxResult, Exception]:

try:
tx_result.source_tx.tx_receipt = await self._confirm_source_tx(tx_result=tx_result)
tx_result.target_tx = TxResult(tx_hash=await self._wait_for_target_event(tx_result=tx_result))
Expand All @@ -393,7 +383,6 @@ async def poll_bridge_progress(self, tx_result: BridgeTxResult) -> IOResult[Brid
return tx_result

async def _prepare_socket_deposit(self, amount: int, context: BridgeContext) -> PreparedBridgeTx:

token_data, _connector = self._resolve_socket_route(context=context)

spender = token_data.Vault if token_data.isNewBridge else self.get_deposit_helper(context.source_chain).address
Expand All @@ -419,7 +408,6 @@ async def _prepare_socket_deposit(self, amount: int, context: BridgeContext) ->
return prepared_tx

async def _prepare_socket_withdrawal(self, amount: int, context: BridgeContext) -> PreparedBridgeTx:

token_data, connector = self._resolve_socket_route(context=context)

# Get estimated fee in token for a withdrawal
Expand Down Expand Up @@ -461,7 +449,6 @@ async def _prepare_socket_withdrawal(self, amount: int, context: BridgeContext)
return prepared_tx

async def _prepare_layerzero_deposit(self, amount: int, context: BridgeContext) -> PreparedBridgeTx:

# check allowance, if needed approve
await ensure_token_balance(context.source_token, self.owner, amount=amount)
await ensure_token_allowance(
Expand Down Expand Up @@ -505,7 +492,6 @@ async def _prepare_layerzero_deposit(self, amount: int, context: BridgeContext)
return prepared_tx

async def _prepare_layerzero_withdrawal(self, amount: int, context: BridgeContext) -> PreparedBridgeTx:

abi = json.loads(LYRA_OFT_WITHDRAW_WRAPPER_ABI_PATH.read_text())
withdraw_wrapper = get_contract(context.source_w3, LYRA_OFT_WITHDRAW_WRAPPER_ADDRESS, abi=abi)
destEID = LayerZeroChainIDv2[context.target_chain.name]
Expand Down Expand Up @@ -542,7 +528,6 @@ async def _prepare_layerzero_withdrawal(self, amount: int, context: BridgeContex
return prepared_tx

async def _send_bridge_tx(self, prepared_tx: PreparedBridgeTx) -> BridgeTxResult:

context = self._get_context(prepared_tx)

# record on target chain where we should start polling
Expand All @@ -561,7 +546,6 @@ async def _send_bridge_tx(self, prepared_tx: PreparedBridgeTx) -> BridgeTxResult
return tx_result

async def _confirm_source_tx(self, tx_result: BridgeTxResult) -> TxReceipt:

context = self._get_context(tx_result)
msg = "⏳ Checking source chain [%s] tx receipt for %s"
self.logger.info(msg, tx_result.source_chain.name, tx_result.source_tx.tx_hash)
Expand All @@ -574,7 +558,6 @@ async def _confirm_source_tx(self, tx_result: BridgeTxResult) -> TxReceipt:
return tx_receipt

async def _wait_for_target_event(self, tx_result: BridgeTxResult) -> HexBytes:

bridge_event_fetchers = {
BridgeType.SOCKET: self._fetch_socket_event_log,
BridgeType.LAYERZERO: self._fetch_lz_event_log,
Expand All @@ -590,7 +573,6 @@ async def _wait_for_target_event(self, tx_result: BridgeTxResult) -> HexBytes:
return tx_hash

async def _confirm_target_tx(self, tx_result: BridgeTxResult) -> TxReceipt:

context = self._get_context(tx_result)
msg = "⏳ Checking target chain [%s] tx receipt for %s"
self.logger.info(msg, tx_result.target_chain.name, tx_result.target_tx.tx_hash)
Expand All @@ -603,7 +585,6 @@ async def _confirm_target_tx(self, tx_result: BridgeTxResult) -> TxReceipt:
return tx_receipt

async def _fetch_lz_event_log(self, tx_result: BridgeTxResult, context: BridgeContext) -> LogReceipt:

try:
source_event = context.source_event.process_log(tx_result.source_tx.tx_receipt.logs[-1])
guid = source_event["args"]["guid"]
Expand All @@ -630,7 +611,6 @@ async def _fetch_lz_event_log(self, tx_result: BridgeTxResult, context: BridgeCo
)

async def _fetch_socket_event_log(self, tx_result: BridgeTxResult, context: BridgeContext) -> LogReceipt:

try:
source_event = context.source_event.process_log(tx_result.source_tx.tx_receipt.logs[-2])
message_id = source_event["args"]["msgId"]
Expand Down Expand Up @@ -664,7 +644,6 @@ def _prepare_new_style_deposit(
amount: int,
context: BridgeContext,
) -> tuple[AsyncContractFunction, int]:

vault_contract = _load_vault_contract(w3=self.w3s[context.source_chain], token_data=token_data)
connector = token_data.connectors[ChainID.DERIVE][TARGET_SPEED]
fees_func = _get_min_fees(bridge_contract=vault_contract, connector=connector, token_data=token_data)
Expand All @@ -685,7 +664,6 @@ def _prepare_old_style_deposit(
amount: int,
context: BridgeContext,
) -> tuple[AsyncContractFunction, int]:

vault_contract = _load_vault_contract(w3=self.w3s[context.source_chain], token_data=token_data)
connector = token_data.connectors[ChainID.DERIVE][TARGET_SPEED]
fees_func = _get_min_fees(bridge_contract=vault_contract, connector=connector, token_data=token_data)
Expand All @@ -701,7 +679,6 @@ def _prepare_old_style_deposit(
return func, fees_func

async def _check_bridge_funds(self, token_data, connector: Address, amount: int) -> None:

controller = _load_controller_contract(w3=self.derive_w3, token_data=token_data)
if token_data.isNewBridge:
deposit_hook = await controller.functions.hook__().call()
Expand Down
15 changes: 3 additions & 12 deletions derive_client/_bridge/standard_bridge.py
Original file line number Diff line number Diff line change
Expand Up @@ -76,9 +76,7 @@ def _load_l2_cross_domain_messenger_proxy(w3: AsyncWeb3) -> AsyncContract:


class StandardBridge:

def __init__(self, account: Account, logger: Logger):

self.account = account
self.logger = logger
self.w3s = get_w3_connections(logger=logger)
Expand All @@ -95,7 +93,6 @@ async def prepare_eth_tx(
source_chain: ChainID,
target_chain: ChainID,
) -> IOResult[PreparedBridgeTx, Exception]:

currency = Currency.ETH

if source_chain is not ChainID.ETH or target_chain is not ChainID.DERIVE or to != self.account.address:
Expand All @@ -118,14 +115,12 @@ def private_key(self) -> str:

@future_safe
async def submit_bridge_tx(self, prepared_tx: PreparedBridgeTx) -> IOResult[BridgeTxResult, Exception]:

tx_result = await self._send_bridge_tx(prepared_tx=prepared_tx)

return tx_result

@future_safe
async def poll_bridge_progress(self, tx_result: BridgeTxResult) -> IOResult[BridgeTxResult, Exception]:

try:
tx_result.source_tx.tx_receipt = await self._confirm_source_tx(tx_result=tx_result)
tx_result.target_tx = TxResult(tx_hash=await self._wait_for_target_event(tx_result=tx_result))
Expand All @@ -142,7 +137,6 @@ async def _prepare_eth_tx(
source_chain: ChainID,
target_chain: ChainID,
) -> PreparedBridgeTx:

w3 = self.w3s[source_chain]

proxy_contract = self.l1_contract
Expand All @@ -156,7 +150,9 @@ async def _prepare_eth_tx(

tx_gas_cost = tx["gas"] * tx["maxFeePerGas"]
if value < tx_gas_cost:
msg = f"⚠️ Bridge tx value {value} is smaller than gas cost {tx_gas_cost} (~{tx_gas_cost/value:.2f}x value)"
msg = (
f"⚠️ Bridge tx value {value} is smaller than gas cost {tx_gas_cost} (~{tx_gas_cost / value:.2f}x value)"
)
self.logger.warning(msg)

signed_tx = sign_tx(w3=w3, tx=tx, private_key=self.private_key)
Expand Down Expand Up @@ -184,7 +180,6 @@ async def _prepare_eth_tx(
return prepared_tx

async def _send_bridge_tx(self, prepared_tx: PreparedBridgeTx) -> BridgeTxResult:

source_w3 = self.w3s[prepared_tx.source_chain]
target_w3 = self.w3s[prepared_tx.target_chain]

Expand All @@ -204,7 +199,6 @@ async def _send_bridge_tx(self, prepared_tx: PreparedBridgeTx) -> BridgeTxResult
return tx_result

async def _confirm_source_tx(self, tx_result: BridgeTxResult) -> TxReceipt:

msg = "⏳ Checking source chain [%s] tx receipt for %s"
self.logger.info(msg, tx_result.source_chain.name, tx_result.source_tx.tx_hash)

Expand All @@ -218,15 +212,13 @@ async def _confirm_source_tx(self, tx_result: BridgeTxResult) -> TxReceipt:
return tx_receipt

async def _wait_for_target_event(self, tx_result: BridgeTxResult) -> HexBytes:

event_log = await self._fetch_standard_event_log(tx_result)
tx_hash = event_log["transactionHash"]
self.logger.info(f"Target event tx_hash found: {tx_hash.to_0x_hex()}")

return tx_hash

async def _confirm_target_tx(self, tx_result: BridgeTxResult) -> TxReceipt:

msg = "⏳ Checking target chain [%s] tx receipt for %s"
self.logger.info(msg, tx_result.target_chain.name, tx_result.target_tx.tx_hash)

Expand All @@ -240,7 +232,6 @@ async def _confirm_target_tx(self, tx_result: BridgeTxResult) -> TxReceipt:
return tx_receipt

async def _fetch_standard_event_log(self, tx_result: BridgeTxResult) -> LogReceipt:

source_event = self.l1_messenger_proxy.events.SentMessage()

target_w3 = self.w3s[tx_result.target_chain]
Expand Down
2 changes: 0 additions & 2 deletions derive_client/_bridge/w3.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,6 @@ def make_rotating_provider_middleware(

async def middleware_factory(make_request: Callable[[str, Any], Any], w3: AsyncWeb3) -> Callable[[str, Any], Any]:
async def rotating_backoff(method: str, params: Any) -> Any:

now = time.monotonic()

while True:
Expand Down Expand Up @@ -146,7 +145,6 @@ def get_w3_connection(
rpc_endpoints: RPCEndpoints | None = None,
logger: Logger | None = None,
) -> AsyncWeb3:

rpc_endpoints = rpc_endpoints or load_rpc_endpoints(DEFAULT_RPC_ENDPOINTS)
providers = [AsyncHTTPProvider(str(url)) for url in rpc_endpoints[chain_id]]

Expand Down
1 change: 0 additions & 1 deletion derive_client/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,6 @@ def fmt_sig_up_to(x: float, sig: int = 4) -> str:


def rich_prepared_tx(prepared_tx: PreparedBridgeTx):

table = Table(title="Prepared Bridge Transaction", show_header=False, box=None)
if prepared_tx.amount > 0:
human_amount = from_base_units(amount=prepared_tx.amount, currency=prepared_tx.currency)
Expand Down
18 changes: 2 additions & 16 deletions derive_client/data/rpc_endpoints.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,14 @@ OPTIMISM:
- https://optimism.api.onfinality.io/public

BASE:
# - https://base.drpc.org
- https://base.drpc.org
- https://base.llamarpc.com
- https://mainnet.base.org
- https://base-rpc.publicnode.com
- https://0xrpc.io/base
- https://base.therpc.io
- https://base-pokt.nodies.app
- https://base.api.onfinality.io/public
# - https://base.api.onfinality.io/public

ARBITRUM:
- https://arbitrum.drpc.org
Expand All @@ -40,17 +40,3 @@ ARBITRUM:
DERIVE:
- https://rpc.derive.xyz/
- https://957.rpc.thirdweb.com/

MODE:
- https://mainnet.mode.network/
- https://mode.drpc.org
- https://mode.gateway.tenderly.co/
- https://mode.rpc.thirdweb.com/

BLAST:
- https://blast.drpc.org
- https://blast.therpc.io
- https://rpc.blast.io/
- https://blast-rpc.publicnode.com/
- https://blast.gateway.tenderly.co/
- https://blast-public.nodies.app
2 changes: 0 additions & 2 deletions derive_client/data_types/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,6 @@


class PAttributeDict(AttributeDict):

@classmethod
def __get_pydantic_core_schema__(cls, _source, _handler: GetCoreSchemaHandler) -> core_schema.CoreSchema:
return core_schema.no_info_plain_validator_function(lambda v, **kwargs: cls._validate(v))
Expand Down Expand Up @@ -306,7 +305,6 @@ class PreparedBridgeTx:
fee_in_token: int

def __post_init_post_parse__(self) -> None:

# rule 1: don't allow both amount (erc20) and value (native) to be non-zero
if self.amount and self.value:
raise ValueError(
Expand Down
Loading