Skip to content

Commit

Permalink
Merge pull request #63 from tinymanorg/swap-router
Browse files Browse the repository at this point in the history
Swap Router
  • Loading branch information
gokselcoban committed Apr 17, 2023
2 parents 5b90253 + 619e989 commit f2f45cf
Show file tree
Hide file tree
Showing 24 changed files with 1,651 additions and 58 deletions.
9 changes: 6 additions & 3 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,11 @@ jobs:
strategy:
matrix:
python-version: ["3.11", "3.10", "3.9", "3.8"]
py-algorand-sdk-version: ["1.10.0", "1.11.0", "1.12.0", "1.13.0", "1.13.1", "1.14.0", "1.15.0", "1.16.0", "1.16.1",
"1.17.0", "1.18.0", "1.19.0", "1.20.0", "1.20.1", "1.20.2", "2.0.0"]
py-algorand-sdk-version: [
"1.10.0", "1.11.0", "1.12.0", "1.13.0", "1.13.1", "1.14.0", "1.15.0", "1.16.0",
"1.16.1","1.17.0", "1.18.0","1.19.0", "1.20.0", "1.20.1", "1.20.2",
"2.0.0", "2.1.0", "2.1.1", "2.1.2"
]

steps:
- uses: actions/checkout@v3
Expand All @@ -23,7 +26,7 @@ jobs:
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install flake8 black py-algorand-sdk==${{ matrix.py-algorand-sdk-version }}
pip install flake8 black==23.3.0 py-algorand-sdk==${{ matrix.py-algorand-sdk-version }} requests
- name: Run flake8
run: flake8 ${{ github.workspace }} --ignore=E501,F403,F405,E126,E121,W503,E203
Expand Down
2 changes: 1 addition & 1 deletion .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ repos:
exclude: ^(env|venv)

- repo: https://github.com/psf/black
rev: 22.8.0
rev: 23.3.0
hooks:
- id: black
args: ['.', '--check']
Expand Down
Empty file.
206 changes: 206 additions & 0 deletions examples/swap_router/swap.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,206 @@
# This sample is provided for demonstration purposes only.
# It is not intended for production use.
# This example does not constitute trading advice.
from typing import Optional
from urllib.parse import quote_plus

from examples.v2.utils import get_algod
from tinyman.assets import Asset
from tinyman.assets import AssetAmount
from tinyman.optin import prepare_asset_optin_transactions
from tinyman.swap_router.routes import Route
from tinyman.swap_router.routes import get_best_fixed_input_route
from tinyman.swap_router.swap_router import fetch_best_route_suggestion
from tinyman.swap_router.swap_router import (
get_swap_router_app_opt_in_required_asset_ids,
prepare_swap_router_asset_opt_in_transaction,
)
from tinyman.v1.client import TinymanClient
from tinyman.v1.pools import Pool as TinymanV1Pool
from tinyman.v2.client import TinymanV2Client
from tinyman.v2.client import TinymanV2TestnetClient
from tinyman.v2.pools import Pool as TinymanV2Pool


def fetch_routes(
tinyman_v1_client: Optional[TinymanClient],
tinyman_v2_client: TinymanV2Client,
asset_in: Asset,
asset_out: Asset,
swap_type: str,
amount: int,
) -> "list[Route]":
"""
This is an example route list preparation.
You can build yor own route list according to your needs.
The list contains;
V1 Direct Route if it exists and has liquidity,
V2 Direct Route if it exists and has liquidity,
V2 Indirect (2 swap) Route provided by Tinyman API.
"""
routes = []

if tinyman_v1_client is not None:
# Don't check V1 pool if the client is not provided
v1_pool = TinymanV1Pool(
client=tinyman_v1_client,
asset_a=asset_in,
asset_b=asset_out,
fetch=True,
)
if v1_pool.exists and v1_pool.issued_liquidity:
v1_direct_route = Route(
asset_in=asset_in, asset_out=asset_out, pools=[v1_pool]
)
routes.append(v1_direct_route)

v2_pool = TinymanV2Pool(
client=tinyman_v2_client,
asset_a=asset_in,
asset_b=asset_out,
fetch=True,
)
if v2_pool.exists and v2_pool.issued_pool_tokens:
v2_direct_route = Route(asset_in=asset_in, asset_out=asset_out, pools=[v2_pool])
routes.append(v2_direct_route)

route = fetch_best_route_suggestion(
tinyman_client=tinyman_v2_client,
asset_in=asset_in,
asset_out=asset_out,
swap_type=swap_type,
amount=amount,
)
if len(route.pools) > 1:
routes.append(route)

return routes


def swap(asset_in_id, asset_out_id, amount, account):
algod = get_algod()

# tinyman_v1_client = TinymanTestnetClient(algod_client=algod, user_address=account["address"])
tinyman_v2_client = TinymanV2TestnetClient(
algod_client=algod, user_address=account["address"]
)

asset_in = tinyman_v2_client.fetch_asset(asset_in_id)
asset_out = tinyman_v2_client.fetch_asset(asset_out_id)

asset_amount_in = AssetAmount(asset_in, amount)
routes = fetch_routes(
tinyman_v1_client=None,
tinyman_v2_client=tinyman_v2_client,
asset_in=asset_in,
asset_out=asset_out,
swap_type="fixed-input",
amount=asset_amount_in.amount,
)

if routes:
print("Routes:")
for index, route in enumerate(routes, start=1):
print(index, route)
else:
print("There is no route available.")
return

print("Best Route:")
best_route = get_best_fixed_input_route(
routes=routes, amount_in=asset_amount_in.amount
)
print(best_route)

if best_route:
print("Quotes:")
quotes = best_route.get_fixed_input_quotes(amount_in=asset_amount_in.amount)
for index, quote in enumerate(quotes, start=1):
print(index, quote)
else:
print("Couldn't calculate the quotes.")
return

suggested_params = tinyman_v2_client.algod.suggested_params()

if len(quotes) > 1:
# Swap Router Flow
print("The best route is single hop swap. Prepare swap router transactions.")

# 1 - Transfer input to swap router app account
# 2 - Swap router app call
txn_group = best_route.prepare_swap_router_transactions_from_quotes(
quotes=quotes, suggested_params=suggested_params
)

# Swap router app account may require to opt in to some assets.
opt_in_required_asset_ids = get_swap_router_app_opt_in_required_asset_ids(
algod_client=tinyman_v2_client.algod,
router_app_id=tinyman_v2_client.router_app_id,
asset_ids=best_route.asset_ids,
)
if opt_in_required_asset_ids:
opt_in_txn_group = prepare_swap_router_asset_opt_in_transaction(
router_app_id=tinyman_v2_client.router_app_id,
asset_ids=opt_in_required_asset_ids,
user_address=account["address"],
suggested_params=suggested_params,
)
txn_group = opt_in_txn_group + txn_group

else:
# Direct Swap Flow
pool = best_route.pools[0]
quote = quotes[0]

if pool.client.version == "v1":
print(f"The best route is direct swap using {pool}.")
print("V1 swap flow is not handled in this example.")
return

elif pool.client.version == "v2":
print(f"The best route is direct swap using {pool}.")
txn_group = pool.prepare_swap_transactions_from_quote(
quote=quote,
user_address=account["address"],
suggested_params=suggested_params,
)
else:
raise NotImplementedError()

# User account may require to opt in to output asset.
if not tinyman_v2_client.asset_is_opted_in(
asset_id=asset_out_id, user_address=account["address"]
):
user_opt_in_txn_group = prepare_asset_optin_transactions(
asset_id=asset_out_id,
sender=account["address"],
suggested_params=suggested_params,
)
txn_group = user_opt_in_txn_group + txn_group

# Sign
txn_group.sign_with_private_key(account["address"], account["private_key"])

# Submit transactions to the network and wait for confirmation
txn_info = tinyman_v2_client.submit(txn_group, wait=True)
print("Transaction Info")
print(txn_info)

print(
f"\nCheck the transaction group on Algoexplorer: https://testnet.algoexplorer.io/tx/group/{quote_plus(txn_group.id)}"
)


if __name__ == "__main__":
# TODO: Set Account
account = {
"address": "ALGORAND_ADDRESS_HERE",
"private_key": "base64_private_key_here",
}

# TODO: Set asset ids
asset_in_id, asset_out_id = 0, 21582668

swap(asset_in_id, asset_out_id, 1_000_000, account)
7 changes: 5 additions & 2 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,12 @@
project_urls={
"Source": "https://github.com/tinyman/tinyman-py-sdk",
},
install_requires=["py-algorand-sdk >= 1.10.0"],
install_requires=["py-algorand-sdk >= 1.10.0", "requests >= 2.0.0"],
packages=setuptools.find_packages(),
python_requires=">=3.8",
package_data={"tinyman.v1": ["asc.json"], "tinyman.v2": ["amm_approval.map.json"]},
package_data={
"tinyman.v1": ["asc.json"],
"tinyman.v2": ["amm_approval.map.json", "swap_router_approval.map.json"],
},
include_package_data=True,
)
Empty file added tests/swap_router/__init__.py
Empty file.

0 comments on commit f2f45cf

Please sign in to comment.