Skip to content

Commit

Permalink
Merge pull request #10 from bancorprotocol/feature/read_reward_settin…
Browse files Browse the repository at this point in the history
…gs_from_arb_contract

Feature/read reward settings from arb contract
  • Loading branch information
mikewcasale authored May 23, 2023
2 parents b4e1fc7 + eaad8d8 commit f3c965c
Show file tree
Hide file tree
Showing 4 changed files with 64 additions and 28 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ _The Fast Lane is in beta. We cannot be held responsible for any losses caused

The Fast Lane is a tool developed to find and close arbitrage opportunities that occur on Bancor exchanges. This serves to increase market efficiency by ensuring Bancor liquidity is trading at rates similar to the rest of the market.

The permament URL for this repo is [https://github.com/bancorprotocol/fastlane-bot](https://github.com/bancorprotocol/fastlane-bot).
The permanent URL for this repo is [https://github.com/bancorprotocol/fastlane-bot](https://github.com/bancorprotocol/fastlane-bot).

## Getting started

Expand Down
14 changes: 13 additions & 1 deletion fastlane_bot/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@
from dotenv import load_dotenv

from fastlane_bot.data.abi import *
import web3
from web3 import Web3

load_dotenv()

Expand Down Expand Up @@ -79,7 +81,7 @@ class EthereumNetworkConstants:
DEFAULT_GAS_PRICE = 0
DEFAULT_GAS = 950000
DEFAULT_GAS_SAFETY_OFFSET = 25000
DEFAULT_GAS_PRICE_OFFSET = 1.05
DEFAULT_GAS_PRICE_OFFSET = 1.15
DEFAULT_REWARD_PERCENT = Decimal("0.5")

STAR_PRINT = "*" * 50
Expand Down Expand Up @@ -171,6 +173,16 @@ class EthereumNetworkConstants:
VALID_TENDERLY_NETWORKS = ["tenderly", "tenderly_mainnet"]
VALID_ETHEREUM_NETWORKS = ["mainnet", "ethereum"]

web3 = Web3(web3.HTTPProvider(ETHEREUM_MAINNET_PROVIDER))
BANCOR_ARBITRAGE_CONTRACT = web3.eth.contract(
address=web3.toChecksumAddress(FASTLANE_CONTRACT_ADDRESS),
abi=FAST_LANE_CONTRACT_ABI,
)
reward_percent, max_profit = BANCOR_ARBITRAGE_CONTRACT.caller.rewards()

ARB_REWARD_PERCENTAGE = str(int(reward_percent) / 1000000)
ARB_MAX_PROFIT = str(int(max_profit) / (10 ** 18))

def __post_init__(self):
"""
Note that autopopulated attributes below are usually expected to be derrived from the pairs.parquet file.
Expand Down
47 changes: 34 additions & 13 deletions fastlane_bot/helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -291,7 +291,7 @@ def build_transaction_with_gas(
self,
routes: List[Dict[str, Any]],
src_amt: int,
gas_price: int,
base_gas_price: int,
max_priority: int,
nonce: int,
):
Expand All @@ -309,20 +309,37 @@ def build_transaction_with_gas(
routes, ec.BNT_ADDRESS, src_amt
).build_transaction(
self.build_tx(
gas_price=gas_price, max_priority_fee=max_priority, nonce=nonce
base_gas_price=base_gas_price, max_priority_fee=max_priority, nonce=nonce
)
)
except ValueError as e:
message = str(e).split("baseFee: ")
split_fee = message[1].split(" (supplied gas ")
baseFee = int(int(split_fee[0]) * ec.DEFAULT_GAS_PRICE_OFFSET)
transaction = self.arb_contract.functions.flashloanAndArb(
routes, ec.BNT_ADDRESS, src_amt
).build_transaction(
self.build_tx(
gas_price=baseFee, max_priority_fee=max_priority, nonce=nonce
logger.error(f"{e.__class__.__name__} Error when building transaction: {e}")
if e.__class__.__name__ == "ContractLogicError":
logger.error(
f"Contract Logic error. This occurs when the transaction would fail & is likely due to stale pool data."
)
)
return None
if "max fee per gas less than block base fee" in str(e):
message = str(e)
split1 = message.split("maxFeePerGas: ")[1]
split2 = split1.split(" baseFee: ")
split_baseFee = int(
int(split2[1].split(" (supplied gas")[0])
)
split_maxPriorityFeePerGas = int(
int(split2[0]) * ec.DEFAULT_GAS_PRICE_OFFSET
)
transaction = self.arb_contract.functions.flashloanAndArb(
routes, ec.BNT_ADDRESS, src_amt
).build_transaction(
self.build_tx(
base_gas_price=split_baseFee,
max_priority_fee=split_maxPriorityFeePerGas,
nonce=nonce,
)
)
else:
return None

try:
estimated_gas = (
Expand All @@ -346,7 +363,7 @@ def get_nonce(self):
def build_tx(
self,
nonce: int,
gas_price: int = 0,
base_gas_price: int = 0,
max_priority_fee: int = None,
) -> Dict[str, Any]:
"""
Expand All @@ -357,9 +374,13 @@ def build_tx(
returns: the transaction to be submitted to the blockchain
"""
max_priority_fee = int(max_priority_fee)
base_gas_price = int(base_gas_price)
max_gas = base_gas_price + max_priority_fee

return {
"type": "0x2",
"maxFeePerGas": gas_price,
"maxFeePerGas": max_gas,
"maxPriorityFeePerGas": max_priority_fee,
"from": self.wallet_address,
"nonce": nonce,
Expand Down
29 changes: 16 additions & 13 deletions fastlane_bot/ui.py
Original file line number Diff line number Diff line change
Expand Up @@ -74,10 +74,10 @@ def execute(self):
return

# Current gas price estimate
current_gas_price = int(
self.get_eth_gas_price_alchemy() * ec.DEFAULT_GAS_PRICE_OFFSET
current_gas_price = int(self.get_eth_gas_price_alchemy())
current_max_priority_gas = int(
self.get_max_priority_fee_per_gas_alchemy() * ec.DEFAULT_GAS_PRICE_OFFSET
)
current_max_priority_gas = self.get_max_priority_fee_per_gas_alchemy()

# Use current Bancor V3 BNT/ETH liquidity to convert gas price to BNT
bnt, eth = self.get_bnt_eth_liquidity()
Expand All @@ -92,7 +92,7 @@ def execute(self):
logger.info(
f"\nRoute to execute: routes: {trade_path}, sourceAmount: {src_amt}, expected_profit {bnt_profit} \n\n"
)
logger.info(f"current gas price = {current_gas_price}")
logger.debug(f"current gas price = {current_gas_price}")
nonce = self.get_nonce()
if self.network_name in ec.VALID_TENDERLY_NETWORKS:
tx_receipt = self.build_transaction_tenderly(
Expand All @@ -107,7 +107,7 @@ def execute(self):
arb_tx = self.build_transaction_with_gas(
routes=trade_path,
src_amt=src_amt,
gas_price=current_gas_price,
base_gas_price=current_gas_price,
max_priority=current_max_priority_gas,
nonce=nonce,
)
Expand All @@ -120,9 +120,7 @@ def execute(self):
)
gas_estimate = int(gas_estimate + ec.DEFAULT_GAS_SAFETY_OFFSET)
logger.info(f"gas estimate = {gas_estimate}")
current_gas_price = int(
current_gas_price * ec.DEFAULT_GAS_PRICE_OFFSET
)
current_gas_price = int(current_gas_price)
# calculate the cost of gas in BNT
gas_in_bnt = self.estimate_gas_in_bnt(
gas_price=current_gas_price,
Expand All @@ -142,14 +140,19 @@ def execute(self):
f"current gas price = {current_gas_price}, breakeven gas price = {break_even_gas_price}, diff = {current_gas_price - break_even_gas_price}"
)

adjusted_reward = Decimal(
Decimal(bnt_profit) * ec.DEFAULT_REWARD_PERCENT
adjusted_reward = Decimal(str(bnt_profit)) * Decimal(
str(ec.ARB_REWARD_PERCENTAGE)
)
max_profit = Decimal(ec.ARB_MAX_PROFIT)

adjusted_reward = (
max_profit
if adjusted_reward > max_profit
else adjusted_reward
)

# The 0.81 below can be modified to adjust for the actual expected gas consumption, which is overestimated. Increase this for safer execution and lower it to increase execution frequency.
if adjusted_reward > (
gas_in_bnt * Decimal("0.81")
) and gas_in_bnt < Decimal("100"):
if adjusted_reward > (gas_in_bnt * Decimal("0.81")):
logger.info(
f"Expected profit of {bnt_profit} BNT vs cost of {gas_in_bnt} BNT in gas, executing"
)
Expand Down

0 comments on commit f3c965c

Please sign in to comment.