Skip to content

Commit

Permalink
Merge pull request #237 from bancorprotocol/2141_ontop_ethweth
Browse files Browse the repository at this point in the history
Major Update - handles upcoming changes to the Fast Lane smart contract
  • Loading branch information
mikewcasale authored Nov 29, 2023
2 parents 0105d7c + cff3f3a commit ed90df1
Show file tree
Hide file tree
Showing 25 changed files with 11,843 additions and 14,322 deletions.
50 changes: 19 additions & 31 deletions fastlane_bot/bot.py
Original file line number Diff line number Diff line change
Expand Up @@ -732,7 +732,6 @@ def calculate_profit(
CCm: CPCContainer,
best_profit: Decimal,
fl_token: str,
fl_token_with_weth: str,
) -> Tuple[Decimal, Decimal, Decimal]:
"""
Calculate the actual profit in USD.
Expand All @@ -754,13 +753,13 @@ def calculate_profit(
The updated best_profit, flt_per_bnt, and profit_usd.
"""
best_profit_fl_token = best_profit
if fl_token_with_weth != self.ConfigObj.WRAPPED_GAS_TOKEN_KEY:
if fl_token not in [self.ConfigObj.WRAPPED_GAS_TOKEN_KEY, self.ConfigObj.NATIVE_GAS_TOKEN_KEY]:
try:
fltkn_eth_conversion_rate = Decimal(str(CCm.bytknb(f"{self.ConfigObj.WRAPPED_GAS_TOKEN_KEY}").bytknq(f"{fl_token_with_weth}")[0].p))
fltkn_eth_conversion_rate = Decimal(str(CCm.bytknb(f"{self.ConfigObj.WRAPPED_GAS_TOKEN_KEY}").bytknq(f"{fl_token}")[0].p))
best_profit_eth = best_profit_fl_token * fltkn_eth_conversion_rate
except:
try:
fltkn_eth_conversion_rate = 1/Decimal(str(CCm.bytknb(f"{fl_token_with_weth}").bytknq(f"{self.ConfigObj.WRAPPED_GAS_TOKEN_KEY}")[0].p))
fltkn_eth_conversion_rate = 1/Decimal(str(CCm.bytknb(f"{fl_token}").bytknq(f"{self.ConfigObj.WRAPPED_GAS_TOKEN_KEY}")[0].p))
best_profit_eth = best_profit_fl_token * fltkn_eth_conversion_rate
except Exception as e:
raise str(e)
Expand Down Expand Up @@ -917,19 +916,15 @@ def _handle_trade_instructions(
)

# Get the flashloan token
fl_token = fl_token_with_weth = calculated_trade_instructions[0].tknin_key

# If the flashloan token is WETH, then use ETH
if fl_token == T.WETH:
fl_token = T.NATIVE_ETH
fl_token = calculated_trade_instructions[0].tknin_key

best_profit = flashloan_tkn_profit = tx_route_handler.calculate_trade_profit(
calculated_trade_instructions
)

# Use helper function to calculate profit
best_profit_fl_token, best_profit_eth, best_profit_usd = self.calculate_profit(
CCm, best_profit, fl_token, fl_token_with_weth
CCm, best_profit, fl_token
)

# Log the best trade instructions
Expand Down Expand Up @@ -981,10 +976,12 @@ def _handle_trade_instructions(
route_struct = [
asdict(rs)
for rs in tx_route_handler.get_route_structs(
encoded_trade_instructions, deadline
trade_instructions=encoded_trade_instructions, deadline=deadline
)
]
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)
# Check if the result is None
assert result is None, f"Unknown result requested {result}"

Expand Down Expand Up @@ -1223,16 +1220,17 @@ def setup_CCm(self, CCm: CPCContainer) -> CPCContainer:
"""
if CCm is None:
CCm = self.get_curves()
filter_out_weth = [
x
for x in CCm
if (x.params.exchange == "carbon_v1")
& (
(x.params.tkny_addr == self.ConfigObj.WETH_ADDRESS)
or (x.params.tknx_addr == self.ConfigObj.WETH_ADDRESS)
)
]
CCm = CPCContainer([x for x in CCm if x not in filter_out_weth])
if self.ConfigObj.ARB_CONTRACT_VERSION < 10:
filter_out_weth = [
x
for x in CCm
if (x.params.exchange == "carbon_v1")
& (
(x.params.tkny_addr == self.ConfigObj.WETH_ADDRESS)
or (x.params.tknx_addr == self.ConfigObj.WETH_ADDRESS)
)
]
CCm = CPCContainer([x for x in CCm if x not in filter_out_weth])
return CCm

def run_continuous_mode(
Expand All @@ -1256,16 +1254,6 @@ def run_continuous_mode(
while True:
try:
CCm = self.get_curves()
filter_out_weth = [
x
for x in CCm
if (x.params.exchange == "carbon_v1")
& (
(x.params.tkny_addr == self.ConfigObj.WETH_ADDRESS)
or (x.params.tknx_addr == self.ConfigObj.WETH_ADDRESS)
)
]
CCm = CPCContainer([x for x in CCm if x not in filter_out_weth])
tx_hash, cids, route_struct = self._run(
flashloan_tokens,
CCm,
Expand Down
4 changes: 2 additions & 2 deletions fastlane_bot/config/connect.py
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ def __init__(
self.network_name = network_name
self.provider_url = provider_url
self.provider_name = provider_name
self.web3 = Web3(Web3.HTTPProvider(provider_url))
self.web3 = Web3(Web3.HTTPProvider(provider_url, request_kwargs={'timeout': 60}))
self.nonce = nonce


Expand Down Expand Up @@ -167,6 +167,6 @@ def connect_network(self):
if self.is_connected:
return

self.web3 = Web3(Web3.HTTPProvider(self.provider_url))
self.web3 = Web3(Web3.HTTPProvider(self.provider_url, request_kwargs={'timeout': 60}))
logger.info(f"Connected to {self.network_id} network")
logger.info(f"Connected to {self.web3.provider.endpoint_uri} network")
30 changes: 26 additions & 4 deletions fastlane_bot/config/multicaller.py
Original file line number Diff line number Diff line change
Expand Up @@ -128,17 +128,39 @@ def add_call(self, fn: Callable, *args, **kwargs) -> None:
def multicall(self) -> List[Any]:
calls_for_aggregate = []
output_types_list = []

_calls_for_aggregate = {}
_output_types_list = {}
for fn in self._contract_calls:
fn_name = str(fn).split('functools.partial(<Function ')[1].split('>')[0]
calls_for_aggregate.append({
output_types = get_output_types_from_abi(self.contract.abi, fn_name)
if fn_name in _calls_for_aggregate:
_calls_for_aggregate[fn_name].append({
'target': self.contract.address,
'callData': fn()._encode_transaction_data()
})
output_types = get_output_types_from_abi(self.contract.abi, fn_name)
output_types_list.append(output_types)
_output_types_list[fn_name].append(output_types)
else:
_calls_for_aggregate[fn_name] = [{
'target': self.contract.address,
'callData': fn()._encode_transaction_data()
}]
_output_types_list[fn_name] = [output_types]

for fn_list in _calls_for_aggregate.keys():
calls_for_aggregate += (_calls_for_aggregate[fn_list])
output_types_list += (_output_types_list[fn_list])

w3 = self.contract.web3
_encoded_data = []
for fn_list in _calls_for_aggregate.keys():
_encoded_data.append(w3.eth.contract(
abi=MULTICALL_ABI,
address=self.MULTICALL_CONTRACT_ADDRESS
).functions.aggregate(_calls_for_aggregate[fn_list]).call(block_identifier=self.block_identifier))

if not isinstance(_encoded_data[0], list):
raise TypeError(f"Expected encoded_data to be a list, got {type(_encoded_data[0])} instead.")

encoded_data = w3.eth.contract(
abi=MULTICALL_ABI,
address=self.MULTICALL_CONTRACT_ADDRESS
Expand Down
2 changes: 1 addition & 1 deletion fastlane_bot/config/multiprovider.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ def __init__(self, abi: Dict, address: str, providers: Dict[str, str]):
self.contracts = {}

for name, url in providers.items():
w3 = Web3(Web3.HTTPProvider(url))
w3 = Web3(Web3.HTTPProvider(url, request_kwargs={'timeout': 60}))
self.contracts[name] = w3.eth.contract(address=address, abi=self.abi)

def __getattr__(self, name: str) -> Contract:
Expand Down
4 changes: 4 additions & 0 deletions fastlane_bot/config/network.py
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,9 @@ class ConfigNetwork(ConfigBase):
VELOCIMETER_V1_NAME = "velocimeter_v1"
VELOCIMETER_V2_NAME = "velocimeter_v2"

PLATFORM_NAME_WRAP_UNWRAP = "wrap_or_unwrap"
PLATFORM_ID_WRAP_UNWRAP = 10

EXCHANGE_IDS = {
BANCOR_V2_NAME: 1,
BANCOR_V3_NAME: 2,
Expand All @@ -222,6 +225,7 @@ class ConfigNetwork(ConfigBase):
CARBON_V1_NAME: 6,
BALANCER_NAME: 7,
CARBON_POL_NAME: 8,
PLATFORM_ID_WRAP_UNWRAP : 10
}

# SOLIDLY_V2_FORKS = [AERODROME_V3_NAME, VELOCIMETER_V2_NAME, SOLIDLY_V2_NAME]
Expand Down
27 changes: 21 additions & 6 deletions fastlane_bot/config/provider.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,8 +68,17 @@ def new(cls, network: ConfigNetwork, provider=None, **kwargs):

def __init__(self, network: ConfigNetwork, **kwargs):
super().__init__(**kwargs)
self.ARB_CONTRACT_VERSION = None
self.BANCOR_ARBITRAGE_CONTRACT = None
self.network = network

def check_version_of_arb_contract(self):
if self.BANCOR_ARBITRAGE_CONTRACT is not None:
try:
self.ARB_CONTRACT_VERSION = self.BANCOR_ARBITRAGE_CONTRACT.caller.version()
except Exception as e:
# Failed to get latest version of arbitrage contract
print(f"Failed to get latest version of arbitrage contract due to exception: {e}")

class _ConfigProviderAlchemy(ConfigProvider):
"""
Expand All @@ -83,7 +92,6 @@ def __init__(self, network: ConfigNetwork, **kwargs):
#assert self.network.NETWORK == ConfigNetwork.NETWORK_ETHEREUM, f"Alchemy only supports Ethereum {self.network}"
self.WEB3_ALCHEMY_PROJECT_ID = network.WEB3_ALCHEMY_PROJECT_ID
self.RPC_URL = f"{network.RPC_ENDPOINT}{self.WEB3_ALCHEMY_PROJECT_ID}"

N = self.network
self.connection = EthereumNetwork(
network_id=N.NETWORK_ID,
Expand All @@ -95,11 +103,7 @@ def __init__(self, network: ConfigNetwork, **kwargs):
self.w3 = self.connection.web3
self.LOCAL_ACCOUNT = self.w3.eth.account.from_key(ETH_PRIVATE_KEY_BE_CAREFUL)

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 network.NETWORK in [N.NETWORK_BASE, N.NETWORK_ETHEREUM]:
self.CARBON_CONTROLLER_CONTRACT = self.w3.eth.contract(
address=network.CARBON_CONTROLLER_ADDRESS,
Expand All @@ -110,9 +114,18 @@ def __init__(self, network: ConfigNetwork, **kwargs):
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,
)
self.ARB_CONTRACT_VERSION = self.BANCOR_ARBITRAGE_CONTRACT.caller.version()

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

if self.BANCOR_ARBITRAGE_CONTRACT is not None:
try:
Expand Down Expand Up @@ -159,6 +172,8 @@ def __init__(self, network: ConfigNetwork, **kwargs):
address=self.w3.toChecksumAddress(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()

self.ARB_REWARD_PERCENTAGE = str(int(reward_percent) / 1000000)
Expand Down
Loading

0 comments on commit ed90df1

Please sign in to comment.