Skip to content

Commit

Permalink
bare bones server
Browse files Browse the repository at this point in the history
- not type checking
- no utility functions
  • Loading branch information
fhenneke committed Dec 19, 2023
1 parent ff84fdc commit 227812a
Show file tree
Hide file tree
Showing 22 changed files with 243 additions and 2,322 deletions.
5 changes: 2 additions & 3 deletions .pylintrc
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
[MASTER]
disable=fixme,too-few-public-methods,too-many-instance-attributes,too-many-arguments,logging-fstring-interpolation,too-many-locals,duplicate-code, def buy_amount(self) -> Decimal:
[MAIN]

extension-pkg-allow-list=pydantic
disable=logging-fstring-interpolation
6 changes: 1 addition & 5 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,8 +1,4 @@
FROM python:3.10-alpine

RUN apk add --update gcc libc-dev linux-headers

WORKDIR /app
FROM python:3.11-alpine

# First copy over the requirements.txt and install dependencies, this makes
# building subsequent images easier.
Expand Down
2 changes: 1 addition & 1 deletion LICENSE
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
MIT License

Copyright (c) 2022 CoW Protocol
Copyright (c) 2022-2023 CoW Protocol

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
Expand Down
78 changes: 13 additions & 65 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,18 +8,18 @@ git clone [email protected]:cowprotocol/solver-template-py.git

## Install Requirements

1. Python 3.10 (or probably also 3.9)
2. Rust v1.60.0 or Docker
1. Python 3.11 or Docker (for running the solver)
2. Rust v1.60.0 or Docker (for running the autopilot and driver) REMOVE DRIVER FROM THIS TEMPLATE. THIS SHOULD BE EXPLAINED IN SOME TUTORIAL and not be part of this solver template

```sh
python3.10 -m venv venv
python3 -m venv venv
source ./venv/bin/activate
pip install -r requirements.txt
```

# Run Solver Server

```shell
```sh
python -m src._server
```

Expand All @@ -32,79 +32,27 @@ docker run -p 8000:8000 gchr.io/cowprotocol/solver-template-py
or build your own docker image with

```sh
docker build -t test-solver-image .
docker build -t solver-template-py .
```

and run it with
```sh
docker run -p 8000:8000 solver-template-py
```

# Feed an Auction Instance to the Solver

```shell
```sh
curl -X POST "http://127.0.0.1:8000/solve" \
-H "accept: application/json" \
-H "Content-Type: application/json" \
--data "@data/small_example.json"
```

# Connect to the orderbook:

Run the driver (auction dispatcher in DryRun mode). Configured to read the orderbook
from our staging environment on Gnosis Chain. These parameters can be altered
in [.env](.env)

## With Docker

If you have docker installed then you can run this.

```shell
docker run -it --rm --env-file .env --add-host host.docker.internal:host-gateway ghcr.io/cowprotocol/services solver
```

or without an env file (as described in
the [How to Write a Solver Tutorial](https://docs.cow.fi/tutorials/how-to-write-a-solver))

```shell
docker run -it --rm --add-host host.docker.internal:host-gateway ghcr.io/cowprotocol/services solver \
--orderbook-url https://barn.api.cow.fi/xdai/api \
--base-tokens 0xDDAfbb505ad214D7b80b1f830fcCc89B60fb7A83 \
--node-url "https://rpc.gnosischain.com" \
--cow-dex-ag-solver-url "http://127.0.0.1:8000" \
--solver-account 0x7942a2b3540d1ec40b2740896f87aecb2a588731 \
--solvers CowDexAg \
--transaction-strategy DryRun
```

Here we have used the orderbook-url for our staging environment on Gnosis Chain (very low traffic) so you can work with your own orders. A complete list of orderbook URLs can be found in a table at the bottom of the services repo [README](https://github.com/cowprotocol/services#solvers)

## Without Docker

Clone the services project with

```shell
git clone https://github.com/cowprotocol/services.git
```

```shell
cargo run -p solver -- \
--orderbook-url https://barn.api.cow.fi/xdai/api \
--base-tokens 0xDDAfbb505ad214D7b80b1f830fcCc89B60fb7A83 \
--node-url "https://rpc.gnosischain.com" \
--cow-dex-ag-solver-url "http://127.0.0.1:8000" \
--solver-account 0x7942a2b3540d1ec40b2740896f87aecb2a588731 \
--solvers CowDexAg \
--transaction-strategy DryRun \
--log-filter=info,solver=debug
```

# Place an order

Navigate to [barn.cowswap.exchange/](https://barn.cowswap.exchange/#/swap) and place a
tiny (real) order. See your driver pick it up and include it in the next auction being
sent to your solver
# Connect to the orderbook TBD:

# References
# References TBD

- How to Build a Solver: https://docs.cow.fi/tutorials/how-to-write-a-solver
- In Depth Solver
Specification: https://docs.cow.fi/off-chain-services/in-depth-solver-specification
- Settlement Contract (namely the settle
method): https://github.com/cowprotocol/contracts/blob/ff6fb7cad7787b8d43a6468809cacb799601a10e/src/contracts/GPv2Settlement.sol#L121-L143
- Interaction Model (Currently missing from this framework): https://github.com/cowprotocol/services/blob/cda5e36db34c55e7bf9eb4ea8b6e36ecb046f2b2/crates/shared/src/http_solver/model.rs#L125-L130
13 changes: 1 addition & 12 deletions mypy.ini
Original file line number Diff line number Diff line change
@@ -1,13 +1,2 @@
[mypy]
python_version = 3.10


[mypy-src.*]
allow_untyped_calls = True
allow_any_generics = True

[mypy-uvicorn.*]
ignore_missing_imports = True

[mypy-fastapi]
implicit_reexport = True
python_version = 3.11
51 changes: 42 additions & 9 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -1,9 +1,42 @@
black==22.3.0
fastapi==0.65.2
uvicorn==0.17.6
pylint==2.13.5
python-dotenv==0.20.0
pydantic==1.9.0
pytest==7.1.1
makefun==1.13.1
mypy==0.942
aiohttp==3.9.1
aiosignal==1.3.1
annotated-types==0.6.0
anyio==3.7.1
astroid==3.0.2
attrs==23.1.0
black==23.12.0
certifi==2023.11.17
click==8.1.7
dill==0.3.7
fastapi==0.105.0
frozenlist==1.4.1
h11==0.14.0
httpcore==1.0.2
httptools==0.6.1
httpx==0.25.2
idna==3.6
iniconfig==2.0.0
isort==5.13.2
mccabe==0.7.0
multidict==6.0.4
mypy==1.7.1
mypy-extensions==1.0.0
packaging==23.2
pathspec==0.12.1
platformdirs==4.1.0
pluggy==1.3.0
pydantic==2.5.2
pydantic_core==2.14.5
pylint==3.0.3
pytest==7.4.3
python-dotenv==1.0.0
PyYAML==6.0.1
sniffio==1.3.0
starlette==0.27.0
tomlkit==0.12.3
typing_extensions==4.9.0
uvicorn==0.24.0.post1
uvloop==0.19.0
watchfiles==0.21.0
websockets==12.0
yarl==1.9.4
114 changes: 25 additions & 89 deletions src/_server.py
Original file line number Diff line number Diff line change
@@ -1,124 +1,60 @@
"""
This is the project's Entry point.
"""
from __future__ import annotations

import argparse
import decimal
import logging

from typing import Any
import uvicorn
from dotenv import load_dotenv
from fastapi import FastAPI, Request
from fastapi.middleware.gzip import GZipMiddleware
from pydantic import BaseSettings

from src.models.batch_auction import BatchAuction
from src.models.solver_args import SolverArgs
from src.util.schema import (
BatchAuctionModel,
SettledBatchAuctionModel,
)

# Set decimal precision.
decimal.getcontext().prec = 100

# Holds parameters passed on the command line when invoking the server.
# These will be merged with request solver parameters
SERVER_ARGS = None

from fastapi import FastAPI

# ++++ Interface definition ++++
# from src.models.solve_model import Auction, Solution


# Server settings: Can be overridden by passing them as env vars or in a .env file.
# Example: PORT=8001 python -m src._server
class ServerSettings(BaseSettings):
"""Basic Server Settings"""

host: str = "0.0.0.0"
port: int = 8000


server_settings = ServerSettings()
logging.basicConfig(level=logging.DEBUG)

# ++++ Endpoints: ++++


app = FastAPI(title="Batch auction solver")
app.add_middleware(GZipMiddleware)


@app.get("/health", status_code=200)
def health() -> bool:
"""Convenience endpoint to check if server is alive."""
return True


@app.post("/notify", response_model=bool)
async def notify(request: Request) -> bool:
async def notify(notification: dict[str, Any]) -> bool:
"""Print response from notify endpoint."""
print(f"Notify request {await request.json()}")
logging.debug(f"Notification: {notification}")
return True


@app.post("/solve", response_model=SettledBatchAuctionModel)
async def solve(problem: BatchAuctionModel, request: Request): # type: ignore
# @app.post("/solve", response_model=Solution)
# async def solve(auction: Auction, request: Request): # type: ignore
@app.post("/solve")
async def solve(auction: dict[str, Any]) -> dict[str, Any]:
"""API POST solve endpoint handler"""
logging.debug(f"Received solve request {await request.json()}")
solver_args = SolverArgs.from_request(request=request, meta=problem.metadata)

batch = BatchAuction.from_dict(problem.dict(), solver_args.instance_name)

print("Received Batch Auction", batch.name)
print("Parameters Supplied", solver_args)
logging.debug(f"Received solve request: {auction}")

# 1. Solve BatchAuction: update batch_auction with
# batch.solve()
# 1. Solve Auction
# (add code)

trivial_solution = {
"orders": {},
"foreign_liquidity_orders": [],
"amms": {},
solution = {
"id": "123",
"trades": [],
"prices": {},
"approvals": [],
"interaction_data": [],
"interactions": [],
"solver": "solvertemplate",
"score": "0",
"weth": "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2",
}

print("\n\n*************\n\nReturning solution: " + str(trivial_solution))
return trivial_solution
logging.debug(f"Returning solution: {solution}")

# return Solution(**solution)
return solution


# ++++ Server setup: ++++


if __name__ == "__main__":
load_dotenv()

parser = argparse.ArgumentParser(
fromfile_prefix_chars="@",
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
)
# TODO - enable flag to write files to persistent storage
# parser.add_argument(
# "--write_auxiliary_files",
# type=bool,
# default=False,
# help="Write auxiliary instance and optimization files, or not.",
# )

parser.add_argument(
"--log-level",
type=str,
default="info",
help="Log level",
)

SERVER_ARGS = parser.parse_args()
uvicorn.run(
"__main__:app",
host=server_settings.host,
port=server_settings.port,
log_level=SERVER_ARGS.log_level,
host="0.0.0.0",
port=8000,
)
Loading

0 comments on commit 227812a

Please sign in to comment.