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

Class TxHelpers Revision (April 11) #542

Merged
merged 36 commits into from
Apr 19, 2024
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
126b85f
Class TxHelpers Revision
Apr 11, 2024
1f28579
Move coinbase tests on hold due to invalid RPC on github test-suite
Apr 11, 2024
949cb80
Move the tests on hold outside the tests folder
Apr 11, 2024
42b0897
Improve documentation
Apr 11, 2024
0f8ffc8
Improve documentation
Apr 11, 2024
2616de5
Remove unused code in utils.py
Apr 14, 2024
1d62d3e
Merge branch 'develop' of https://github.com/bancorprotocol/fastlane-…
Apr 15, 2024
337c5fc
Removed uneeded returning of `mgr`
Apr 16, 2024
e5e913d
Improve logging
Apr 17, 2024
ea61435
Minor
Apr 17, 2024
0cb658e
Minor
Apr 17, 2024
6b44890
Fix logging
Apr 17, 2024
bd7e5c8
Improve logging
Apr 18, 2024
fcdb3f8
Improve the handing of transaction receipts
Apr 18, 2024
f86ce6a
Improve receipt logging
Apr 18, 2024
72ea9a1
Remove bot run-continuous mode
Apr 18, 2024
f8653f1
Removed unused function
Apr 18, 2024
c40eada
Cleanup
Apr 18, 2024
733bde5
Fix declaration error
Apr 18, 2024
157b1e7
Do data-validation based on user input only (rather than enforcing it…
Apr 18, 2024
de97376
Remove input argument `tenderly_fork` in function `bot.run`
Apr 18, 2024
26f27e2
Remove unused `import` statements
Apr 18, 2024
3b31e45
Fix `bot` method in order to support tests
Apr 18, 2024
28e1b81
Remove unused functions
Apr 18, 2024
0c07cb9
Improve logging
Apr 18, 2024
e7a0db3
Remove irrelevant test 904
Apr 18, 2024
7046416
Fix `bot._handle_trade_instructions`
Apr 18, 2024
7768a24
Fix syntax
Apr 18, 2024
90d1a5e
Fix test 040
Apr 18, 2024
f6a4db3
Test 040 cleanup
Apr 18, 2024
e4acbf7
Simplify access-list creation
Apr 18, 2024
86672ef
Merge branch 'develop' of https://github.com/bancorprotocol/fastlane-…
Apr 18, 2024
c3528f4
Fix function `gas_strategy`
Apr 19, 2024
2bb926a
Improve logging
Apr 19, 2024
7f667c1
Disable access lists for private transactions
Apr 19, 2024
292dd58
Send `eth_sendPrivateTransaction` directly to Flashbots, in order to:
Apr 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
51 changes: 8 additions & 43 deletions fastlane_bot/bot.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,12 +62,10 @@
from fastlane_bot.helpers import (
TxRouteHandler,
TxHelpers,
TxHelpersBase,
TradeInstruction,
Univ3Calculator,
add_wrap_or_unwrap_trades_to_route,
split_carbon_trades,
submit_transaction_tenderly
split_carbon_trades
)
from fastlane_bot.helpers.routehandler import maximize_last_trade_per_tkn
from fastlane_bot.tools.cpc import ConstantProductCurve as CPC, CPCContainer, T
Expand Down Expand Up @@ -95,7 +93,7 @@ class CarbonBotBase:
the database manager.
TxRouteHandlerClass
ditto (default: TxRouteHandler).
TxHelpersClass: class derived from TxHelpersBase
TxHelpersClass:
ditto (default: TxHelpers).

"""
Expand All @@ -119,8 +117,6 @@ def __post_init__(self):
if self.ConfigObj is None:
self.ConfigObj = Config()

self.c = self.ConfigObj

assert (
self.polling_interval is None
), "polling_interval is now a parameter to run"
Expand All @@ -129,10 +125,7 @@ def __post_init__(self):
self.TxRouteHandlerClass = TxRouteHandler

if self.TxHelpersClass is None:
self.TxHelpersClass = TxHelpers(ConfigObj=self.ConfigObj)
assert issubclass(
self.TxHelpersClass.__class__, TxHelpersBase
), f"TxHelpersClass not derived from TxHelpersBase {self.TxHelpersClass}"
self.TxHelpersClass = TxHelpers(cfg=self.ConfigObj)

self.db = QueryInterface(ConfigObj=self.ConfigObj)
self.RUN_FLASHLOAN_TOKENS = [*self.ConfigObj.CHAIN_FLASHLOAN_TOKENS.values()]
Expand Down Expand Up @@ -1042,19 +1035,6 @@ def _handle_trade_instructions(

route_struct_maximized = maximize_last_trade_per_tkn(route_struct=route_struct_processed)

# Get the cids
cids = list({ti["cid"] for ti in best_trade_instructions_dic})

# Check if the network is tenderly and submit the transaction accordingly
if self.ConfigObj.NETWORK == self.ConfigObj.NETWORK_TENDERLY:
return submit_transaction_tenderly(
cfg=self.ConfigObj,
flashloan_struct=flashloan_struct,
route_struct=route_struct_maximized,
src_amount=flashloan_amount_wei,
src_address=flashloan_token_address,
)

# Log the route_struct
self.handle_logging_for_trade_instructions(
4, # The log id
Expand All @@ -1065,18 +1045,13 @@ def _handle_trade_instructions(
best_trade_instructions_dic=best_trade_instructions_dic,
)

# Get the tx helpers class
tx_helpers = TxHelpers(ConfigObj=self.ConfigObj)

# Return the validate and submit transaction
return tx_helpers.validate_and_submit_transaction(
return self.TxHelpersClass.validate_and_submit_transaction(
route_struct=route_struct_maximized,
src_amt=flashloan_amount_wei,
src_address=flashloan_token_address,
expected_profit_gastkn=best_profit_gastkn,
expected_profit_usd=best_profit_usd,
safety_override=False,
verbose=True,
log_object=log_dict,
flashloan_struct=flashloan_struct,
)
Expand Down Expand Up @@ -1320,33 +1295,23 @@ def run_single_mode(
randomizer=randomizer,
replay_mode=replay_mode,
)
if tx_hash and tx_hash[0]:
if tx_hash:
self.ConfigObj.logger.info(
f"[bot.run_single_mode] Arbitrage executed [hash={tx_hash}]"
)

# Write the tx hash to a file in the logging_path directory
if self.logging_path:
filename = f"successful_tx_hash_{datetime.now().strftime('%Y-%m-%d_%H-%M-%S')}.txt"
print(f"Writing tx_hash hash {tx_hash} to {filename}")
self.ConfigObj.logger.info(f"Writing tx hash {tx_hash} to {filename}")
with open(f"{self.logging_path}/{filename}", "w") as f:

# if isinstance(tx_hash[0], AttributeDict):
# f.write(str(tx_hash[0]))
# else:
for record in tx_hash:
f.write("\n")
f.write("\n")
try:
json.dump(record, f, indent=4)
except:
f.write(str(record))
f.write(tx_hash)

except self.NoArbAvailable as e:
self.ConfigObj.logger.warning(f"[NoArbAvailable] {e}")
except Exception as e:
self.ConfigObj.logger.error(f"[bot:run:single] {e}")
raise
raise e

def _ensure_connection(self, tenderly_fork: str):
"""
Expand Down
24 changes: 9 additions & 15 deletions fastlane_bot/config/network.py
Original file line number Diff line number Diff line change
Expand Up @@ -266,25 +266,10 @@ class ConfigNetwork(ConfigBase):

# DEFAULT VALUES SECTION
#######################################################################################
UNIV3_FEE_LIST = [80, 100, 250, 450, 500, 2500, 3000, 10000]
MIN_BNT_LIQUIDITY = 2_000_000_000_000_000_000
DEFAULT_GAS = 950_000
DEFAULT_GAS_PRICE = 0
DEFAULT_GAS_PRICE_OFFSET = 1.09
DEFAULT_GAS_SAFETY_OFFSET = 25_000
DEFAULT_POLL_INTERVAL = 12
DEFAULT_BLOCKTIME_DEVIATION = 13 * 500 * 100 # 10 block time deviation
DEFAULT_MAX_SLIPPAGE = Decimal("1") # 1%
_PROJECT_PATH = os.path.normpath(f"{os.getcwd()}") # TODO: FIX THIS
DEFAULT_CURVES_DATAFILE = os.path.normpath(
f"{_PROJECT_PATH}/carbon/data/curves.csv.gz"
)
CARBON_STRATEGY_CHUNK_SIZE = 200
Q96 = Decimal("2") ** Decimal("96")
DEFAULT_TIMEOUT = 60
CARBON_FEE = Decimal("0.002")
BANCOR_V3_FEE = Decimal("0.0")
DEFAULT_REWARD_PERCENT = Decimal("0.5")
LIMIT_BANCOR3_FLASHLOAN_TOKENS = True
DEFAULT_MIN_PROFIT_GAS_TOKEN = Decimal("0.02")

Expand All @@ -308,6 +293,15 @@ class ConfigNetwork(ConfigBase):
GAS_TKN_IN_FLASHLOAN_TOKENS = None
IS_NO_FLASHLOAN_AVAILABLE = False

# HOOKS
#######################################################################################
@staticmethod
def gas_strategy(web3):
return {
"maxFeePerGas": web3.eth.gas_price,
"maxPriorityFeePerGas": web3.eth.max_priority_fee
}

@classmethod
def new(cls, network=None):
"""
Expand Down
73 changes: 17 additions & 56 deletions fastlane_bot/config/provider.py
Original file line number Diff line number Diff line change
Expand Up @@ -112,51 +112,20 @@ def __init__(self, network: ConfigNetwork, **kwargs):
self.connection.connect_network()
self.w3 = self.connection.web3
self.w3_async = self.connection.w3_async
self.LOCAL_ACCOUNT = self.w3.eth.account.from_key(ETH_PRIVATE_KEY_BE_CAREFUL)


if network.NETWORK in [N.NETWORK_BASE, N.NETWORK_ETHEREUM, N.NETWORK_FANTOM, N.NETWORK_MANTLE]:
self.CARBON_CONTROLLER_CONTRACT = self.w3.eth.contract(
address=network.CARBON_CONTROLLER_ADDRESS,
abi=CARBON_CONTROLLER_ABI,
)
self.BANCOR_ARBITRAGE_CONTRACT = self.w3.eth.contract(
address=self.w3.to_checksum_address(network.FASTLANE_CONTRACT_ADDRESS),
abi=FAST_LANE_CONTRACT_ABI,
)

if network.GAS_ORACLE_ADDRESS:
self.GAS_ORACLE_CONTRACT = self.w3_async.eth.contract(
address=network.GAS_ORACLE_ADDRESS,
abi=GAS_ORACLE_ABI
)

self.BANCOR_ARBITRAGE_CONTRACT = self.w3.eth.contract(
address=self.w3.to_checksum_address(N.FASTLANE_CONTRACT_ADDRESS),
abi=FAST_LANE_CONTRACT_ABI,
)

if network.NETWORK in N.NETWORK_ETHEREUM:
self.BANCOR_NETWORK_INFO_CONTRACT = self.w3.eth.contract(
address=network.BANCOR_V3_NETWORK_INFO_ADDRESS,
abi=BANCOR_V3_NETWORK_INFO_ABI,
if N.GAS_ORACLE_ADDRESS:
self.GAS_ORACLE_CONTRACT = self.w3.eth.contract(
address=N.GAS_ORACLE_ADDRESS,
abi=GAS_ORACLE_ABI,
)
self.ARB_CONTRACT_VERSION = self.BANCOR_ARBITRAGE_CONTRACT.caller.version()

else:
self.CARBON_CONTROLLER_CONTRACT = None
self.ARB_CONTRACT_VERSION = 10

if self.BANCOR_ARBITRAGE_CONTRACT is not None:
try:
(
reward_percent,
max_profit,
) = self.BANCOR_ARBITRAGE_CONTRACT.caller.rewards()
self.ARB_REWARD_PERCENTAGE = str(int(reward_percent) / 1000000)
self.ARB_MAX_PROFIT = 1000000 # This is no longer used
except:
self.ARB_REWARD_PERCENTAGE = "0.5"
else:
self.ARB_REWARD_PERCENTAGE = "0.5"

self.EXPECTED_GAS_MODIFIER = "0.85"
self.ARB_CONTRACT_VERSION = self.BANCOR_ARBITRAGE_CONTRACT.caller.version()
self.ARB_REWARDS_PPM = self.BANCOR_ARBITRAGE_CONTRACT.caller.rewards()[0]


class _ConfigProviderTenderly(ConfigProvider):
Expand All @@ -182,26 +151,20 @@ def __init__(self, network: ConfigNetwork, **kwargs):
)
self.connection.connect_network()
self.w3 = self.connection.web3
self.LOCAL_ACCOUNT = self.w3.eth.account.from_key(ETH_PRIVATE_KEY_BE_CAREFUL)

self.BANCOR_NETWORK_INFO_CONTRACT = self.w3.eth.contract(
address=N.BANCOR_V3_NETWORK_INFO_ADDRESS,
abi=BANCOR_V3_NETWORK_INFO_ABI,
)
self.CARBON_CONTROLLER_CONTRACT = self.w3.eth.contract(
address=N.CARBON_CONTROLLER_ADDRESS,
abi=CARBON_CONTROLLER_ABI,
)
self.BANCOR_ARBITRAGE_CONTRACT = self.w3.eth.contract(
address=self.w3.to_checksum_address(N.FASTLANE_CONTRACT_ADDRESS),
abi=FAST_LANE_CONTRACT_ABI,
)
self.ARB_CONTRACT_VERSION = self.BANCOR_ARBITRAGE_CONTRACT.caller.version()

reward_percent, max_profit = self.BANCOR_ARBITRAGE_CONTRACT.caller.rewards()
if N.GAS_ORACLE_ADDRESS:
self.GAS_ORACLE_CONTRACT = self.w3.eth.contract(
address=N.GAS_ORACLE_ADDRESS,
abi=GAS_ORACLE_ABI,
)

self.ARB_REWARD_PERCENTAGE = str(int(reward_percent) / 1000000)
self.ARB_MAX_PROFIT = str(int(max_profit) / (10**18))
self.ARB_CONTRACT_VERSION = self.BANCOR_ARBITRAGE_CONTRACT.caller.version()
self.ARB_REWARDS_PPM = self.BANCOR_ARBITRAGE_CONTRACT.caller.rewards()[0]


class _ConfigProviderInfura(ConfigProvider):
Expand Down Expand Up @@ -232,6 +195,4 @@ def __init__(self, network: ConfigNetwork, **kwargs):
# raise NotImplementedError("Infura not implemented")
self.connection = None
self.w3 = None
self.BANCOR_NETWORK_INFO_CONTRACT = None
self.CARBON_CONTROLLER_CONTRACT = None
self.BANCOR_ARBITRAGE_CONTRACT = None
90 changes: 5 additions & 85 deletions fastlane_bot/events/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -578,8 +578,8 @@ def get_config(

Parameters
----------
default_min_profit_bnt : int or Decimal
The default minimum profit in BNT.
default_min_profit_gas_token : int or Decimal
The default minimum profit in the gas token.
limit_bancor3_flashloan_tokens : bool
Whether to limit the flashloan tokens to Bancor v3 pools.
loglevel : str
Expand Down Expand Up @@ -2001,93 +2001,13 @@ def handle_tokens_csv(mgr, prefix_path, read_only: bool = False):
)


def self_funding_warning_sequence(cfg):
"""
This function initiates a warning sequence if the user has specified to use their own funds.

:param cfg: the config object

"""
cfg.logger.info(
f"\n\n*********************************************************************************\n********************************* WARNING *********************************\n\n"
)
cfg.logger.info(
f"Arbitrage bot is set to use its own funds instead of using Flashloans.\n\n***** This could put your funds at risk. ******\nIf you did not mean to use this mode, cancel the bot now.\n\nOtherwise, the bot will submit token approvals IRRESPECTIVE OF CURRENT GAS PRICE for each token specified in Flashloan tokens.\n\n*********************************************************************************"
)
time.sleep(5)
cfg.logger.info(f"Submitting approvals in 15 seconds")
time.sleep(5)
cfg.logger.info(f"Submitting approvals in 10 seconds")
time.sleep(5)
cfg.logger.info(f"Submitting approvals in 5 seconds")
time.sleep(5)
cfg.logger.info(
f"*********************************************************************************\n\nSelf-funding mode activated."
)
cfg.logger.info(
f"""\n\n
_____
|A . | _____
| /.\ ||A ^ | _____
|(_._)|| / \ ||A _ | _____
| | || \ / || ( ) ||A_ _ |
|____V|| . ||(_'_)||( v )|
|____V|| | || \ / |
|____V|| . |
|____V|
\n\n"""
)


def find_unapproved_tokens(tokens: List, cfg, tx_helpers) -> List:
"""
This function checks if tokens have been previously approved from the wallet address to the Arbitrage contract.
If they are not already approved, it will submit approvals for each token specified in Flashloan tokens.
:param tokens: the list of tokens to check/approve
:param cfg: the config object
:param tx_helpers: the TxHelpers instantiated class

returns: List of tokens that have not been approved

"""
unapproved_tokens = []
for tkn in tokens:
if not tx_helpers.check_if_token_approved(token_address=tkn):
unapproved_tokens.append(tkn)
return unapproved_tokens


def check_and_approve_tokens(tokens: List, cfg) -> bool:
def check_and_approve_tokens(cfg: Config, tokens: List):
"""
This function checks if tokens have been previously approved from the wallet address to the Arbitrage contract.
If they are not already approved, it will submit approvals for each token specified in Flashloan tokens.

:param tokens: the list of tokens to check/approve
:param cfg: the config object
:param tokens: the list of tokens to check/approve

"""

tokens = [tkn for tkn in tokens if tkn != cfg.NATIVE_GAS_TOKEN_ADDRESS]

self_funding_warning_sequence(cfg=cfg)
tx_helpers = TxHelpers(ConfigObj=cfg)
unapproved_tokens = find_unapproved_tokens(
tokens=tokens, cfg=cfg, tx_helpers=tx_helpers
)

if len(unapproved_tokens) == 0:
return True

for _tkn in unapproved_tokens:
tx = tx_helpers.approve_token_for_arb_contract(token_address=_tkn)
if tx is not None:
continue
else:
assert (
False
), f"Failed to approve token: {_tkn}. This can be fixed by approving manually, or restarting the bot to try again."

unapproved_tokens = find_unapproved_tokens(
tokens=unapproved_tokens, cfg=cfg, tx_helpers=tx_helpers
)
return len(unapproved_tokens) == 0
TxHelpers(cfg=cfg).check_and_approve_tokens(tokens=tokens)
5 changes: 0 additions & 5 deletions fastlane_bot/helpers/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,7 @@
"""
from .tradeinstruction import TradeInstruction
from .routehandler import TxRouteHandler, RouteStruct
from .submithandler import submit_transaction_tenderly
from .txhelpers import TxHelpers
from .univ3calc import Univ3Calculator
from .wrap_unwrap_processor import add_wrap_or_unwrap_trades_to_route
from .carbon_trade_splitter import split_carbon_trades
TxHelpersBase = TxHelpers



Loading
Loading