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

Release 0.1.5 #284

Merged
merged 38 commits into from
Jul 2, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
4cf7925
implemented starting smart account api
nerfZael Jun 20, 2024
3b66585
removed the reliance on manager.balance_of
nerfZael Jun 20, 2024
cf5e542
decoupling safe manager from smart wallet
nerfZael Jun 20, 2024
b0f9076
Merge remote-tracking branch 'origin/main' into nerfzael/biconomy
nerfZael Jun 21, 2024
ca52a5d
Merge remote-tracking branch 'origin/dev' into nerfzael/biconomy
nerfZael Jun 24, 2024
eca4e24
smart-account-api docker implementation
nerfZael Jun 24, 2024
56cff6d
sending transactions with local biconomy account
nerfZael Jun 24, 2024
e352897
initial biconomy api implementation
nerfZael Jun 24, 2024
27d67ef
updated readme with biconomy instructions
nerfZael Jun 24, 2024
0f45fbb
added network with bundler url
nerfZael Jun 24, 2024
d03dc70
typing fixes
nerfZael Jun 24, 2024
a0b62cc
Merge remote-tracking branch 'origin/nerfzael/logging-and-errors' int…
nerfZael Jun 27, 2024
8197f6b
swapping is now async
nerfZael Jun 27, 2024
52c0bd1
lowered max rounds to 20 when testing
nerfZael Jun 27, 2024
224fb06
added execution logs
nerfZael Jun 27, 2024
7649dce
fixed inverted bool
nerfZael Jun 28, 2024
e95d49d
catching all exceptions for swap
nerfZael Jun 28, 2024
0fa32df
added pytest timeout
nerfZael Jun 28, 2024
0214491
fixes issue where benchmarks break if first test fails
nerfZael Jun 28, 2024
625acef
added timeouts to tests
nerfZael Jun 28, 2024
906aef9
using aiohttp directly to not dispose of session
nerfZael Jun 28, 2024
305acc4
added constants for test timeouts
nerfZael Jun 28, 2024
e7ab90c
executing transactions one by one in safe if the multisend fails to e…
nerfZael Jun 28, 2024
024a08f
added lifi api key to benchamarks ga
nerfZael Jun 28, 2024
b2edd8c
Merge pull request #281 from agentcoinorg/nerfzael/lifi-key-ga
nerfZael Jun 28, 2024
0749613
raising exception where appropriate
nerfZael Jun 28, 2024
4c2d438
Merge remote-tracking branch 'origin/dev' into nerfzael/biconomy
nerfZael Jun 28, 2024
6532b06
increased test timeout of research tests
nerfZael Jun 28, 2024
fd61f5c
updated tests with intents and using more gaming categories as valid
nerfZael Jun 28, 2024
b340e60
implemented feedback system
nerfZael Jul 2, 2024
d6e34d6
Merge pull request #277 from agentcoinorg/nerfzael/biconomy
nerfZael Jul 2, 2024
f4d8113
Merge remote-tracking branch 'origin/dev' into nerfzael/feedback
nerfZael Jul 2, 2024
4fd10ec
fixed types
nerfZael Jul 2, 2024
53aa6ef
raising not implemented error in abstract methods
nerfZael Jul 2, 2024
2d84bd2
fixed async load tokens
nerfZael Jul 2, 2024
67543ae
validating chain id
nerfZael Jul 2, 2024
1422f87
Merge pull request #283 from agentcoinorg/nerfzael/cesar-comment-fixes
cbrzn Jul 2, 2024
5b6553c
Merge pull request #282 from agentcoinorg/nerfzael/feedback
nerfZael Jul 2, 2024
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
1 change: 1 addition & 0 deletions .github/workflows/benchmarks.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ jobs:
echo "OPENAI_API_KEY=${{ secrets.CI_OPENAI_API_KEY }}" >> .env
echo "CHAIN_RPC_URL=${{ secrets.CI_CHAIN_RPC_URL }}" >> .env
echo "COINGECKO_API_KEY=${{ secrets.CI_COINGECKO_API_KEY }}" >> .env
echo "LIFI_API_KEY=${{ secrets.LIFI_API_KEY }}" >> .env

- name: Parse Comment for Arguments
id: parse_args
Expand Down
23 changes: 23 additions & 0 deletions Biconomy.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# Biconomy Smart Accounts

## Description
AutoTx can be used with a Biconomy Smart Account.

## Getting started
First, add the private key of the account you want to use as the owner to the `.env` file. Use the variable `SMART_ACCOUNT_OWNER_PK`.
Since the private key is stored as plain text, it is recommended to use a test account.

Next, start the `smart_account_api`, run: `poetry run start-smart-account-api`. This API server will be used to interact with the Biconomy Smart Account.
You can run `poetry run stop-smart-account-api` to stop the server.

To start AutoTx, you can now run: `poetry run ask "my-prompt-here"`.

When running for the first time, you will be prompted to send funds to your new Biconomy Smart Account in order to pay for the gas fees.
```
Using Biconomy smart account: {SMART-ACCOUNT-ADDRESS}
Detected empty account balance.
To use your new smart account, please top it up with some native currency.
Send the funds to: {SMART-ACCOUNT-ADDRESS} on {NETWORK-NAME}
Waiting...
```
After sending the funds, AutoTx will automatically detect the balance and continue with the transaction.
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,8 @@ python benchmarks.py ./autotx/tests/file_name.py::function_name 5
# run a specific test with 5 iterations and name the output folder (instead of the default timestamp)
python benchmarks.py ./autotx/tests/file_name.py::function_name 5 output_folder_name
```
# Biconomy Smart Accounts
To view the Biconomy Smart Accounts documentation, please see the [Biconomy.md](./Biconomy.md) file.

# API Server
To view the API server documentation, please see the [API.md](./API.md) file.
Expand Down
6 changes: 3 additions & 3 deletions autotx/AutoTx.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
from autotx.utils.logging.Logger import Logger
from autotx.utils.ethereum.networks import NetworkInfo
from autotx.utils.constants import OPENAI_BASE_URL, OPENAI_MODEL_NAME
from autotx.wallets.smart_wallet import SmartWallet
from autotx.smart_accounts.smart_account import SmartAccount

@dataclass(kw_only=True)
class CustomModel:
Expand Down Expand Up @@ -75,7 +75,7 @@ class RunResult:

class AutoTx:
web3: Web3
wallet: SmartWallet
wallet: SmartAccount
logger: Logger
intents: list[Intent]
network: NetworkInfo
Expand All @@ -94,7 +94,7 @@ class AutoTx:
def __init__(
self,
web3: Web3,
wallet: SmartWallet,
wallet: SmartAccount,
network: NetworkInfo,
agents: list[AutoTxAgent],
config: Config,
Expand Down
3 changes: 2 additions & 1 deletion autotx/agents/SwapTokensAgent.py
Original file line number Diff line number Diff line change
Expand Up @@ -171,7 +171,8 @@ async def run(
for swap_str in swaps:
(token_to_sell, token_to_buy) = swap_str.strip().split(" to ")
try:
all_intents.append(await swap(autotx, token_to_sell, token_to_buy))
intent = await swap(autotx, token_to_sell, token_to_buy)
all_intents.append(intent)
except InvalidInput as e:
all_errors.append(e)
except Exception as e:
Expand Down
38 changes: 34 additions & 4 deletions autotx/cli.py
Original file line number Diff line number Diff line change
@@ -1,17 +1,29 @@
from dotenv import load_dotenv
load_dotenv()

from eth_account import Account
import time
from web3 import Web3
import uuid
import uvicorn
from typing import cast
import click

from autotx.wallets.safe_smart_wallet import SafeSmartWallet
import uuid
from eth_account.signers.local import LocalAccount

from autotx.eth_address import ETHAddress
from autotx.utils.ethereum.get_native_balance import get_native_balance
from autotx.utils.ethereum.networks import NetworkInfo
from autotx.utils.constants import SMART_ACCOUNT_OWNER_PK
from autotx.smart_accounts.safe_smart_account import SafeSmartAccount
from autotx.smart_accounts.smart_account import SmartAccount
from autotx.utils.configuration import AppConfig
from autotx.utils.is_dev_env import is_dev_env
from autotx.setup import print_agent_address, setup_agents
from autotx.AutoTx import AutoTx, Config
from autotx.utils.ethereum.helpers.show_address_balances import show_address_balances
from autotx.smart_accounts.smart_account import SmartAccount
from autotx.smart_accounts.local_biconomy_smart_account import LocalBiconomySmartAccount

def print_autotx_info() -> None:
print("""
Expand All @@ -32,6 +44,15 @@ def print_autotx_info() -> None:
def main() -> None:
pass

def wait_for_native_top_up(web3: Web3, address: ETHAddress) -> None:
network = NetworkInfo(web3.eth.chain_id)

print(f"Detected empty account balance.\nTo use your new smart account, please top it up with some native currency.\nSend the funds to: {address} on {network.chain_id.name}")
print("Waiting...")
while get_native_balance(web3, address) == 0:
time.sleep(2)
print(f"Account balance detected ({get_native_balance(web3, address)}). Ready to use.")

@main.command()
@click.argument('prompt', required=False)
@click.option("-n", "--non-interactive", is_flag=True, help="Non-interactive mode (will not expect further user input or approval)")
Expand All @@ -45,8 +66,17 @@ def run(prompt: str | None, non_interactive: bool, verbose: bool, logs: str | No
if prompt == None:
prompt = click.prompt("What do you want to do?")

app_config = AppConfig.load(fill_dev_account=True)
wallet = SafeSmartWallet(app_config.manager, auto_submit_tx=non_interactive)
app_config = AppConfig()
wallet: SmartAccount
if SMART_ACCOUNT_OWNER_PK:
smart_account_owner = cast(LocalAccount, Account.from_key(SMART_ACCOUNT_OWNER_PK))
wallet = LocalBiconomySmartAccount(app_config.web3, smart_account_owner, auto_submit_tx=non_interactive)
print(f"Using Biconomy smart account: {wallet.address}")
if get_native_balance(app_config.web3, wallet.address) == 0:
wait_for_native_top_up(app_config.web3, wallet.address)
else:
wallet = SafeSmartAccount(app_config.rpc_url, app_config.network_info, auto_submit_tx=non_interactive, fill_dev_account=True)
print(f"Using Safe smart account: {wallet.address}")

(get_llm_config, agents, logs_dir) = setup_agents(logs, cache)

Expand Down
26 changes: 22 additions & 4 deletions autotx/db.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ def __init__(self, app_id: str):
self.client = get_db_client("public")
self.app_id = app_id

def start(self, prompt: str, address: str, chain_id: int, app_user_id: str) -> models.Task:
def start(self, prompt: str, address: str, chain_id: int, app_user_id: str, previous_task_id: str | None = None) -> models.Task:
client = get_db_client("public")

created_at = datetime.utcnow()
Expand All @@ -57,6 +57,8 @@ def start(self, prompt: str, address: str, chain_id: int, app_user_id: str) -> m
"messages": json.dumps([]),
"logs": json.dumps([]),
"intents": json.dumps([]),
"previous_task_id": previous_task_id,
"feedback": None
}
).execute()

Expand All @@ -72,6 +74,8 @@ def start(self, prompt: str, address: str, chain_id: int, app_user_id: str) -> m
messages=[],
logs=[],
intents=[],
previous_task_id=previous_task_id,
feedback=None
)

def stop(self, task_id: str) -> None:
Expand All @@ -95,9 +99,19 @@ def update(self, task: models.Task) -> None:
"messages": json.dumps(task.messages),
"error": task.error,
"logs": dump_pydantic_list(task.logs if task.logs else []),
"intents": dump_pydantic_list(task.intents)
"intents": dump_pydantic_list(task.intents),
"previous_task_id": task.previous_task_id
}
).eq("id", task.id).eq("app_id", self.app_id).execute()

def update_feedback(self, task_id: str, feedback: str) -> None:
client = get_db_client("public")

client.table("tasks").update(
{
"feedback": feedback
}
).eq("id", task_id).eq("app_id", self.app_id).execute()

def get(self, task_id: str) -> models.Task | None:
client = get_db_client("public")
Expand All @@ -124,7 +138,9 @@ def get(self, task_id: str) -> models.Task | None:
error=task_data["error"],
messages=json.loads(task_data["messages"]),
logs=[models.TaskLog(**log) for log in json.loads(task_data["logs"])] if task_data["logs"] else None,
intents=[load_intent(intent) for intent in json.loads(task_data["intents"])]
intents=[load_intent(intent) for intent in json.loads(task_data["intents"])],
previous_task_id=task_data["previous_task_id"],
feedback=task_data["feedback"]
)

def get_all(self) -> list[models.Task]:
Expand All @@ -147,7 +163,9 @@ def get_all(self) -> list[models.Task]:
error=task_data["error"],
messages=json.loads(task_data["messages"]),
logs=[models.TaskLog(**log) for log in json.loads(task_data["logs"])] if task_data["logs"] else None,
intents=[load_intent(intent) for intent in json.loads(task_data["intents"])]
intents=[load_intent(intent) for intent in json.loads(task_data["intents"])],
previous_task_id=task_data["previous_task_id"],
feedback=task_data["feedback"]
)
)

Expand Down
2 changes: 1 addition & 1 deletion autotx/intents.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ class IntentBase(BaseModel):

@abstractmethod
async def build_transactions(self, web3: Web3, network: NetworkInfo, smart_wallet_address: ETHAddress) -> list[Transaction]:
pass
raise NotImplementedError()

class SendIntent(IntentBase):
receiver: str
Expand Down
16 changes: 9 additions & 7 deletions autotx/load_tokens.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import asyncio
import json
from textwrap import dedent
from typing import Union

import requests
import aiohttp

KLEROS_TOKENS_LIST = "https://t2crtokens.eth.link/"
COINGECKO_TOKENS_LISTS = [
Expand All @@ -19,14 +19,16 @@
TOKENS_LIST = [KLEROS_TOKENS_LIST, *COINGECKO_TOKENS_LISTS]


def fetch_tokens_list() -> None:
async def fetch_tokens_list() -> None:
loaded_tokens: list[dict[str, Union[str, int]]] = []

for token_list_url in TOKENS_LIST:
try:
response = requests.get(token_list_url)
tokens = json.loads(response.text)["tokens"]
loaded_tokens.extend(tokens)
async with aiohttp.ClientSession() as session:
response = await session.get(token_list_url)
result = await response.json()
tokens = result["tokens"]
loaded_tokens.extend(tokens)
except:
print("Error while trying to fetch list:", token_list_url)

Expand All @@ -42,4 +44,4 @@ def fetch_tokens_list() -> None:


def run() -> None:
fetch_tokens_list()
asyncio.run(fetch_tokens_list())
2 changes: 2 additions & 0 deletions autotx/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ class Task(BaseModel):
messages: List[str]
logs: List[TaskLog] | None
intents: List[Intent]
previous_task_id: str | None
feedback: str | None

class TaskError(BaseModel):
id: str
Expand Down
Loading
Loading