Skip to content

Commit

Permalink
add UUID and some cleanup
Browse files Browse the repository at this point in the history
  • Loading branch information
ani authored and ani committed Mar 7, 2024
1 parent 20bdd30 commit d62498d
Show file tree
Hide file tree
Showing 2 changed files with 25 additions and 30 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
import json
import urllib.parse
from typing import Callable
from uuid import UUID

import httpx
import web3
Expand All @@ -18,7 +19,7 @@


class BidInfo:
def __init__(self, opportunity_id: str, opportunity_bid: OpportunityBid):
def __init__(self, opportunity_id: UUID, opportunity_bid: OpportunityBid):
self.opportunity_id = opportunity_id
self.opportunity_bid = opportunity_bid

Expand Down Expand Up @@ -56,6 +57,7 @@ async def start_ws(
Initializes the websocket connection to the server, if not already connected.
Args:
opportunity_callback: An async function that serves as the callback on a new liquidation opportunity. Should take in one external argument of type OpportunityParamsWithMetadata.
kwargs: Keyword arguments to pass to the websocket connection.
Returns:
The websocket task.
Expand Down Expand Up @@ -126,14 +128,14 @@ async def send_ws_message(self, msg: dict):
self.ws_msg_counter += 1

future = asyncio.get_event_loop().create_future()
self.ws_msg_futures[msg["id"]] = future

await self.ws.send(json.dumps(msg))
self.ws_msg_futures[msg["id"]] = future

# await the response msg for the subscription from the server
# await the response for the subscription from the server
msg = await future

self.process_msg(msg)
self.process_response_msg(msg)

async def subscribe_chains(self, chain_ids: list[str]):
"""
Expand Down Expand Up @@ -165,16 +167,16 @@ async def unsubscribe_chains(self, chain_ids: list[str]):
}
await self.send_ws_message(json_unsubscribe)

def process_msg(self, msg: dict):
def process_response_msg(self, msg: dict):
"""
Processes a message received from the server via websocket.
Processes a response message received from the server via websocket.
Args:
msg: The message to process.
"""
if msg.get("status") and msg.get("status") != "success":
raise ExpressRelayClientException(
f"Error in websocket subscription: {msg.get('result')}"
f"Error in websocket with message id {msg.get('id')}: {msg.get('result')}"
)

async def ws_handler(
Expand All @@ -197,19 +199,18 @@ async def ws_handler(
if msg.get("id"):
future = self.ws_msg_futures.pop(msg["id"])
future.set_result(msg)
else:
self.process_msg(msg)

if msg.get("type") == "new_opportunity":
opportunity = msg["opportunity"]
opportunity = OpportunityParamsWithMetadata.from_dict(opportunity)
opportunity = OpportunityParamsWithMetadata.from_dict(
msg["opportunity"]
)

if opportunity_callback is not None:
asyncio.create_task(opportunity_callback(opportunity))

async def submit_opportunity(
self, opportunity: OpportunityParams, timeout: int = 10
) -> str:
) -> UUID:
"""
Submits an opportunity to the liquidation server.
Expand All @@ -228,9 +229,9 @@ async def submit_opportunity(
timeout=timeout,
)
resp.raise_for_status()
return resp.json()["opportunity_id"]
return UUID(resp.json()["opportunity_id"])

async def submit_bid(self, bid_info: BidInfo, timeout: int = 10) -> str:
async def submit_bid(self, bid_info: BidInfo, timeout: int = 10) -> UUID:
"""
Submits a bid to the liquidation server.
Expand All @@ -244,14 +245,14 @@ async def submit_bid(self, bid_info: BidInfo, timeout: int = 10) -> str:
resp = await client.post(
urllib.parse.urlparse(self.server_url)
._replace(
path=f"/v1/liquidation/opportunities/{bid_info.opportunity_id}/bids"
path=f"/v1/liquidation/opportunities/{str(bid_info.opportunity_id)}/bids"
)
.geturl(),
json=bid_info.opportunity_bid.to_dict(),
timeout=timeout,
)
resp.raise_for_status()
return resp.json()["id"]
return UUID(resp.json()["id"])


def sign_bid(
Expand Down Expand Up @@ -314,7 +315,7 @@ def sign_bid(
opportunity_bid = OpportunityBid.from_dict(opportunity_bid)

bid_info = BidInfo(
opportunity_id=liquidation_opportunity.opportunity_id,
opportunity_id=UUID(liquidation_opportunity.opportunity_id),
opportunity_bid=opportunity_bid,
)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,16 +10,16 @@

logger = logging.getLogger(__name__)

NAIVE_BID = 10
# Set validity (naively) to max uint256
VALID_UNTIL_MAX = 2**256 - 1


class SimpleSearcher:
def __init__(self, server_url: str, private_key: str, default_bid: int):
def __init__(self, server_url: str, private_key: str):
self.client = ExpressRelayClient(server_url)
self.private_key = private_key
self.liquidator = Account.from_key(private_key).address
self.default_bid = default_bid

def assess_liquidation_opportunity(
self,
Expand All @@ -31,13 +31,13 @@ def assess_liquidation_opportunity(
This function determines whether the given opportunity deals with the specified repay and receipt tokens that the searcher wishes to transact in and whether it is profitable to execute the liquidation.
There are many ways to evaluate this, but the most common way is to check that the value of the amount the searcher will receive from the liquidation exceeds the value of the amount repaid.
Individual searchers will have their own methods to determine market impact and the profitability of conducting a liquidation. This function can be expanded to include external prices to perform this evaluation.
In this simple searcher, the function always (naively) returns a BidInfo object with the default bid and a valid_until timestamp.
In this simple searcher, the function always (naively) returns a BidInfo object with a default bid and valid_until timestamp.
Args:
opp: A OpportunityParamsWithMetadata object, representing a single liquidation opportunity.
Returns:
If the opportunity is deemed worthwhile, this function can return a BidInfo object, whose contents can be submitted to the auction server. If the opportunity is not deemed worthwhile, this function can return None.
"""
bid_info = sign_bid(opp, self.default_bid, VALID_UNTIL_MAX, self.private_key)
bid_info = sign_bid(opp, NAIVE_BID, VALID_UNTIL_MAX, self.private_key)

return bid_info

Expand All @@ -53,11 +53,11 @@ async def opportunity_callback(self, opp: OpportunityParamsWithMetadata):
try:
await self.client.submit_bid(bid_info)
logger.info(
f"Submitted bid amount {bid_info.opportunity_bid.amount} for opportunity {bid_info.opportunity_id}"
f"Submitted bid amount {bid_info.opportunity_bid.amount} for opportunity {str(bid_info.opportunity_id)}"
)
except Exception as e:
logger.error(
f"Error submitting bid amount {bid_info.opportunity_bid.amount} for opportunity {bid_info.opportunity_id}: {e}"
f"Error submitting bid amount {bid_info.opportunity_bid.amount} for opportunity {str(bid_info.opportunity_id)}: {e}"
)


Expand All @@ -77,12 +77,6 @@ async def main():
nargs="+",
help="Chain ID(s) of the network(s) to monitor for liquidation opportunities",
)
parser.add_argument(
"--bid",
type=int,
default=10,
help="Default amount of bid for liquidation opportunities",
)
parser.add_argument(
"--server-url",
type=str,
Expand All @@ -102,7 +96,7 @@ async def main():

sk_liquidator = args.private_key

simple_searcher = SimpleSearcher(args.server_url, sk_liquidator, args.bid)
simple_searcher = SimpleSearcher(args.server_url, sk_liquidator)
logger.info("Liquidator address: %s", simple_searcher.liquidator)

task = await simple_searcher.client.start_ws(simple_searcher.opportunity_callback)
Expand Down

0 comments on commit d62498d

Please sign in to comment.