Skip to content

Commit 7eb5081

Browse files
authored
feat: final submission (#16)
1 parent 9739186 commit 7eb5081

22 files changed

+1278
-95
lines changed

.env.example

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
# -------------------------------
2+
# Bot variables
3+
# -------------------------------
4+
# ANTHROPIC_API_KEY: Your Anthropic API key used by the PydanticAI agent.
5+
export ANTHROPIC_API_KEY=
6+
7+
# ENCOURAGE_TRADE: When set (e.g., python booleans, True), it injects a system prompt to encourage the agent to trade during its next decision period.
8+
export ENCOURAGE_TRADE=
9+
10+
# START_BLOCK: The block number at which the bot catches up on CoW Swap trades from upon first startup.
11+
export START_BLOCK=
12+
13+
# -------------------------------
14+
# Web3 variables
15+
# -------------------------------
16+
# PRIVATE_KEY: The agent's private key for signing transactions.
17+
export PRIVATE_KEY=
18+
19+
# WEB3_ALCHEMY_PROJECT_ID: Your Alchemy project ID used as the default provider by bot.py with the ape-alchemy plugin.
20+
export WEB3_ALCHEMY_PROJECT_ID=
21+
22+
# GNOSISSCAN_API_KEY: API key for Gnosisscan, which lets bot.py automatically fetch and cache ABIs for contracts with ape-etherscan plugin.
23+
export GNOSISSCAN_API_KEY=
24+
25+
# CHAIN_ID: The chain ID required by smart-contract-infra for deploying and configuring agent-safe components.
26+
export CHAIN_ID=
27+
28+
# SAFE_ADDRESS: (Optional) The Safe address to interact with. Set this if you already have a deployed Safe.
29+
export SAFE_ADDRESS=
30+
31+
# TRADING_MODULE_ADDRESS: (Optional) The deployed trading module proxy address. Set this if available.
32+
export TRADING_MODULE_ADDRESS=
33+
34+

LICENSE.md

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
MIT License
2+
3+
Copyright (c) 2025 Lumoswiz
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in all
13+
copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
SOFTWARE.

README.md

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
# Moozilla x Kong [![Open in Gitpod][gitpod-badge]][gitpod] [![CI Status][ci-badge]][ci] [![Built with Silverback][silverback-badge]][silverback] [![License: MIT][license-badge]][license]
2+
3+
[gitpod]: https://gitpod.io/#https://github.com/lumoswiz/cow-agent
4+
[gitpod-badge]: https://img.shields.io/badge/Gitpod-Open%20in%20Gitpod-FFB45B?logo=gitpod
5+
[ci]: https://github.com/lumoswiz/cow-agent/actions
6+
[ci-badge]: https://github.com/lumoswiz/cow-agent/actions/workflows/ci.yml/badge.svg
7+
[silverback]: https://github.com/ApeWorX/silverback
8+
[silverback-badge]: https://img.shields.io/badge/Built%20with-Silverback-blue?style=flat-square
9+
[license]: https://opensource.org/licenses/MIT
10+
[license-badge]: https://img.shields.io/badge/License-MIT-yellow.svg
11+
12+
Welcome to **Moozilla x Kong**
13+
Your project description goes here...
14+
15+
## Development Prerequisites
16+
17+
- **[Bun](https://bun.sh)** `v1.1.31`
18+
- **[uv](https://docs.astral.sh/uv/getting-started/)** `v0.5.26`
19+
20+
## Monorepo Contents
21+
22+
This repository is organized as a **monorepo** and contains multiple related projects:
23+
24+
- **CoW Trader:** A Silverback CoW Swap trading bot that uses PydanticAI as the agent framework.
25+
- **Smart Contract Infra:** Provides the smart contract development,deployment and configuration scripts for:
26+
- The token allowlist governance mechanism.
27+
- Setting up a new Safe or bringing your own.
28+
- Deploying a trading module proxy enabled on the Safe.
29+
- Configuring the module on the Safe with the guard and enable the agent to trade via the module.
30+
31+
Each project is maintained in its own subdirectory and may have additional project-specific configuration and dependencies. However, they all share a centralized `.env` file located at the repository root.
32+
33+
## Project Specific Configuration
34+
35+
While a centralized `.env` file is used for common environment variables, each project in this monorepo may have additional settings and build steps:
36+
37+
- **CoW Trader:**
38+
This project might require extra, project-specific configuration. Please refer to the [CoW Trader README](./path/to/cow-trader/README.md) for full setup instructions.
39+
40+
- **Smart Contract Infra:**
41+
Building and deploying the contract components is handled separately. Detailed instructions are available in the [Smart Contract Infra README](./path/to/smart-contract-infra/README.md).
42+
43+
### Environment Variables
44+
45+
Each variable is explained in the `.env.example` file. Be sure to copy `.env.example` to `.env` and fill in the required values:
46+
47+
- **PydanticAI Variables:**
48+
49+
- `ANTHROPIC_API_KEY`
50+
- `ENCOURAGE_TRADE`
51+
52+
- **Bot Variables:**
53+
54+
- `START_BLOCK`
55+
56+
- **Web3 Variables:**
57+
- `PRIVATE_KEY`
58+
- `WEB3_ALCHEMY_PROJECT_ID`
59+
- `GNOSISSCAN_API_KEY`
60+
- `CHAIN_ID`
61+
- `SAFE_ADDRESS` (optional)
62+
- `TRADING_MODULE_ADDRESS` (optional)
63+
64+
This setup allows you to configure both individual projects while sharing common environment variables.
65+
66+
## License
67+
68+
This project is licensed under the MIT License. See the [LICENSE](LICENSE) file for details.

cow-trader/README.md

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,12 @@ Create venv and install dependencies:
1212
uv sync --all-extras --dev
1313
```
1414

15+
Venv:
16+
17+
```bash
18+
source .venv/bin/activate
19+
```
20+
1521
Install all Ape framework plugins:
1622

1723
```bash
@@ -20,6 +26,58 @@ ape plugins install .
2026

2127
## Run Silverback Bot
2228

29+
Before running the bot, ensure you've set up an account alias as outlined in the [Ape Accounts documentation](https://docs.apeworx.io/ape/stable/userguides/accounts.html).
30+
31+
Once your account alias is ready (e.g. `cow-agent`), you can run the bot:
32+
2333
```bash
2434
silverback run --network gnosis:mainnet:alchemy --account cow-agent
2535
```
36+
37+
This command uses the alias you configured as the signer. There will be a prompt asking if you want to enable auto-signing.
38+
39+
### Bot Overview
40+
41+
The bot continuously monitors new blocks and manages trading through two primary block handlers:
42+
43+
- **update_state:**
44+
45+
- Processes each new block to fetch and update CoW Swap trade data.
46+
- Tracks key events (e.g. recent trades, block numbers) and updates local storage (e.g. `trades.csv`, `block.csv`, `orders.csv`, `reasoning.csv`, `decisions.csv`).
47+
- Controls whether trading is enabled based on a cooldown and past decisions.
48+
49+
- **make_trading_decision:**
50+
- When permitted, it builds a **TradeContext** from recent trade events and past decisions.
51+
- Provides this context to an AI agent (with tools like token naming, token type, and eligible buy tokens) along with a system prompt (stored in `system_prompt.txt`).
52+
- The agent returns a decision on whether to trade and which token to buy.
53+
- If a trade is executed, the bot builds a CoW Swap order (via a quote → order payload → submit → pre-sign sequence using the TradingModule).
54+
- A trading cooldown is applied after executing a trade.
55+
56+
Other key components include:
57+
58+
- **Agent Architecture:**
59+
60+
- The agent receives an aggregated **TradeContext** via **AgentDependencies** and produces an **AgentResponse** that's converted to an **AgentDecision**.
61+
- Past decisions (and their outcomes) are fed back to refine future trading decisions.
62+
63+
- **Contract Address Configuration:**
64+
- The `TOKEN_ALLOWLIST_ADDRESS` is loaded from [deployments](../smart-contract-infra/deployments/contracts.json) (for chain ID 100).
65+
- The `SAFE_ADDRESS` and `TRADING_MODULE_ADDRESS` are taken from environment variables if set; otherwise, they default to the values in deployments.
66+
67+
- **Local Storage Helpers:**
68+
69+
- Utility functions load and persist state data (trades, orders, decisions, and processed blocks) in CSV files.
70+
71+
- **CoW Swap Trading Functions:**
72+
73+
- Dedicated functions handle constructing, submitting, and signing orders through the CoW Swap orderbook API and TradingModule.
74+
75+
- **Initialization:**
76+
- On startup (`bot_startup`), the bot loads persistent state, catches up on historical trades, and optionally enables auto-signing.
77+
- During worker initialization (`worker_startup`), each worker (both block handlers) gets access to shared state—including the trading agent instance, historical trades, and past decisions.
78+
79+
This design ensures the bot continuously tracks market activity, leverages an AI agent for dynamic decision-making, and safely executes trades on CoW Swap.
80+
81+
## Acknowledgements
82+
83+
- [Marginal Protocol](https://github.com/MarginalProtocol/v1-liquidator-bot)

cow-trader/ape-config.yaml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,3 +9,7 @@ plugins:
99
version: 0.8.7
1010
- name: solidity
1111
version: 0.8.5
12+
13+
gnosis:
14+
mainnet:
15+
transaction_acceptance_timeout: 120 # 2 minutes

cow-trader/bot.py

Lines changed: 26 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -32,19 +32,41 @@
3232
DECISIONS_FILEPATH = os.environ.get("DECISIONS_FILEPATH", ".db/decisions.csv")
3333
REASONING_FILEPATH = os.environ.get("REASONING_FILEPATH", ".db/reasoning.csv")
3434

35+
36+
# Loading contract helper functions
37+
def _load_contracts_deployments(
38+
filepath: str = "../smart-contract-infra/deployments/contracts.json",
39+
) -> dict:
40+
with Path(filepath).open() as f:
41+
return json.load(f)
42+
43+
44+
def _get_contract_address(contract_key: str, chain_id: int = 100) -> str:
45+
contracts = _load_contracts_deployments()
46+
chain_key = str(chain_id)
47+
try:
48+
return contracts["chains"][chain_key][contract_key]
49+
except KeyError:
50+
raise Exception(f"{contract_key} not found for chain ID {chain_id}")
51+
52+
3553
# Addresses
36-
SAFE_ADDRESS = "0xbc3c7818177dA740292659b574D48B699Fdf0816"
37-
TOKEN_ALLOWLIST_ADDRESS = "0x98a4351d926e6274829c3807f39D9a7037462589"
3854
GPV2_SETTLEMENT_ADDRESS = "0x9008D19f58AAbD9eD0D60971565AA8510560ab41"
39-
TRADING_MODULE_ADDRESS = "0xF11bC1ff8Ab8Cc297e5a1f1A51B8d1792E99D648"
55+
TOKEN_ALLOWLIST_ADDRESS = os.environ.get(
56+
"TOKEN_ALLOWLIST_ADDRESS", _get_contract_address("allowlist", 100)
57+
)
58+
SAFE_ADDRESS = os.environ.get("SAFE_ADDRESS", _get_contract_address("safe", 100))
59+
TRADING_MODULE_ADDRESS = os.environ.get(
60+
"TRADING_MODULE_ADDRESS", _get_contract_address("tradingModuleProxy", 100)
61+
)
4062

4163
GNO = "0x9C58BAcC331c9aa871AFD802DB6379a98e80CEdb"
4264
COW = "0x177127622c4A00F3d409B75571e12cB3c8973d3c"
4365
WXDAI = "0xe91D153E0b41518A2Ce8Dd3D7944Fa863463a97d"
4466
MONITORED_TOKENS = [GNO, COW, WXDAI]
4567

4668
MINIMUM_TOKEN_BALANCES = {
47-
GNO: 116,
69+
GNO: 55e14,
4870
COW: 10e18,
4971
WXDAI: 5e18,
5072
}
@@ -70,8 +92,6 @@ def _load_abi(abi_name: str) -> Dict:
7092

7193
# Variables
7294
START_BLOCK = int(os.environ.get("START_BLOCK", chain.blocks.head.number))
73-
HISTORICAL_BLOCK_STEP = int(os.environ.get("HISTORICAL_BLOCK_STEP", 720))
74-
EXTENSION_INTERVAL = int(os.environ.get("EXTENSION_INTERVAL", 6))
7595
TRADING_BLOCK_COOLDOWN = int(os.environ.get("TRADING_BLOCK_COOLDOWN", 360))
7696
SYSTEM_PROMPT = Path("./system_prompt.txt").read_text().strip()
7797

@@ -535,11 +555,6 @@ def _get_canonical_pair(token_a: str, token_b: str) -> tuple[str, str]:
535555
return (token_a, token_b) if token_a.lower() < token_b.lower() else (token_b, token_a)
536556

537557

538-
def _calculate_price(sell_amount: str, buy_amount: str) -> float:
539-
"""Calculate price from amounts"""
540-
return int(sell_amount) / int(buy_amount)
541-
542-
543558
def _process_trade_log(log) -> Dict:
544559
"""
545560
Process trade log and compute canonical price as:

smart-contract-infra/bun.lockb

9.76 KB
Binary file not shown.

0 commit comments

Comments
 (0)