Skip to content

Commit

Permalink
Merge pull request #284 from agentcoinorg/dev
Browse files Browse the repository at this point in the history
Release 0.1.5
  • Loading branch information
nerfZael authored Jul 2, 2024
2 parents bf996ae + 5b6553c commit 1d70f36
Show file tree
Hide file tree
Showing 51 changed files with 2,618 additions and 728 deletions.
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

0 comments on commit 1d70f36

Please sign in to comment.