Skip to content

Commit

Permalink
Merge pull request #234 from polywrap/feat/lifi
Browse files Browse the repository at this point in the history
feat: use `li.fi` for swaps
  • Loading branch information
nerfZael authored Apr 22, 2024
2 parents 417ce46 + 4c14962 commit 585ac5e
Show file tree
Hide file tree
Showing 14 changed files with 558 additions and 402 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ Below is a list of existing and anticipated agents that AutoTx can use. If you'd
| Agent | Description | Status |
|-|-|-|
| [Send Tokens](./autotx/agents/SendTokensAgent.py) | Send tokens (ERC20 & ETH) to a receiving address. | :rocket: |
| [Swap Tokens](./autotx/agents/SwapTokensAgent.py) | Swap from one token to another. Currently integrated with Uniswap. | :rocket: |
| [Swap Tokens](./autotx/agents/SwapTokensAgent.py) | Swap from one token to another. Currently integrated with [Li.Fi](https://docs.li.fi/). | :rocket: |
| [Token Research](./autotx/agents/ResearchTokensAgent.py) | Research tokens, liquidity, prices, graphs, etc. | :rocket: |
| Earn Yield | Stake assets to earn yield. | :memo: [draft](https://github.com/polywrap/AutoTx/issues/98) |
| Bridge Tokens | Bridge tokens from one chain to another. | :memo: [draft](https://github.com/polywrap/AutoTx/issues/46) |
Expand Down
20 changes: 11 additions & 9 deletions autotx/agents/SwapTokensAgent.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,10 @@
from autotx.AutoTx import AutoTx
from autotx.autotx_agent import AutoTxAgent
from autotx.autotx_tool import AutoTxTool
from autotx.utils.ethereum.eth_address import ETHAddress
from autotx.utils.ethereum.lifi.swap import SUPPORTED_NETWORKS_BY_LIFI, build_swap_transaction
from autotx.utils.PreparedTx import PreparedTx
from autotx.utils.ethereum.networks import NetworkInfo
from autotx.utils.ethereum.uniswap.swap import SUPPORTED_UNISWAP_V3_NETWORKS, build_swap_transaction
from gnosis.eth import EthereumNetworkNotSupported as ChainIdNotSupported

name = "swap-tokens"
Expand Down Expand Up @@ -70,11 +71,6 @@ def get_tokens_address(token_in: str, token_out: str, network_info: NetworkInfo)
token_in = token_in.lower()
token_out = token_out.lower()

if not network_info.chain_id in SUPPORTED_UNISWAP_V3_NETWORKS:
raise ChainIdNotSupported(
f"Network {network_info.chain_id.name.lower()} not supported for swap"
)

if token_in not in network_info.tokens:
raise Exception(f"Token {token_in} is not supported in network {network_info.chain_id.name.lower()}")

Expand All @@ -90,6 +86,11 @@ def swap(autotx: AutoTx, token_to_sell: str, token_to_buy: str) -> list[Prepared
sell_parts = token_to_sell.split(" ")
buy_parts = token_to_buy.split(" ")

if not autotx.network.chain_id in SUPPORTED_NETWORKS_BY_LIFI:
raise ChainIdNotSupported(
f"Network {autotx.network.chain_id.name.lower()} not supported for swap"
)

if len(sell_parts) == 2 and len(buy_parts) == 2:
raise InvalidInput(f"Invalid input: \"{token_to_sell} to {token_to_buy}\". Only one token amount should be provided. IMPORTANT: Take another look at the user's goal, and try again.")

Expand All @@ -113,10 +114,11 @@ def swap(autotx: AutoTx, token_to_sell: str, token_to_buy: str) -> list[Prepared
swap_transactions = build_swap_transaction(
autotx.manager.client,
Decimal(exact_amount),
token_in_address,
token_out_address,
autotx.manager.address.hex,
ETHAddress(token_in_address),
ETHAddress(token_out_address),
autotx.manager.address,
is_exact_input,
autotx.network.chain_id
)
autotx.transactions.extend(swap_transactions)

Expand Down
50 changes: 30 additions & 20 deletions autotx/tests/agents/token/test_swap.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
from autotx.utils.ethereum import load_w3
from autotx.utils.ethereum.lifi.swap import SLIPPAGE
from autotx.utils.ethereum.networks import NetworkInfo
from autotx.utils.ethereum.eth_address import ETHAddress

DIFFERENCE_PERCENTAGE = 1.01

def test_swap_with_non_default_token(configuration, auto_tx):
(_, _, _, manager) = configuration
web3 = load_w3()
Expand All @@ -15,7 +18,8 @@ def test_swap_with_non_default_token(configuration, auto_tx):

new_balance = manager.balance_of(shib_address)

assert 100000 == new_balance
expected_shib_amount = 100000
assert expected_shib_amount <= new_balance <= expected_shib_amount * DIFFERENCE_PERCENTAGE

def test_swap_native(configuration, auto_tx):
(_, _, _, manager) = configuration
Expand All @@ -24,13 +28,11 @@ def test_swap_native(configuration, auto_tx):
usdc_address = ETHAddress(network_info.tokens["usdc"])

prompt = "Buy 100 USDC with ETH"
balance = manager.balance_of(usdc_address)

auto_tx.run(prompt, non_interactive=True)

new_balance = manager.balance_of(usdc_address)

assert balance + 100 == new_balance
expected_usdc_amount = 100
assert expected_usdc_amount <= new_balance <= expected_usdc_amount * DIFFERENCE_PERCENTAGE

def test_swap_multiple_1(configuration, auto_tx):
(_, _, _, manager) = configuration
Expand All @@ -40,12 +42,15 @@ def test_swap_multiple_1(configuration, auto_tx):
wbtc_address = ETHAddress(network_info.tokens["wbtc"])

prompt = "Buy 1000 USDC with ETH and then buy WBTC with 500 USDC"
usdc_balance = manager.balance_of(usdc_address)
wbtc_balance = manager.balance_of(wbtc_address)

auto_tx.run(prompt, non_interactive=True)

assert usdc_balance + 500 == manager.balance_of(usdc_address)
expected_usdc_amount = 500
usdc_balance = manager.balance_of(usdc_address)
# 1000 is the amount bought so we need to get the difference from that amount
expected_usdc_amount_plus_slippage = 1000 * DIFFERENCE_PERCENTAGE
assert expected_usdc_amount <= usdc_balance <= expected_usdc_amount_plus_slippage - expected_usdc_amount
assert wbtc_balance < manager.balance_of(wbtc_address)

def test_swap_multiple_2(configuration, auto_tx):
Expand All @@ -56,12 +61,13 @@ def test_swap_multiple_2(configuration, auto_tx):
wbtc_address = ETHAddress(network_info.tokens["wbtc"])

prompt = "Sell ETH for 1000 USDC and then sell 500 USDC for WBTC"
usdc_balance = manager.balance_of(usdc_address)
wbtc_balance = manager.balance_of(wbtc_address)

auto_tx.run(prompt, non_interactive=True)

assert usdc_balance + 500 == manager.balance_of(usdc_address)
expected_amount = 500
usdc_balance = manager.balance_of(usdc_address)
assert expected_amount <= usdc_balance
assert wbtc_balance < manager.balance_of(wbtc_address)

def test_swap_triple(configuration, auto_tx):
Expand All @@ -73,15 +79,18 @@ def test_swap_triple(configuration, auto_tx):
wbtc_address = ETHAddress(network_info.tokens["wbtc"])

prompt = "Buy 1 USDC, 0.5 UNI and 0.05 WBTC with ETH"
usdc_balance = manager.balance_of(usdc_address)
uni_balance = manager.balance_of(uni_address)
wbtc_balance = manager.balance_of(wbtc_address)

auto_tx.run(prompt, non_interactive=True)

assert usdc_balance + 1 == manager.balance_of(usdc_address)
assert uni_balance + 0.5 == manager.balance_of(uni_address)
assert wbtc_balance + 0.05 == manager.balance_of(wbtc_address)
expected_usdc_amount = 1
expected_uni_amount = 0.5
expected_wbtc_amount = 0.05
usdc_balance = manager.balance_of(usdc_address)
uni_balance = manager.balance_of(uni_address)
wbtc_balance = manager.balance_of(wbtc_address)
assert expected_usdc_amount <= usdc_balance <= expected_usdc_amount * DIFFERENCE_PERCENTAGE
assert expected_uni_amount <= uni_balance <= expected_uni_amount * DIFFERENCE_PERCENTAGE
assert expected_wbtc_amount <= wbtc_balance <= expected_wbtc_amount * DIFFERENCE_PERCENTAGE

def test_swap_complex_1(configuration, auto_tx): # This one is complex because it confuses the LLM with WBTC amount
(_, _, _, manager) = configuration
Expand All @@ -91,12 +100,12 @@ def test_swap_complex_1(configuration, auto_tx): # This one is complex because i
wbtc_address = ETHAddress(network_info.tokens["wbtc"])

prompt = "Swap ETH to 0.05 WBTC, then, swap WBTC to 1000 USDC"
usdc_balance = manager.balance_of(usdc_address)
wbtc_balance = manager.balance_of(wbtc_address)

auto_tx.run(prompt, non_interactive=True)

assert usdc_balance + 1000 == manager.balance_of(usdc_address)
expected_usdc_amount = 1000
usdc_balance = manager.balance_of(usdc_address)
assert expected_usdc_amount <= usdc_balance <= expected_usdc_amount * DIFFERENCE_PERCENTAGE
assert wbtc_balance < manager.balance_of(wbtc_address)

def test_swap_complex_2(configuration, auto_tx): # This one is complex because it confuses the LLM with WBTC amount
Expand All @@ -108,9 +117,10 @@ def test_swap_complex_2(configuration, auto_tx): # This one is complex because i

prompt = "Buy 1000 USDC with ETH, then sell the USDC to buy 0.001 WBTC"
usdc_balance = manager.balance_of(usdc_address)
wbtc_balance = manager.balance_of(wbtc_address)

auto_tx.run(prompt, non_interactive=True)

wbtc_balance = manager.balance_of(wbtc_address)
expected_wbtc_amount = 0.001
assert expected_wbtc_amount <= wbtc_balance <= expected_wbtc_amount * DIFFERENCE_PERCENTAGE
assert usdc_balance < manager.balance_of(usdc_address)
assert wbtc_balance + 0.001 == manager.balance_of(wbtc_address)
38 changes: 17 additions & 21 deletions autotx/tests/agents/token/test_swap_and_send.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
from autotx.utils.ethereum.networks import NetworkInfo
from autotx.utils.ethereum.eth_address import ETHAddress

DIFFERENCE_PERCENTAGE = 1.01

def test_swap_and_send_simple(configuration, auto_tx, test_accounts):
(_, _, client, manager) = configuration
web3 = load_w3()
Expand All @@ -12,16 +14,13 @@ def test_swap_and_send_simple(configuration, auto_tx, test_accounts):

prompt = f"Swap ETH to 0.05 WBTC, and send 0.01 WBTC to {receiver}"

wbtc_safe_address = manager.balance_of(wbtc_address)
receiver_wbtc_balance = get_erc20_balance(client.w3, wbtc_address, receiver)

auto_tx.run(prompt, non_interactive=True)

new_wbtc_safe_address = manager.balance_of(wbtc_address)
new_receiver_wbtc_balance = get_erc20_balance(client.w3, wbtc_address, receiver)

assert new_wbtc_safe_address == wbtc_safe_address + 0.04
assert new_receiver_wbtc_balance == receiver_wbtc_balance + 0.01
excepted_safe_wbtc_balance = 0.04
assert excepted_safe_wbtc_balance <= new_wbtc_safe_address <= new_wbtc_safe_address * DIFFERENCE_PERCENTAGE
assert new_receiver_wbtc_balance == 0.01

def test_swap_and_send_complex(configuration, auto_tx, test_accounts):
(_, _, client, manager) = configuration
Expand All @@ -35,18 +34,16 @@ def test_swap_and_send_complex(configuration, auto_tx, test_accounts):
prompt = f"Swap ETH to 0.05 WBTC, then, swap WBTC to 1000 USDC and send 50 USDC to {receiver}"

wbtc_safe_address = manager.balance_of(wbtc_address)
usdc_safe_address = manager.balance_of(usdc_address)
receiver_usdc_balance = get_erc20_balance(client.w3, usdc_address, receiver)

auto_tx.run(prompt, non_interactive=True)

new_wbtc_safe_address = manager.balance_of(wbtc_address)
new_usdc_safe_address = manager.balance_of(usdc_address)
new_receiver_usdc_balance = get_erc20_balance(client.w3, usdc_address, receiver)

expected_usdc_safe_balance = 950
assert new_wbtc_safe_address > wbtc_safe_address
assert new_usdc_safe_address == usdc_safe_address + 950
assert new_receiver_usdc_balance == receiver_usdc_balance + 50
assert expected_usdc_safe_balance <= new_usdc_safe_address <= expected_usdc_safe_balance * DIFFERENCE_PERCENTAGE
assert new_receiver_usdc_balance == 50

def test_send_and_swap_simple(configuration, auto_tx, test_accounts):
(_, _, client, manager) = configuration
Expand All @@ -58,17 +55,17 @@ def test_send_and_swap_simple(configuration, auto_tx, test_accounts):

prompt = f"Send 0.1 ETH to {receiver}, and then swap ETH to 0.05 WBTC"

user_wbtc_balance = manager.balance_of(wbtc_address)
receiver_native_balance = get_native_balance(client.w3, receiver)
receiver_wbtc_balance = get_erc20_balance(client.w3, wbtc_address, receiver)

auto_tx.run(prompt, non_interactive=True)

new_user_wbtc_balance = manager.balance_of(wbtc_address)
safe_wbtc_balance = manager.balance_of(wbtc_address)
new_receiver_native_balance = get_native_balance(client.w3, receiver)
new_receiver_wbtc_balance = get_erc20_balance(client.w3, wbtc_address, receiver)

assert new_user_wbtc_balance == user_wbtc_balance + 0.05
expected_wbtc_safe_balance = 0.05
assert expected_wbtc_safe_balance <= safe_wbtc_balance <= expected_wbtc_safe_balance * DIFFERENCE_PERCENTAGE
assert receiver_wbtc_balance == 0
assert new_receiver_wbtc_balance == receiver_wbtc_balance
assert new_receiver_native_balance == receiver_native_balance + 0.1
Expand All @@ -85,23 +82,22 @@ def test_send_and_swap_complex(configuration, auto_tx, test_accounts):

prompt = f"Send 0.1 ETH to {receiver_1}, then swap ETH to 0.05 WBTC, then, swap WBTC to 1000 USDC and send 50 USDC to {receiver_2}"

wbtc_safe_address = manager.balance_of(wbtc_address)
usdc_safe_address = manager.balance_of(usdc_address)
wbtc_safe_balance = manager.balance_of(wbtc_address)
receiver_1_native_balance = get_native_balance(client.w3, receiver_1)
receiver_2_usdc_balance = get_erc20_balance(client.w3, usdc_address, receiver_2)

auto_tx.run(prompt, non_interactive=True)

new_wbtc_safe_address = manager.balance_of(wbtc_address)
new_usdc_safe_address = manager.balance_of(usdc_address)
new_wbtc_safe_balance = manager.balance_of(wbtc_address)
new_usdc_safe_balance = manager.balance_of(usdc_address)
new_receiver_1_native_balance = get_native_balance(client.w3, receiver_1)
new_receiver_1_usdc_balance = get_erc20_balance(client.w3, usdc_address, receiver_1)
new_receiver_1_wbtc_balance = get_erc20_balance(client.w3, wbtc_address, receiver_1)
new_receiver_2_wbtc_balance = get_erc20_balance(client.w3, wbtc_address, receiver_2)
new_receiver_2_usdc_balance = get_erc20_balance(client.w3, usdc_address, receiver_2)

assert new_wbtc_safe_address > wbtc_safe_address
assert new_usdc_safe_address == usdc_safe_address + 950
expected_usdc_safe_balance = 950
assert expected_usdc_safe_balance <= new_usdc_safe_balance <= expected_usdc_safe_balance * DIFFERENCE_PERCENTAGE
assert new_wbtc_safe_balance > wbtc_safe_balance
assert new_receiver_1_native_balance == receiver_1_native_balance + 0.1
assert new_receiver_1_usdc_balance == 0
assert new_receiver_1_wbtc_balance == 0
Expand Down
2 changes: 1 addition & 1 deletion autotx/tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ def usdc(configuration) -> ETHAddress:

amount = 100

swap(client, user, amount, eth_address, usdc_address)
swap(client, user, amount, eth_address, usdc_address, network_info.chain_id)

transfer_erc20(client.w3, usdc_address, user, manager.address, amount)

Expand Down
Loading

0 comments on commit 585ac5e

Please sign in to comment.