Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

381 add ability to split native vs wrapped carbon orders #390

Merged
Merged
Show file tree
Hide file tree
Changes from 11 commits
Commits
Show all changes
72 commits
Select commit Hold shift + click to select a range
8cf0973
Add Carbon split trade function
Lesigh-3100 Feb 19, 2024
9fd2f73
Fix to split carbon trades
Lesigh-3100 Feb 19, 2024
b59b0df
Add advanced wrap/unwrap functionality
Lesigh-3100 Feb 21, 2024
0542d00
Update tests
Lesigh-3100 Feb 21, 2024
9a1f24e
Update routehandler.py
Lesigh-3100 Feb 21, 2024
afcebb5
Refactor & add tests
Lesigh-3100 Feb 21, 2024
1c9c2cb
Merge branch 'develop' into 381-add-ability-to-split-native-vs-wrappe…
Lesigh-3100 Feb 22, 2024
a332cff
Update routehandler.py
Lesigh-3100 Feb 22, 2024
67731c5
Add test
Lesigh-3100 Feb 22, 2024
74bb42e
Merge branch 'develop' into 381-add-ability-to-split-native-vs-wrappe…
mikewcasale Feb 22, 2024
12865ab
Add split route class & wrap_unwrap_processor class
Lesigh-3100 Feb 25, 2024
60d008d
Remove unused code
Lesigh-3100 Feb 28, 2024
43f5e09
Small refactor
Lesigh-3100 Feb 29, 2024
c31b0e7
Add test for processor, splitter, and move order of trade maximizer
Lesigh-3100 Mar 3, 2024
3105ef8
Move FlashloanTokenException Exception & Update tests
Lesigh-3100 Mar 4, 2024
0648144
Removed unused dependencies from tests & delete code that is no longe…
Lesigh-3100 Mar 5, 2024
7e79036
Update tests
Lesigh-3100 Mar 5, 2024
d102196
Remove extra test imports
Lesigh-3100 Mar 5, 2024
28eca1c
Update tests
Lesigh-3100 Mar 5, 2024
3264029
Update test
Lesigh-3100 Mar 5, 2024
106ffe1
Remove redundant test
Lesigh-3100 Mar 5, 2024
5e22f1a
Remove repeated code
Lesigh-3100 Mar 5, 2024
53f4d49
Update test
Lesigh-3100 Mar 5, 2024
329bdcc
Update splitter test
Lesigh-3100 Mar 5, 2024
b58026f
Update Tests
Lesigh-3100 Mar 6, 2024
b81fe8e
Update tests
Lesigh-3100 Mar 7, 2024
8c26c8b
Update tests
Lesigh-3100 Mar 10, 2024
204da52
Add assertions to WrapUnwrapProcessor test
Lesigh-3100 Mar 11, 2024
e9715d7
Update tests
Lesigh-3100 Mar 11, 2024
dab713a
Merge branch 'develop' into 381-add-ability-to-split-native-vs-wrappe…
Lesigh-3100 Mar 11, 2024
44d6bed
Revision (ongoing work)
Mar 11, 2024
b3d57f4
Semantic
Mar 13, 2024
549fdbf
Fix the trade-splitter function
Mar 13, 2024
3a16c2a
Update wrap_unwrap_processor.py
Lesigh-3100 Mar 13, 2024
d3aaa31
Undo the previous commit
Mar 13, 2024
70f7b2a
Fix the trade-splitter
Mar 14, 2024
5bf7eb1
General fixes
Mar 14, 2024
a509265
Ongoing work
Mar 17, 2024
486e4cf
Ongoing work
Mar 17, 2024
3185de2
Improve documentation
Mar 17, 2024
b528ceb
Minor
Mar 17, 2024
c5f5176
Clean test
Mar 17, 2024
0ce9f0f
Ongoing work
Mar 17, 2024
4282190
Fix tests
Mar 17, 2024
e0656f5
Simplify tests
Mar 17, 2024
489e3e5
Improve test
Mar 17, 2024
7181dc0
Improve test
Mar 17, 2024
b027a1c
Improve documentation
Mar 17, 2024
96f90cf
Remove redundant code in tests
Mar 17, 2024
b0ec837
Remove jupyter tests 070 and 071
Mar 17, 2024
da3b346
Improve tests
Mar 17, 2024
f674e8a
Clean tests
Mar 18, 2024
a0e881c
Clean tests
Mar 18, 2024
ef18aa3
Clean test
Mar 18, 2024
1e5e589
Clean test
Mar 18, 2024
d063ae3
Update NBTest_070_TestCarbonTradeSplitter.py
Lesigh-3100 Mar 18, 2024
8fb1dc3
Rename 'id' to 'token_type'
Mar 18, 2024
42aaa51
Improve documentation
Mar 18, 2024
4c2574f
Improve the tests
Mar 18, 2024
71d47d2
Minor cleanup
Mar 18, 2024
6f39882
Minor cleanup
Mar 18, 2024
ce7cb6a
Update NBTest_070_TestCarbonTradeSplitter.py
Lesigh-3100 Mar 19, 2024
5e35d44
Clear potential confusion in the code which determines the value of `…
Mar 19, 2024
a6c8536
Rename `token_type` to `pool_type`
Mar 19, 2024
f752162
Fix docstring format
Mar 19, 2024
4c7d829
Improve documentation
Mar 19, 2024
2c4ad1d
Merge branch 'add-test-cases-to-revision' of https://github.com/banco…
Mar 19, 2024
5bd9f92
Merge branch '381-revision' of https://github.com/bancorprotocol/fast…
Mar 19, 2024
287b4cf
Merge branch 'develop' of https://github.com/bancorprotocol/fastlane-…
Mar 19, 2024
08cc237
Fix types
Mar 19, 2024
b02aad9
Remove opsolete files
Mar 19, 2024
9d66e51
Delete test_061_TestWETHConversion.py
Lesigh-3100 Mar 19, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 19 additions & 12 deletions fastlane_bot/bot.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,13 +69,14 @@
TxHelpersBase,
TradeInstruction,
Univ3Calculator,
RouteStruct,
RouteStruct, WrapUnwrapProcessor,
)
from fastlane_bot.helpers.routehandler import maximize_last_trade_per_tkn
from fastlane_bot.tools.cpc import ConstantProductCurve as CPC, CPCContainer, T
from fastlane_bot.tools.optimizer import CPCArbOptimizer
from .config.constants import FLASHLOAN_FEE_MAP
from .events.interface import QueryInterface
from .helpers.carbon_trade_splitter import CarbonTradeSplitter
from .modes.pairwise_multi import FindArbitrageMultiPairwise
from .modes.pairwise_multi_all import FindArbitrageMultiPairwiseAll
from .modes.pairwise_multi_pol import FindArbitrageMultiPairwisePol
Expand Down Expand Up @@ -1019,9 +1020,13 @@ def _handle_trade_instructions(
flashloan_amount=flashloan_amount_wei,
)

# Split Carbon Orders
split_calculated_trade_instructions = CarbonTradeSplitter(ConfigObj=self.ConfigObj).split_carbon_trades(
barakman marked this conversation as resolved.
Show resolved Hide resolved
trade_instructions=calculated_trade_instructions)

# Encode the trade instructions
encoded_trade_instructions = tx_route_handler.custom_data_encoder(
calculated_trade_instructions
split_calculated_trade_instructions
)

# Get the deadline
Expand All @@ -1035,12 +1040,14 @@ def _handle_trade_instructions(
)
]
route_struct = maximize_last_trade_per_tkn(route_struct=route_struct)
if self.ConfigObj.ARB_CONTRACT_VERSION >= 10:
route_struct = tx_route_handler.add_wrap_or_unwrap_trades_to_route(
trade_instructions=calculated_trade_instructions,
route_struct=route_struct,
flashloan_struct=flashloan_struct,
)

route_struct_processed = WrapUnwrapProcessor(cfg=self.ConfigObj).add_wrap_or_unwrap_trades_to_route(trade_instructions=split_calculated_trade_instructions, route_struct=route_struct, flashloan_struct=flashloan_struct)

barakman marked this conversation as resolved.
Show resolved Hide resolved
route_struct = tx_route_handler.add_wrap_or_unwrap_trades_to_route_v4(
barakman marked this conversation as resolved.
Show resolved Hide resolved
trade_instructions=split_calculated_trade_instructions,
route_struct=route_struct,
flashloan_struct=flashloan_struct,
)

# Get the cids
cids = list({ti["cid"].split("-")[0] for ti in best_trade_instructions_dic})
Expand All @@ -1051,12 +1058,12 @@ def _handle_trade_instructions(
self._validate_and_submit_transaction_tenderly(
ConfigObj=self.ConfigObj,
flashloan_struct=flashloan_struct,
route_struct=route_struct,
route_struct=route_struct_processed,
src_amount=flashloan_amount_wei,
src_address=flashloan_token_address,
),
cids,
route_struct,
route_struct_processed,
log_dict,
)

Expand All @@ -1066,7 +1073,7 @@ def _handle_trade_instructions(
flashloan_amount=flashloan_amount_wei,
flashloan_token_symbol=fl_token_symbol,
flashloan_token_address=flashloan_token_address,
route_struct=route_struct,
route_struct=route_struct_processed,
best_trade_instructions_dic=best_trade_instructions_dic,
)

Expand All @@ -1076,7 +1083,7 @@ def _handle_trade_instructions(
# Return the validate and submit transaction
return (
tx_helpers.validate_and_submit_transaction(
route_struct=route_struct,
route_struct=route_struct_processed,
src_amt=flashloan_amount_wei,
src_address=flashloan_token_address,
expected_profit_gastkn=best_profit_gastkn,
Expand Down
2 changes: 2 additions & 0 deletions fastlane_bot/helpers/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
from .submithandler import TxSubmitHandler, TxSubmitHandlerBase
from .txhelpers import TxHelpers, TxHelper
from .univ3calc import Univ3Calculator
from .wrap_unwrap_processor import WrapUnwrapProcessor
from .carbon_trade_splitter import CarbonTradeSplitter
TxHelpersBase = TxHelpers


Expand Down
195 changes: 195 additions & 0 deletions fastlane_bot/helpers/carbon_trade_splitter.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,195 @@
import json
from typing import List, Dict

from fastlane_bot.helpers import TradeInstruction


class CarbonTradeSplitter:
NATIVE = "native"
WRAPPED = "wrapped"
NEITHER = "neither"

def __init__(self, ConfigObj):
self.ConfigObj = ConfigObj

def split_carbon_trades(
self, trade_instructions: List[TradeInstruction]
) -> List[TradeInstruction]:
"""Splits Carbon trades that cannot be aggregated into a single trade action.

This function split a single Carbon trade into multiple trades for cases that include a mix of tokens or different Carbon deployments.
For example NATIVE/WRAPPED -> TKN -> NATIVE -> TKN & WRAPPED -> TKN.

Args:
trade_instructions: The list of TradeInstruction objects.

Returns:
The processed list of TradeInstruction objects after splitting incompatible trades.

"""
new_trade_list = []
for trade in trade_instructions:
if not self._is_carbon_trade(trade):
new_trade_list.append(trade)
continue

carbon_exchanges = self._process_carbon_trades(trade)
self._create_new_trades_from_carbon_exchanges(
carbon_exchanges, trade, new_trade_list
)
barakman marked this conversation as resolved.
Show resolved Hide resolved

return new_trade_list

def _is_carbon_trade(self, trade: TradeInstruction) -> bool:
"""Checks if a trade is on a Carbon deployment.

Args:
trade: a single TradeInstruction object.

Returns:
True if the trade is on a Carbon deployment. False otherwise.

"""
return trade.exchange_name in self.ConfigObj.CARBON_V1_FORKS

def _get_real_tkn(self, token_address: str, token_type: str):
"""Returns the correct token address for the trade.

This function returns the real token address for the pool. If the token isn't the native/wrapped gas token address, it just returns the token.
If the token is native/wrapped, it will use the token_type to return the correct address.

Args:
token_address: the token address
token_type: the self.NATIVE, self.WRAPPED, or SELF.NEITHER

Returns:
The correct token address for the pool.

"""
if token_address not in [
self.ConfigObj.NATIVE_GAS_TOKEN_ADDRESS,
self.ConfigObj.WRAPPED_GAS_TOKEN_ADDRESS,
]:
return token_address
return (
self.ConfigObj.NATIVE_GAS_TOKEN_ADDRESS
if token_type == self.NATIVE
else self.ConfigObj.WRAPPED_GAS_TOKEN_ADDRESS
)
barakman marked this conversation as resolved.
Show resolved Hide resolved

def _process_carbon_trades(self, trade: TradeInstruction) -> Dict:
"""Processes

Process Carbon trades and organize them by exchange and token type.

Args:
trade: a single TradeInstruction object

Returns:
A dictionary containing a list of

"""
carbon_exchanges = {}

raw_tx_str = trade.raw_txs.replace("'", '"')

raw_txs = json.loads(raw_tx_str)

for _tx in raw_txs:
curve = trade.db.get_pool(cid=str(_tx["cid"]).split("-")[0])
exchange = curve.exchange_name
token_type = self._get_token_type(curve)

if exchange not in carbon_exchanges:
carbon_exchanges[exchange] = self._initialize_exchange_data()

self._update_exchange_data(
carbon_exchanges[exchange], token_type, _tx, trade
)

return carbon_exchanges

def _get_token_type(self, curve) -> str:
"""Determines if the trade uses native or wrapped gas tokens.

Args:
curve: the Pool object representing the curve.

Returns:
A string indicating if the curve contains native gas tokens, wrapped gas tokens, or neither.
barakman marked this conversation as resolved.
Show resolved Hide resolved

"""
if self.ConfigObj.NATIVE_GAS_TOKEN_ADDRESS in curve.get_tokens:
return self.NATIVE
elif self.ConfigObj.WRAPPED_GAS_TOKEN_ADDRESS in curve.get_tokens:
return self.WRAPPED
return self.NEITHER

def _initialize_exchange_data(self) -> Dict:
"""Initializes data structure for a new exchange."""
return {
self.NATIVE: self._new_trade_data_structure(),
self.WRAPPED: self._new_trade_data_structure(),
self.NEITHER: self._new_trade_data_structure(),
}

def _new_trade_data_structure(self) -> Dict:
"""Initializes a new trade data structure."""
return {
"raw_txs": [],
"amtin": 0,
"amtout": 0,
"_amtin_wei": 0,
"_amtout_wei": 0,
}

def _update_exchange_data(
self, exchange_data: Dict, token_type: str, _tx: Dict, trade: TradeInstruction
):
"""Combines like-trades.

Update exchange data with information from a transaction.

Args:
exchange_data: The dictionary containing trades for each Carbon deployment being traded through.
token_type: a string indicating if the pool contains native/wrapped gas token, or neither.
_tx: the TX dictionary containing trade details.
trade: the TradeInstruction object.

"""
_tx["tknin"] = self._get_real_tkn(trade.tknin, token_type)
_tx["tknout"] = self._get_real_tkn(trade.tknout, token_type)

data = exchange_data[token_type]
data["raw_txs"].append(_tx)
data["amtin"] += _tx["amtin"]
data["amtout"] += _tx["amtout"]
data["_amtin_wei"] += _tx["_amtin_wei"]
data["_amtout_wei"] += _tx["_amtout_wei"]
data["tknin"] = _tx["tknin"]
data["tknout"] = _tx["tknout"]

def _create_new_trades_from_carbon_exchanges(
self,
carbon_exchanges: Dict,
original_trade: TradeInstruction,
new_trade_list: List[TradeInstruction],
):
"""Creates new TradeInstruction instances from processed Carbon exchanges data.

This function adds trades that were added as a result of splitting Carbon trades.

Args:
carbon_exchanges: The list of Carbon deployments being traded through in this trade.
original_trade: The original TradeInstruction object, utilized here to pass the db & Config objects.
new_trade_list: The updated list of TradeInstruction objects with any trades that were added from the splitting process.

"""
for exchange, data in carbon_exchanges.items():
for token_type, trade_data in data.items():
barakman marked this conversation as resolved.
Show resolved Hide resolved
if trade_data["raw_txs"]:
trade_data["db"] = original_trade.db
trade_data["ConfigObj"] = original_trade.ConfigObj
trade_data["cid"] = trade_data["raw_txs"][0]["cid"]
trade_data["raw_txs"] = str(trade_data["raw_txs"])
new_trade_list.append(TradeInstruction(**trade_data))
Loading
Loading