def convert_tokens_multi(account, multi_address, token0_address, token1_address, iterations, attempts=18):
# check if conversion route exists or is disabled
routes_functions = json.load(open('./data/routes.json'))
if token0_address not in routes_functions[multi_address]['functions'].keys():
raise Exception("Route not available for {} to {} in {}".format(token0_address, token1_address, multi_address))
elif routes_functions[multi_address]['functions'][token0_address][0] == '#':
raise Exception("Route is disabled for {} to {} in {}".format(token0_address, token1_address, multi_address))
# get the cost required to convert tokens
cost = routes_functions[multi_address]['costs'][token0_address]
tokens_cost = cost * iterations * routes_functions[multi_address]['mints']
# python is so stupid sometimes
try:
decimal_places = len(str(cost).split('.')[1])
decimal_places = decimal_places if decimal_places <= 15 else 15
except (AttributeError, IndexError):
tokens_required = tokens_cost
else:
tokens_required = float(round(tokens_cost, decimal_places))
# check if the wallet has enough tokens to convert
if tokens_required > (tokens_balance := get_token_balance(token0_address, account.address)):
logging.error("Need {} more tokens".format(tokens_required - tokens_balance))
return False
# approve the tokens required to convert and determine how many loops
approve_token_spending(account, token0_address, multi_address, get_token_supply(token0_address, True))
loops = math.floor(iterations / routes_functions[multi_address]['max_iterations'])
if iterations % routes_functions[multi_address]['max_iterations'] != 0:
loops += 1
# start calling multi mints
for i in list(range(0, loops)):
# cancel the rest of this loop if the gas price is too damn high
if get_beacon_gas_prices('rapid', beacon_gasnow_cache_seconds) > rapid_gas_fee_limit:
logging.warning("Gas fees are too high")
return None
if i + 1 < loops or iterations == routes_functions[multi_address]['max_iterations']:
# do max iterations during loop
call_iterations = routes_functions[multi_address]['max_iterations']
else:
# on final loop run the remaining iterations
call_iterations = iterations % routes_functions[multi_address]['max_iterations']
# call the multi mint function with iterations based on tokens minted
call_function = routes_functions[multi_address]['functions'][token0_address]
multi_contract = load_contract(multi_address, load_contract_abi(multi_address))
try:
tx = getattr(multi_contract.functions, call_function)(call_iterations).build_transaction({
"from": account.address,
"nonce": get_nonce(account.address)
})
success = broadcast_transaction(account, tx, True, attempts)
except Exception as e:
if error := interpret_exception_message(e):
logging.error("{}. Failed to convert using {}".format(error, routes_functions[multi_address]['label']))
else:
if success:
logging.info("Called {}({}) from {}".format(
call_function,
call_iterations,
routes_functions[multi_address]['label']
))
else:
logging.warning("Failed to call {}({}) from {}".format(
call_function,
call_iterations,
routes_functions[multi_address]['label']
))
return False
return True
Description
Load a list of routes
Check if the source token has a route to the desired token
Check if the route is disabled
Check how many tokens are minted in one call
Fix float/decimal amounts
Check if the wallet has enough tokens required to convert
Approve the source token contract with spender as the multi contract address
Determine how many loops and iterations are needed to complete minting
Start the loop
Check if gas prices are reasonable
Get the function to call for the multi contract
Load the multi contract
Create a transaction and call the multi function with the desired amount