Skip to content

Commit

Permalink
added retries for no quotes error and showing better message for low …
Browse files Browse the repository at this point in the history
…amount error
  • Loading branch information
nerfZael committed Jun 19, 2024
1 parent bfac4c5 commit 4da2002
Show file tree
Hide file tree
Showing 8 changed files with 96 additions and 30 deletions.
2 changes: 1 addition & 1 deletion autotx/AutoTx.py
Original file line number Diff line number Diff line change
Expand Up @@ -209,7 +209,7 @@ async def try_run(self, prompt: str, non_interactive: bool, summary_method: str
is_goal_supported = chat.chat_history[-1]["content"] != "Goal not supported: TERMINATE"

try:
result = self.wallet.on_intents_ready(self.intents)
result = await self.wallet.on_intents_ready(self.intents)

if isinstance(result, str):
intents_info ="\n".join(
Expand Down
10 changes: 5 additions & 5 deletions autotx/agents/SwapTokensAgent.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
from autotx.intents import BuyIntent, Intent, SellIntent
from autotx.token import Token
from autotx.eth_address import ETHAddress
from autotx.utils.ethereum.lifi.swap import SUPPORTED_NETWORKS_BY_LIFI, can_build_swap_transaction
from autotx.utils.ethereum.lifi.swap import SUPPORTED_NETWORKS_BY_LIFI, a_can_build_swap_transaction
from autotx.utils.ethereum.networks import NetworkInfo
from gnosis.eth import EthereumNetworkNotSupported as ChainIdNotSupported

Expand Down Expand Up @@ -81,7 +81,7 @@ def get_tokens_address(token_in: str, token_out: str, network_info: NetworkInfo)
class InvalidInput(Exception):
pass

def swap(autotx: AutoTx, token_to_sell: str, token_to_buy: str) -> Intent:
async def swap(autotx: AutoTx, token_to_sell: str, token_to_buy: str) -> Intent:
sell_parts = token_to_sell.split(" ")
buy_parts = token_to_buy.split(" ")

Expand Down Expand Up @@ -118,7 +118,7 @@ def swap(autotx: AutoTx, token_to_sell: str, token_to_buy: str) -> Intent:
token_in, token_out, autotx.network
)

can_build_swap_transaction(
await a_can_build_swap_transaction(
autotx.web3,
Decimal(exact_amount),
ETHAddress(token_in_address),
Expand Down Expand Up @@ -152,7 +152,7 @@ class BulkSwapTool(AutoTxTool):
)

def build_tool(self, autotx: AutoTx) -> Callable[[str], str]:
def run(
async def run(
tokens: Annotated[
str,
"""
Expand All @@ -171,7 +171,7 @@ def run(
for swap_str in swaps:
(token_to_sell, token_to_buy) = swap_str.strip().split(" to ")
try:
all_intents.append(swap(autotx, token_to_sell, token_to_buy))
all_intents.append(await swap(autotx, token_to_sell, token_to_buy))
except InvalidInput as e:
all_errors.append(e)
except Exception as e:
Expand Down
14 changes: 7 additions & 7 deletions autotx/intents.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
from autotx.utils.ethereum.build_transfer_native import build_transfer_native
from autotx.utils.ethereum.constants import NATIVE_TOKEN_ADDRESS
from autotx.eth_address import ETHAddress
from autotx.utils.ethereum.lifi.swap import build_swap_transaction
from autotx.utils.ethereum.lifi.swap import a_build_swap_transaction
from autotx.utils.ethereum.networks import NetworkInfo
from autotx.utils.format_amount import format_amount

Expand All @@ -27,7 +27,7 @@ class IntentBase(BaseModel):
summary: str

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

class SendIntent(IntentBase):
Expand All @@ -45,7 +45,7 @@ def create(cls, token: Token, amount: float, receiver: ETHAddress) -> 'SendInten
summary=f"Transfer {format_amount(amount)} {token.symbol} to {receiver}",
)

def build_transactions(self, web3: Web3, network: NetworkInfo, smart_wallet_address: ETHAddress) -> list[Transaction]:
async def build_transactions(self, web3: Web3, network: NetworkInfo, smart_wallet_address: ETHAddress) -> list[Transaction]:
tx: TxParams

if self.token.address == NATIVE_TOKEN_ADDRESS:
Expand Down Expand Up @@ -79,8 +79,8 @@ def create(cls, from_token: Token, to_token: Token, amount: float) -> 'BuyIntent
summary=f"Buy {format_amount(amount)} {to_token.symbol} with {from_token.symbol}",
)

def build_transactions(self, web3: Web3, network: NetworkInfo, smart_wallet_address: ETHAddress) -> list[Transaction]:
transactions = build_swap_transaction(
async def build_transactions(self, web3: Web3, network: NetworkInfo, smart_wallet_address: ETHAddress) -> list[Transaction]:
transactions = await a_build_swap_transaction(
web3,
Decimal(str(self.amount)),
ETHAddress(self.from_token.address),
Expand All @@ -107,9 +107,9 @@ def create(cls, from_token: Token, to_token: Token, amount: float) -> 'SellInten
summary=f"Sell {format_amount(amount)} {from_token.symbol} for {to_token.symbol}",
)

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

transactions = build_swap_transaction(
transactions = await a_build_swap_transaction(
web3,
Decimal(str(self.amount)),
ETHAddress(self.from_token.address),
Expand Down
14 changes: 7 additions & 7 deletions autotx/server.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ def __init__(self,
self.max_rounds = max_rounds
self.is_dev = is_dev

autotx_params: AutoTxParams = AutoTxParams(verbose=False, logs=None, cache=False, max_rounds=None, is_dev=False)
autotx_params: AutoTxParams = AutoTxParams(verbose=False, logs=None, cache=False, max_rounds=200, is_dev=False)

app_router = APIRouter()

Expand Down Expand Up @@ -82,7 +82,7 @@ def authorize_app_and_user(authorization: str | None, user_id: str) -> tuple[mod

return (app, app_user)

def build_transactions(app_id: str, user_id: str, chain_id: int, address: str, task: models.Task) -> List[Transaction]:
async def build_transactions(app_id: str, user_id: str, chain_id: int, address: str, task: models.Task) -> List[Transaction]:
if task.running:
raise HTTPException(status_code=400, detail="Task is still running")

Expand All @@ -94,7 +94,7 @@ def build_transactions(app_id: str, user_id: str, chain_id: int, address: str, t
transactions: list[Transaction] = []

for intent in task.intents:
transactions.extend(intent.build_transactions(app_config.web3, app_config.network_info, app_config.manager.address))
transactions.extend(await intent.build_transactions(app_config.web3, app_config.network_info, app_config.manager.address))

return transactions

Expand Down Expand Up @@ -211,7 +211,7 @@ def get_intents(task_id: str, authorization: Annotated[str | None, Header()] = N
return task.intents

@app_router.get("/api/v1/tasks/{task_id}/transactions", response_model=List[Transaction])
def get_transactions(
async def get_transactions(
task_id: str,
address: str,
chain_id: int,
Expand All @@ -227,7 +227,7 @@ def get_transactions(
if task.chain_id != chain_id:
raise HTTPException(status_code=400, detail="Chain ID does not match task")

transactions = build_transactions(app.id, user_id, chain_id, address, task)
transactions = await build_transactions(app.id, user_id, chain_id, address, task)

return transactions

Expand All @@ -236,7 +236,7 @@ class PreparedTransactionsDto(BaseModel):
transactions: List[Transaction]

@app_router.post("/api/v1/tasks/{task_id}/transactions/prepare", response_model=PreparedTransactionsDto)
def prepare_transactions(
async def prepare_transactions(
task_id: str,
address: str,
chain_id: int,
Expand All @@ -252,7 +252,7 @@ def prepare_transactions(
if task.chain_id != chain_id:
raise HTTPException(status_code=400, detail="Chain ID does not match task")

transactions = build_transactions(app.id, app_user.user_id, chain_id, address, task)
transactions = await build_transactions(app.id, app_user.user_id, chain_id, address, task)

if len(transactions) == 0:
raise HTTPException(status_code=400, detail="No transactions to send")
Expand Down
78 changes: 72 additions & 6 deletions autotx/utils/ethereum/lifi/swap.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import asyncio
from dataclasses import dataclass
from decimal import Decimal
from typing import Any, cast
Expand Down Expand Up @@ -103,6 +104,49 @@ def get_quote(
quote["toolDetails"]["name"],
)

async def fetch_quote_with_retries(
token_in_address: ETHAddress,
token_in_decimals: int,
token_in_symbol: str,
token_out_address: ETHAddress,
token_out_decimals: int,
token_out_symbol: str,
chain: ChainId,
amount: Decimal,
is_exact_input: bool,
_from: ETHAddress,
) -> QuoteInformation:
retries = 0
while True:
try:
quote = get_quote(
token_in_address,
token_in_decimals,
token_in_symbol,
token_out_address,
token_out_decimals,
token_out_symbol,
chain,
amount,
not is_exact_input,
_from,
)
return quote
except Exception as e:
if "The from amount must be greater than zero." in str(e):
if is_exact_input:
raise Exception(f"The specified amount of {token_in_symbol} is too low")
else:
raise Exception(f"The specified amount of {token_out_symbol} is too low")

elif "No available quotes for the requested transfer" in str(e):
if retries < 5:
retries += 1
await asyncio.sleep(1)
continue
raise e


def build_swap_transaction(
web3: Web3,
amount: Decimal,
Expand All @@ -111,6 +155,17 @@ def build_swap_transaction(
_from: ETHAddress,
is_exact_input: bool,
chain: ChainId,
) -> list[Transaction]:
return asyncio.run(a_build_swap_transaction(web3, amount, token_in_address, token_out_address, _from, is_exact_input, chain))

async def a_build_swap_transaction(
web3: Web3,
amount: Decimal,
token_in_address: ETHAddress,
token_out_address: ETHAddress,
_from: ETHAddress,
is_exact_input: bool,
chain: ChainId,
) -> list[Transaction]:
native_token_symbol = get_native_token_symbol(chain)
token_in_is_native = token_in_address.hex == NATIVE_TOKEN_ADDRESS
Expand Down Expand Up @@ -139,7 +194,8 @@ def build_swap_transaction(
if token_out_is_native
else token_out.functions.symbol().call()
)
quote = get_quote(

quote = await fetch_quote_with_retries(
token_in_address,
token_in_decimals,
token_in_symbol,
Expand All @@ -148,10 +204,9 @@ def build_swap_transaction(
token_out_symbol,
chain,
amount,
not is_exact_input,
is_exact_input,
_from,
)

transactions: list[Transaction] = []
if not token_in_is_native:
approval_address = quote.approval_address
Expand Down Expand Up @@ -194,6 +249,17 @@ def can_build_swap_transaction(
_from: ETHAddress,
is_exact_input: bool,
chain: ChainId,
) -> bool:
return asyncio.run(a_can_build_swap_transaction(web3, amount, token_in_address, token_out_address, _from, is_exact_input, chain))

async def a_can_build_swap_transaction(
web3: Web3,
amount: Decimal,
token_in_address: ETHAddress,
token_out_address: ETHAddress,
_from: ETHAddress,
is_exact_input: bool,
chain: ChainId,
) -> bool:
native_token_symbol = get_native_token_symbol(chain)
token_in_is_native = token_in_address.hex == NATIVE_TOKEN_ADDRESS
Expand Down Expand Up @@ -222,7 +288,8 @@ def can_build_swap_transaction(
if token_out_is_native
else token_out.functions.symbol().call()
)
quote = get_quote(

quote = await fetch_quote_with_retries(
token_in_address,
token_in_decimals,
token_in_symbol,
Expand All @@ -231,10 +298,9 @@ def can_build_swap_transaction(
token_out_symbol,
chain,
amount,
not is_exact_input,
is_exact_input,
_from,
)

if not token_in_is_native:
approval_address = quote.approval_address
allowance = token_in.functions.allowance(_from.hex, approval_address).call()
Expand Down
2 changes: 1 addition & 1 deletion autotx/wallets/api_smart_wallet.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,5 +26,5 @@ def on_intents_prepared(self, intents: list[Intent]) -> None:
saved_task.intents.extend(intents)
self.tasks.update(saved_task)

def on_intents_ready(self, _intents: list[Intent]) -> bool | str:
async def on_intents_ready(self, _intents: list[Intent]) -> bool | str:
return True
4 changes: 2 additions & 2 deletions autotx/wallets/safe_smart_wallet.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,10 @@ def __init__(self, manager: SafeManager, auto_submit_tx: bool):
def on_intents_prepared(self, intents: list[Intent]) -> None:
pass

def on_intents_ready(self, intents: list[Intent]) -> bool | str:
async def on_intents_ready(self, intents: list[Intent]) -> bool | str:
transactions: list[TransactionBase] = []

for intent in intents:
transactions.extend(intent.build_transactions(self.manager.web3, self.manager.network, self.manager.address))
transactions.extend(await intent.build_transactions(self.manager.web3, self.manager.network, self.manager.address))

return self.manager.send_multisend_tx_batch(transactions, not self.auto_submit_tx)
2 changes: 1 addition & 1 deletion autotx/wallets/smart_wallet.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ def on_intents_prepared(self, intents: list[Intent]) -> None:
pass

@abstractmethod
def on_intents_ready(self, intents: list[Intent]) -> bool | str: # True if sent, False if declined, str if feedback
async def on_intents_ready(self, intents: list[Intent]) -> bool | str: # True if sent, False if declined, str if feedback
pass

def balance_of(self, token_address: ETHAddress | None = None) -> float:
Expand Down

0 comments on commit 4da2002

Please sign in to comment.