Skip to content

Commit

Permalink
Merge pull request #15 from skip-mev/v0.2.x
Browse files Browse the repository at this point in the history
✨ Add support for wyndex pools and routers, update burn fees for Hopers
  • Loading branch information
NotJeremyLiu authored Jan 29, 2023
2 parents d5ee3c2 + e8cf1b4 commit f63da38
Show file tree
Hide file tree
Showing 17 changed files with 2,130 additions and 246 deletions.
2,057 changes: 1,909 additions & 148 deletions contracts/juno_contracts.json

Large diffs are not rendered by default.

5 changes: 3 additions & 2 deletions envs/juno.env
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ MNEMONIC = "<YOUR MNEMONIC>"

# RPC URL to stream mempool and query contract state from
RPC_URL = "https://rpc-juno-ia.cosmosia.notional.ventures/"
#RPC_URL = "https://juno-rpc.reece.sh/"

# Rest URL to use to get a network client from cosmpy
REST_URL = "https://api-juno-ia.cosmosia.notional.ventures/"
Expand Down Expand Up @@ -50,8 +51,8 @@ AUCTION_HOUSE_ADDRESS = "juno10g0l3hd9sau3vnjrayjhergcpxemucxcspgnn4"
# 0.5 represents 50% of the profit, 1 represents 100% of the profit
AUCTION_BID_PROFIT_PERCENTAGE = 0.50

FACTORY_CONTRACTS = "{}"
ROUTER_CONTRACTS = "{}"
FACTORY_CONTRACTS = "{'wyndex': 'juno16adshp473hd9sruwztdqrtsfckgtd69glqm6sqk0hc4q40c296qsxl3u3s'}"
ROUTER_CONTRACTS = "{'wyndex': 'juno1pctfpv9k03v0ff538pz8kkw5ujlptntzkwjg6c0lrtqv87s9k28qdtl50w'}"

DECODER = "cosmwasm"
QUERIER = "cosmwasm"
Expand Down
3 changes: 2 additions & 1 deletion src/bot.py
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,7 @@ async def init(self):
print("Updating all pool contracts in state...")
await self.state.set_all_pool_contracts(
init_contracts=self.init_contracts,
router_contracts=self.router_contracts,
querier=self.querier,
creator=self.creator,
factory_contracts=self.factory_contracts,
Expand Down Expand Up @@ -302,7 +303,7 @@ def _retry(self, base64_encoded_bundle, bundle_signature) -> bool:
try:
# Continue sending bundles if we get a Not a Skip Validator error
if response.json()["result"]["code"] == NOT_A_SKIP_VAL_CODE:
logging.info("Not a skip val, retyring...")
logging.info("Not a skip val, retrying...")
time.sleep(DELAY_BETWEEN_SENDS)
return None
# If we get a 0 error code, we move on to the next transaction
Expand Down
8 changes: 6 additions & 2 deletions src/contract/factory/factories/terraswap.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
from dataclasses import dataclass

from src.contract import Factory
from src.querier import Querier

@dataclass
class Terraswap(Factory):
""" This class is a factory contract for TerraSwap."""

Expand All @@ -27,8 +30,9 @@ async def get_all_pairs(self, querier: Querier) -> list:
continue
if 'xyk' in pair['pair_type']:
filtered_pairs.append(pair)

return filtered_pairs

pair_addresses = [pair["contract_addr"] for pair in filtered_pairs]
return pair_addresses

async def _query_terraswap_factory(self,
querier: Querier,
Expand Down
3 changes: 2 additions & 1 deletion src/contract/pool/pools/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,5 @@
from .whitewhale import Whitewhale
from .astroport import Astroport
from .phoenix import Phoenix
from .hopers import Hopers
from .hopers import Hopers
from .wyndex import Wyndex
18 changes: 3 additions & 15 deletions src/contract/pool/pools/hopers.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,23 +6,11 @@
@dataclass
class Hopers(Junoswap):
DEFAULT_LP_FEE: float = 0.0
DEFAULT_PROTOCOL_FEE: float = 0.005
DEFAULT_PROTOCOL_FEE: float = 0.01
DEFAULT_FEE_FROM_INPUT: bool = True

async def update_fees(self, querier: Querier) -> None:
""" Updates the lp and protocol fees for the pool."""
payload = self.get_query_fees_payload(
contract_address=self.contract_address,
querier=querier)
try:
fee_info = await querier.query_node_and_return_response(
payload=payload,
decoded=True
)
protocol_fee = float(fee_info['total_fee_percent'])
except:
protocol_fee = self.DEFAULT_PROTOCOL_FEE

""" Updates the lp and protocol fees for the pool."""
self.lp_fee = self.DEFAULT_LP_FEE
self.protocol_fee = protocol_fee
self.protocol_fee = self.DEFAULT_PROTOCOL_FEE
self.fee_from_input = self.DEFAULT_FEE_FROM_INPUT
82 changes: 82 additions & 0 deletions src/contract/pool/pools/wyndex.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
from dataclasses import dataclass
from src.contract.pool.pools.terraswap import Terraswap
from src.querier.queriers.cosmwasm import CosmWasmQuerier
from src.contract.pool.pool import Pool
from src.transaction import Swap

from cosmpy.aerial.contract import create_cosmwasm_execute_msg
from cosmpy.protos.cosmos.base.v1beta1.coin_pb2 import Coin
from cosmpy.protos.cosmwasm.wasm.v1.tx_pb2 import MsgExecuteContract


@dataclass
class Wyndex(Terraswap):
DEFAULT_LP_FEE: float = 0.002
DEFAULT_PROTOCOL_FEE: float = 0.001
DEFAULT_FEE_FROM_INPUT: float = False

async def update_tokens(self,
querier: CosmWasmQuerier) -> None:
""" Update the tokens in the pool."""
payload = self.get_query_tokens_payload(
contract_address=self.contract_address,
querier=querier)
pool_info = await querier.query_node_and_return_response(
payload=payload,
decoded=True
)
self.token1_type = list(pool_info['assets'][0]['info'].keys())[0]
if self.token1_type == "token":
self.token1_denom = pool_info['assets'][0]['info'][self.token1_type]
elif self.token1_type == "native":
self.token1_denom = pool_info['assets'][0]['info'][self.token1_type]

self.token2_type = list(pool_info['assets'][1]['info'].keys())[0]
if self.token2_type == "token":
self.token2_denom = pool_info['assets'][1]['info'][self.token2_type]
elif self.token2_type == "native":
self.token2_denom = pool_info['assets'][1]['info'][self.token2_type]

def get_swaps_from_message(self,
msg,
message_value,
contracts: dict[str, Pool] = {}) -> list[Swap]:
""" Return list of Swaps from the message.
Expects that the pool object has already been chosen for send messages.
See: get_relevant_contract method in cosmwasm decoder.
"""
if "swap" in msg:
input_denom = msg['swap']['offer_asset']['info']['native']
return [Swap(sender=message_value.sender,
contract_address=self.contract_address,
input_denom=input_denom,
input_amount=int(msg['swap']['offer_asset']['amount']),
output_denom=self.get_other_denom(input_denom))]
elif "send" in msg:
input_denom=message_value.contract
return [Swap(sender=message_value.sender,
contract_address=self.contract_address,
input_denom=input_denom,
input_amount=int(msg['send']['amount']),
output_denom=self.get_other_denom(input_denom))]
else:
return []

def _get_swap_msg(self,
address: str,
input_amount: int) -> MsgExecuteContract:
""" Creates a MsgExecuteContract for JunoSwap's swap function."""
msg = create_cosmwasm_execute_msg(
sender_address=address,
contract_address=self.contract_address,
args={"swap": {
"offer_asset": {
"amount": str(input_amount),
"info": {
"native": self.input_denom
}}}}
)
msg.funds.append(Coin(amount=str(input_amount),
denom=self.input_denom))

return msg
2 changes: 1 addition & 1 deletion src/contract/router/__init__.py
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
from src.contract.router.router import Router
from src.contract.router.routers import TerraswapRouter, AstroportRouter, PhoenixRouter, WhiteWhaleRouter
from src.contract.router.routers import TerraswapRouter
7 changes: 6 additions & 1 deletion src/contract/router/router.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,9 @@ def get_swaps_from_message(self,
message_value,
contracts: dict) -> list[Swap]:
""" This method returns a list of swaps from a message.
"""
"""

@staticmethod
def _sort_and_combine_strings(str1: str, str2: str) -> str:
""" Sorts and combines 2 strings."""
return str1 + str2 if str1 < str2 else str2 + str1
5 changes: 1 addition & 4 deletions src/contract/router/routers/__init__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1 @@
from .terraswap import TerraswapRouter
from .astroport import AstroportRouter
from .phoenix import PhoenixRouter
from .whitewhale import WhiteWhaleRouter
from .terraswap import TerraswapRouter
15 changes: 0 additions & 15 deletions src/contract/router/routers/astroport.py

This file was deleted.

14 changes: 0 additions & 14 deletions src/contract/router/routers/phoenix.py

This file was deleted.

50 changes: 48 additions & 2 deletions src/contract/router/routers/terraswap.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,60 @@
from dataclasses import dataclass
import json
from base64 import b64decode
from dataclasses import dataclass, InitVar
from src.contract.pool.pool import Pool

from src.contract.router.router import Router
from src.swap import Swap


@dataclass
class TerraswapRouter(Router):
contracts: InitVar[dict]

def __post_init__(self, contracts: dict):
""" Loads the ABI for the contract, and creates a contract object."""

router_pools: dict[str, Pool] = {address: contracts[address]
for address
in contracts
if contracts[address].protocol == self.protocol
and isinstance(contracts[address], Pool)
}

self.pair_pool_mapping = {self._sort_and_combine_strings(pool.token1_denom, pool.token2_denom): pool.contract_address
for pool
in router_pools.values()
}

def get_swaps_from_message(self,
msg,
message_value,
contracts: dict) -> list[Swap]:
pass
swaps = []

if "execute_swap_operations" in msg:
operations = msg["execute_swap_operations"]["operations"]
first_input_amount = int(message_value.funds[0].amount) if message_value.funds else 0
elif "send" in msg:
operations_msg = json.loads(b64decode(msg['send']['msg']).decode('utf-8'))
first_input_amount = int(msg["send"]["amount"])
if "execute_swap_operations" in operations_msg:
operations = operations_msg["execute_swap_operations"]["operations"]
else:
return swaps

for index, operation in enumerate(operations):
input_amount = first_input_amount if index == 0 else 0
swap_info = list(operation.values())[0]
input_denom = list(swap_info['offer_asset_info'].values())[0]
output_denom = list(swap_info['ask_asset_info'].values())[0]
contract_address = self.pair_pool_mapping[self._sort_and_combine_strings(input_denom, output_denom)]
swaps.append(
Swap(sender=message_value.sender,
contract_address=contract_address,
input_denom=input_denom,
input_amount=input_amount,
output_denom=output_denom)
)

return swaps
14 changes: 0 additions & 14 deletions src/contract/router/routers/whitewhale.py

This file was deleted.

24 changes: 11 additions & 13 deletions src/creator/creator.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,7 @@
import src.contract.factory.factories as factories

from src.contract.router.router import Router
from src.contract.router.routers import (
TerraswapRouter,
AstroportRouter,
PhoenixRouter,
WhiteWhaleRouter
)
from src.contract.router.routers import TerraswapRouter

class Creator:

Expand Down Expand Up @@ -86,7 +81,8 @@ def create_pool(contract_address: str, pool: str) -> Pool:
"loop": pools.Loop,
"phoenix": pools.Phoenix,
"white_whale": pools.Whitewhale,
"hopers": pools.Hopers
"hopers": pools.Hopers,
"wyndex": pools.Wyndex,
}
return protocols[pool](contract_address, pool)

Expand All @@ -99,19 +95,21 @@ def create_factory(contract_address: str, protocol: str) -> Factory:
"terraswap": factories.Terraswap,
"astroport": factories.Terraswap,
"phoenix": factories.Terraswap,
"white_whale": factories.Terraswap
"white_whale": factories.Terraswap,
"wyndex": factories.Terraswap
}
return protocols[protocol](contract_address, protocol)

@staticmethod
def create_router(contract_address: str, router: str) -> Router:
def create_router(contract_address: str, router: str, contracts: dict) -> Router:
""" Factory function to create router contracts.
@DEV TODO: Add more router contracts here.
"""
routers = {
"terraswap": TerraswapRouter,
"astroport": AstroportRouter,
"phoenix": PhoenixRouter,
"white_whale": WhiteWhaleRouter
"astroport": TerraswapRouter,
"phoenix": TerraswapRouter,
"white_whale": TerraswapRouter,
"wyndex": TerraswapRouter
}
return routers[router](contract_address, router)
return routers[router](contract_address, router, contracts)
Loading

0 comments on commit f63da38

Please sign in to comment.