├── .gitignore ├── README.md ├── config.py ├── files └── wallets.txt ├── main.py ├── requirements.txt └── vars.py /.gitignore: -------------------------------------------------------------------------------- 1 | /.idea 2 | 3 | /__pycache__ 4 | 5 | .DS_Store -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Bungee refueler 2 | 3 | ### Settings 4 | `files/wallets.txt` - Wallets with private keys \ 5 | `config.py` - Custom settings 6 | 7 | ### Run 8 | `pip3 install -r requirements.txt` \ 9 | `python3 main.py` 10 | 11 | ### Donate :) 12 | 13 | TRC-20 - `TX7yeJVHwhNsNy4ksF1pFRFnunF1aFRmet` \ 14 | ERC-20 - `0x5aa3c82045f944f5afa477d3a1d0be3c96196319` 15 | -------------------------------------------------------------------------------- /config.py: -------------------------------------------------------------------------------- 1 | RPCs = { 2 | 'Ethereum': 'https://eth.llamarpc.com', 3 | 'Optimism': 'https://rpc.ankr.com/optimism', 4 | 'BSC': 'https://rpc.ankr.com/bsc', 5 | 'Gnosis': 'https://rpc.gnosischain.com', 6 | 'Polygon': 'https://polygon.llamarpc.com', 7 | 'Fantom': 'https://rpc.fantom.network', 8 | 'Arbitrum': 'https://arb1.arbitrum.io/rpc', 9 | 'Avalanche': 'https://avalanche-c-chain.publicnode.com', 10 | 'zkSync': 'https://mainnet.era.zksync.io', 11 | 'zkEVM': 'https://rpc.ankr.com/polygon_zkevm', 12 | } 13 | 14 | ############################################################################################################### 15 | 16 | # Время ожидания между выполнением разных акков рандомное в указанном диапазоне 17 | NEXT_ADDRESS_MIN_WAIT_TIME = 0.5 # В минутах 18 | NEXT_ADDRESS_MAX_WAIT_TIME = 1.5 # В минутах 19 | 20 | # Время ожидания между транзакциями одного аккаунта 21 | NEXT_TX_MIN_WAIT_TIME = 6 # В секундах 22 | NEXT_TX_MAX_WAIT_TIME = 12 # В секундах 23 | 24 | # Максимальное кол-во попыток сделать запрос/транзакцию если они фейлятся 25 | MAX_TRIES = 3 26 | 27 | ############################################################################################################### 28 | 29 | # Кол-во потоков, в которых будет запускаться скрипт 30 | # В большинстве случаев можно оставить 1 поток и просто уменьшить время ожидания между акками 31 | PROCESSES_NUM = 1 32 | 33 | # Possible values: Ethereum, Optimism, BSC, Gnosis, Polygon, Fantom, Arbitrum, Avalanche, zkSync, zkEVM 34 | REFUEL_FROM = 'Avalanche' 35 | REFUEL_TO = ['Optimism', 'zkSync'] 36 | 37 | # Если стоит 0, то используются значения из диапазонов ниже 38 | # Если значение больше нуля, то рефуелится указанный процент от всего баланса 39 | # Лучше использовать только тогда, когда рефуел идет в одну сеть 40 | REFUEL_AMOUNT_PERCENTAGE = 95 41 | 42 | # Amount of network native token 43 | DEFAULT_MIN_REFUEL_AMOUNT = 0.35 44 | DEFAULT_MAX_REFUEL_AMOUNT = 0.35 45 | 46 | # Максимально возможное кол-во дополнительных рандомных refuel транзакции (итоговая сумма остается та же) 47 | MAX_RANDOM_TX_COUNT = 2 48 | # Минимальная сумма одного рандом рефуела 49 | RANDOM_TX_MIN_REFUEL_AMOUNT = 0.1 50 | 51 | # : (min_amount, max_amount) 52 | # (in native token) 53 | # Если для какой-то сети не указано, то для нее берутся значения DEFAULT_MIN_REFUEL_AMOUNT и DEFAULT_MAX_REFUEL_AMOUNT 54 | REFUEL_AMOUNT_BY_CHAIN = { 55 | 'Optimism': (0.3, 0.4), 56 | 'zkSync': (0.4, 0.4), 57 | } 58 | -------------------------------------------------------------------------------- /files/wallets.txt: -------------------------------------------------------------------------------- 1 | private_key1 or address1;private_key1 2 | private_key2 or address2;private_key2 -------------------------------------------------------------------------------- /main.py: -------------------------------------------------------------------------------- 1 | import random 2 | import time 3 | import requests 4 | import ua_generator 5 | 6 | import multiprocessing as mp 7 | from termcolor import cprint 8 | from enum import Enum 9 | from pathlib import Path 10 | from datetime import datetime 11 | from retry import retry 12 | from web3 import Web3 13 | 14 | from config import * 15 | from vars import * 16 | 17 | default_ua = ua_generator.generate(device='desktop', browser='chrome') 18 | default_headers = {'User-Agent': default_ua.text} 19 | proxies = {} 20 | try: 21 | for proxy in open('files/proxies.txt').readlines(): 22 | proxies = {'http': 'http://' + proxy, 'https': 'http://' + proxy} 23 | break 24 | except Exception: 25 | pass 26 | 27 | 28 | NATIVE_TOKEN = NATIVE_TOKENS[REFUEL_FROM] 29 | 30 | 31 | def decimal_to_int(d, n): 32 | return int(d * (10 ** n)) 33 | 34 | 35 | def int_to_decimal(i, n): 36 | return i / (10 ** n) 37 | 38 | 39 | def wait_next_tx(): 40 | time.sleep(random.uniform(NEXT_TX_MIN_WAIT_TIME, NEXT_TX_MAX_WAIT_TIME)) 41 | 42 | 43 | class RunnerException(Exception): 44 | 45 | def __init__(self, message, caused=None): 46 | super().__init__() 47 | self.message = message 48 | self.caused = caused 49 | 50 | def __str__(self): 51 | if self.caused: 52 | return self.message + ": " + str(self.caused) 53 | return self.message 54 | 55 | 56 | class Runner: 57 | class Status(Enum): 58 | ALREADY = 1 59 | SUCCESS = 2 60 | FAILED = 3 61 | 62 | @retry(tries=MAX_TRIES, delay=1.5, backoff=2, jitter=(0, 1)) 63 | def runner_wrapper(self, msg, func, *args, **kwargs): 64 | try: 65 | return func(*args, **kwargs) 66 | except Exception as e: 67 | raise RunnerException(msg, e) 68 | 69 | def __init__(self, w3, private_key, refuel_chain_info): 70 | self.w3 = w3 71 | self.private_key = private_key 72 | self.refuel_chain_info = refuel_chain_info 73 | 74 | account = w3.eth.account.from_key(private_key) 75 | self.address = account.address 76 | 77 | def _tx_verification(self, tx_hash, action_print): 78 | transaction_data = self.w3.eth.wait_for_transaction_receipt(tx_hash) 79 | if transaction_data.get('status') is not None and transaction_data.get('status') == 1: 80 | scan_link = tx_hash.hex() 81 | if REFUEL_FROM in SCANS: 82 | scan_link = SCANS[REFUEL_FROM] + '/tx/' + scan_link 83 | print(f'{self.address} | {action_print}Successful tx: {scan_link}') 84 | else: 85 | raise RunnerException(f'{action_print}Tx error: {transaction_data.get("transactionHash").hex()}') 86 | 87 | def tx_verification(self, tx_hash, action=None): 88 | action_print = action + ' - ' if action else '' 89 | return self.runner_wrapper( 90 | f'{action_print}Tx error: {tx_hash.hex()}', 91 | self._tx_verification, tx_hash, action_print 92 | ) 93 | 94 | @classmethod 95 | def _get_refuel_quote(cls, from_chain_id, to_chain_id, amount): 96 | resp = requests.get(f'https://refuel.socket.tech/quote?' 97 | f'fromChainId={from_chain_id}&' 98 | f'toChainId={to_chain_id}&' 99 | f'amount={amount}', headers=default_headers, proxies=proxies) 100 | if resp.status_code != 200: 101 | raise RunnerException(f'status_code = {resp.status_code}, response = {resp.text}') 102 | 103 | json_resp = resp.json() 104 | if not json_resp['success']: 105 | return False, '', '' 106 | 107 | return True, json_resp['result']['contractAddress'], json_resp['result']['estimatedTime'] 108 | 109 | def get_refuel_quote(self, from_chain, to_chain, amount): 110 | return self.runner_wrapper('Get refuel quote failed', self._get_refuel_quote, 111 | CHAIN_IDS[from_chain], CHAIN_IDS[to_chain], amount) 112 | 113 | def get_limit(self, from_chain, to_chain): 114 | from_chain_id, to_chain_id = CHAIN_IDS[from_chain], CHAIN_IDS[to_chain] 115 | for limit in self.refuel_chain_info['limits']: 116 | if limit['chainId'] == to_chain_id: 117 | if limit['isEnabled']: 118 | return int(limit['minAmount']), int(limit['maxAmount']) 119 | else: 120 | return -1, -1 121 | return -1, -1 122 | 123 | def _refuel(self, contract_address, to_chain_id, amount_int): 124 | contract = self.w3.eth.contract(self.w3.to_checksum_address(contract_address), abi=REFUEL_CONTRACT_ABI) 125 | tx = contract.functions.depositNativeToken(to_chain_id, self.address).build_transaction({ 126 | 'from': self.address, 127 | 'value': amount_int, 128 | 'gasPrice': self.w3.eth.gas_price, 129 | 'nonce': self.w3.eth.get_transaction_count(self.address) 130 | }) 131 | estimate = self.w3.eth.estimate_gas(tx) 132 | tx['gas'] = estimate 133 | 134 | signed_tx = self.w3.eth.account.sign_transaction(tx, self.private_key) 135 | tx_hash = self.w3.eth.send_raw_transaction(signed_tx.rawTransaction) 136 | self.tx_verification( 137 | tx_hash, 138 | action=f'Refuel of {int_to_decimal(amount_int, NATIVE_TOKEN_DECIMAL)} ${NATIVE_TOKEN} ' 139 | f'to {CHAIN_NAMES[to_chain_id]}' 140 | ) 141 | 142 | def refuel(self, contract_address, to_chain, amount): 143 | to_chain_id = CHAIN_IDS[to_chain] 144 | return self.runner_wrapper(f'Refuel to {to_chain} failed', self._refuel, contract_address, to_chain_id, amount) 145 | 146 | def run(self, to_chain): 147 | balance_int = self.w3.eth.get_balance(self.address) 148 | balance = int_to_decimal(balance_int, NATIVE_TOKEN_DECIMAL) 149 | 150 | min_amount, max_amount = get_chain_amount_range(to_chain) 151 | 152 | valuable_decimals = NATIVE_TOKEN_VALUABLE_DECIMALS[REFUEL_FROM] 153 | 154 | if REFUEL_AMOUNT_PERCENTAGE == 0: 155 | if min_amount > balance: 156 | raise RunnerException(f'Insufficient balance for min amount refuel to {to_chain}') 157 | amount = random.uniform(min_amount, min(max_amount, balance)) 158 | else: 159 | amount = balance * REFUEL_AMOUNT_PERCENTAGE / 100.0 160 | 161 | amount = round(amount, random.randint(valuable_decimals, valuable_decimals + 2)) 162 | amount_int = decimal_to_int(amount, NATIVE_TOKEN_DECIMAL) 163 | 164 | allowed, contract_address, estimated_time = self.get_refuel_quote(REFUEL_FROM, to_chain, amount_int) 165 | if not allowed: 166 | raise RunnerException(f'Refuel of {amount} ${NATIVE_TOKEN} is not allowed to {to_chain}') 167 | 168 | min_limit_int, max_limit_int = self.get_limit(REFUEL_FROM, to_chain) 169 | if min_limit_int == -1 and max_limit_int == -1: 170 | raise RunnerException(f'Refuel limits to {to_chain} error') 171 | 172 | random_tx = 0 173 | max_random_tx = random.randint(0, MAX_RANDOM_TX_COUNT) 174 | random_tx_min_amount = decimal_to_int(RANDOM_TX_MIN_REFUEL_AMOUNT, NATIVE_TOKEN_DECIMAL) 175 | while amount_int >= min_limit_int: 176 | if min_limit_int * 2 > amount_int: 177 | refuel_amount_int = amount_int 178 | elif random_tx < max_random_tx and random_tx_min_amount <= amount_int - min_limit_int: 179 | random_tx_max = min(amount_int - min_limit_int, max_limit_int) 180 | random_tx_min = min(random_tx_min_amount, random_tx_max) 181 | refuel_amount_int = random.randint(random_tx_min, random_tx_max) 182 | random_tx += 1 183 | elif amount_int > max_limit_int and amount_int - max_limit_int < min_limit_int: 184 | refuel_amount_int = amount_int - min_limit_int 185 | else: 186 | refuel_amount_int = min(amount_int, max_limit_int) 187 | self.refuel(contract_address, to_chain, refuel_amount_int) 188 | amount_int -= refuel_amount_int 189 | wait_next_tx() 190 | 191 | cprint(f'{self.address} | Refuel of {amount} ${NATIVE_TOKEN} to {to_chain} started, ' 192 | f'estimated time = ~{estimated_time / 1000} seconds', 'green') 193 | 194 | return Runner.Status.SUCCESS 195 | 196 | 197 | def get_chain_amount_range(to_chain): 198 | if to_chain in REFUEL_AMOUNT_BY_CHAIN: 199 | min_amount, max_amount = REFUEL_AMOUNT_BY_CHAIN[to_chain] 200 | else: 201 | min_amount, max_amount = DEFAULT_MIN_REFUEL_AMOUNT, DEFAULT_MAX_REFUEL_AMOUNT 202 | return min_amount, max_amount 203 | 204 | 205 | def ensure_refuel_limits(): 206 | resp = requests.get('https://refuel.socket.tech/chains', headers=default_headers, proxies=proxies) 207 | if resp.status_code != 200: 208 | raise Exception(f'status_code = {resp.status_code}, response = {resp.text}') 209 | 210 | json_resp = resp.json() 211 | if not json_resp['success']: 212 | raise Exception(f'unsuccessful, response = {resp.text}') 213 | 214 | chain_info = None 215 | for chain in json_resp['result']: 216 | if chain['name'] == REFUEL_FROM: 217 | chain_info = chain 218 | break 219 | 220 | if chain_info is None: 221 | raise Exception(f'unsupported chain: {REFUEL_FROM}') 222 | 223 | for to_chain in REFUEL_TO: 224 | to_chain_id = CHAIN_IDS[to_chain] 225 | min_amount, max_amount = get_chain_amount_range(to_chain) 226 | if min_amount > max_amount: 227 | raise Exception(f'min amount > max amount for {to_chain} in config') 228 | min_amount = decimal_to_int(min_amount, NATIVE_TOKEN_DECIMAL) 229 | max_amount = decimal_to_int(max_amount, NATIVE_TOKEN_DECIMAL) 230 | random_tx_min_amount = decimal_to_int(RANDOM_TX_MIN_REFUEL_AMOUNT, NATIVE_TOKEN_DECIMAL) 231 | for limit in chain_info['limits']: 232 | if limit['chainId'] == to_chain_id: 233 | if not limit['isEnabled']: 234 | raise Exception(f'refuel from {REFUEL_FROM} to {to_chain} is not enabled') 235 | min_limit, max_limit = int(limit['minAmount']), int(limit['maxAmount']) 236 | if random_tx_min_amount < min_limit: 237 | raise Exception(f'random tx min amount is lower that refuel to {to_chain} min value ' 238 | f'{round(int_to_decimal(min_limit, NATIVE_TOKEN_DECIMAL) + 0.00005, 4)} ' 239 | f'${NATIVE_TOKEN}.\n' 240 | f'Check https://www.bungee.exchange/refuel to get correct limits') 241 | if min_amount < min_limit: 242 | raise Exception(f'min amount for {to_chain} in config is lower than refuel min value ' 243 | f'{round(int_to_decimal(min_limit, NATIVE_TOKEN_DECIMAL) + 0.00005, 4)} ' 244 | f'${NATIVE_TOKEN}.\n' 245 | f'Check https://www.bungee.exchange/refuel to get correct limits') 246 | if max_amount > max_limit: 247 | cprint(f'Max amount for {to_chain} in config is greater than refuel max value ' 248 | f'{round(int_to_decimal(max_limit, NATIVE_TOKEN_DECIMAL) - 0.00005, 4)} ${NATIVE_TOKEN}.\n' 249 | f'You can continue to refuel with multiple transactions\n' 250 | f'or stop and check https://www.bungee.exchange/refuel to get correct limits.\n' 251 | f'Continue? (Y/n):', 252 | 'yellow', end=' ') 253 | answer = input() 254 | if answer != 'Y': 255 | raise Exception(f'you aborted refuel with multiple transactions') 256 | break 257 | 258 | return chain_info 259 | 260 | 261 | results_path = 'results/' + datetime.now().strftime('%d-%m-%Y-%H-%M-%S') 262 | 263 | 264 | def clear_results(): 265 | for c in REFUEL_TO: 266 | Path(f'{results_path}/to_{c}').mkdir(parents=True, exist_ok=True) 267 | 268 | 269 | def write_listener(q): 270 | while True: 271 | m = q.get() 272 | if m == 'Finished': 273 | break 274 | (row, to_chain, status) = m 275 | with open(f'{results_path}/to_{to_chain}/{status.name}.txt', 'a') as file: 276 | file.write(f'{row}\n') 277 | file.flush() 278 | 279 | 280 | def log_run(q, _row, _to_chain, _status, msg=''): 281 | address = 'None' if _row.find(';') == -1 else _row.split(';')[0] 282 | print_msg = address + ' |' 283 | if _status == Runner.Status.ALREADY: 284 | cprint(f'{print_msg} Already done', 'yellow') 285 | elif _status == Runner.Status.SUCCESS: 286 | cprint(f'{print_msg} Run success', 'green') 287 | else: 288 | cprint(f'{print_msg} Run failed: {msg}', 'red') 289 | q.put((_row, _to_chain, _status)) 290 | 291 | 292 | def pool_worker(pid, q, batch, refuel_chain_info): 293 | w3 = Web3(Web3.HTTPProvider(RPCs[REFUEL_FROM])) 294 | 295 | random.shuffle(batch) 296 | 297 | first = True 298 | for idx, (account_row, to_chain) in enumerate(batch): 299 | if account_row[0] == '#': 300 | continue 301 | 302 | if not first: 303 | wait = random.randint( 304 | int(NEXT_ADDRESS_MIN_WAIT_TIME * 60), 305 | int(NEXT_ADDRESS_MAX_WAIT_TIME * 60) 306 | ) 307 | cprint('\n#########################################\n#', 'cyan', end='') 308 | cprint(f'Process #{pid}. Done: {idx}/{len(batch)}'.center(39), 'magenta', end='') 309 | cprint('#\n#########################################\n# ', 'cyan', end='') 310 | cprint('Waiting for next run for {:.2f} minutes'.format(wait / 60), 'magenta', end='') 311 | cprint(' #\n#########################################\n', 'cyan') 312 | time.sleep(wait) 313 | first = False 314 | 315 | if account_row.find(';') == -1: 316 | current_key = account_row 317 | else: 318 | current_key = account_row.split(';')[1] 319 | 320 | try: 321 | runner = Runner(w3, current_key, refuel_chain_info) 322 | current_address = runner.address 323 | if account_row.find(';') == -1: 324 | account_row = current_address + ';' + current_key 325 | status = runner.run(to_chain) 326 | log_run(q, account_row, to_chain, status) 327 | except Exception as run_ex: 328 | log_run(q, account_row, to_chain, Runner.Status.FAILED, str(run_ex)) 329 | 330 | 331 | def main(): 332 | clear_results() 333 | 334 | try: 335 | refuel_chain_info = ensure_refuel_limits() 336 | except Exception as _e: 337 | cprint(f'Check refuel limits failed: {str(_e)}', 'red') 338 | exit(1) 339 | 340 | random.seed(datetime.now().timestamp()) 341 | 342 | with open('files/wallets.txt', 'r') as f: 343 | data = f.read().splitlines() 344 | 345 | random.shuffle(data) 346 | 347 | batches = [[] for _ in range(PROCESSES_NUM)] 348 | for i, row in enumerate(data): 349 | batches[i % PROCESSES_NUM] += [(row, c) for c in REFUEL_TO] 350 | 351 | manager = mp.Manager() 352 | queue = manager.Queue() 353 | pool = mp.Pool(PROCESSES_NUM + 1) 354 | 355 | watcher = pool.apply_async(write_listener, (queue,)) 356 | 357 | jobs = [] 358 | for i in range(PROCESSES_NUM): 359 | job = pool.apply_async(pool_worker, (i + 1, queue, batches[i], refuel_chain_info)) 360 | jobs.append(job) 361 | 362 | for job in jobs: 363 | job.get() 364 | 365 | cprint('\n#########################################\n#', 'cyan', end='') 366 | cprint(f'Finished'.center(39), 'magenta', end='') 367 | cprint('#\n#########################################', 'cyan') 368 | 369 | queue.put('Finished') 370 | pool.close() 371 | pool.join() 372 | 373 | 374 | if __name__ == '__main__': 375 | main() 376 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | Requests==2.31.0 2 | retry==0.9.2 3 | termcolor==2.3.0 4 | ua_generator==0.1.8 5 | web3==6.0.0 6 | -------------------------------------------------------------------------------- /vars.py: -------------------------------------------------------------------------------- 1 | SCANS = { 2 | 'Ethereum': 'https://etherscan.io', 3 | 'BSC': 'https://bscscan.com', 4 | 'Polygon': 'https://polygonscan.com', 5 | 'Avalanche': 'https://snowtrace.io', 6 | 'Fantom': 'https://ftmscan.com', 7 | 'Optimism': 'https://optimistic.etherscan.io', 8 | 'Arbitrum': 'https://arbiscan.io', 9 | 'Gnosis': 'https://gnosisscan.io', 10 | 'zkSync': 'https://explorer.zksync.io', 11 | 'zkEVM': 'https://zkevm.polygonscan.com', 12 | } 13 | 14 | CHAIN_IDS = { 15 | 'Ethereum': 1, 16 | 'Optimism': 10, 17 | 'BSC': 56, 18 | 'Gnosis': 100, 19 | 'Polygon': 137, 20 | 'Fantom': 250, 21 | 'Arbitrum': 42161, 22 | 'Avalanche': 43114, 23 | 'zkSync': 324, 24 | 'zkEVM': 1101, 25 | } 26 | 27 | CHAIN_NAMES = { 28 | 1: 'Ethereum', 29 | 10: 'Optimism', 30 | 56: 'BSC', 31 | 100: 'Gnosis', 32 | 137: 'Polygon', 33 | 250: 'Fantom', 34 | 42161: 'Arbitrum', 35 | 43114: 'Avalanche', 36 | 324: 'zkSync', 37 | 1101: 'zkEVM', 38 | } 39 | 40 | NATIVE_TOKENS = { 41 | 'Ethereum': 'ETH', 42 | 'Optimism': 'ETH', 43 | 'BSC': 'BNB', 44 | 'Gnosis': 'xDAI', 45 | 'Polygon': 'MATIC', 46 | 'Fantom': 'FTM', 47 | 'Arbitrum': 'ETH', 48 | 'Avalanche': 'AVAX', 49 | 'zkSync': 'ETH', 50 | 'zkEVM': 'ETH', 51 | } 52 | 53 | NATIVE_TOKEN_VALUABLE_DECIMALS = { 54 | 'Ethereum': 5, 55 | 'Optimism': 5, 56 | 'BSC': 4, 57 | 'Gnosis': 2, 58 | 'Polygon': 2, 59 | 'Fantom': 2, 60 | 'Arbitrum': 5, 61 | 'Avalanche': 3, 62 | 'zkSync': 5, 63 | 'zkEVM': 5, 64 | } 65 | 66 | NATIVE_TOKEN_DECIMAL = 18 67 | 68 | REFUEL_CONTRACT_ABI = '[{"inputs":[],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"destinationReceiver","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":true,"internalType":"uint256","name":"destinationChainId","type":"uint256"}],"name":"Deposit","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"sender","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Donation","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"sender","type":"address"}],"name":"GrantSender","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"account","type":"address"}],"name":"Paused","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"sender","type":"address"}],"name":"RevokeSender","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"receiver","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"bytes32","name":"srcChainTxHash","type":"bytes32"}],"name":"Send","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"account","type":"address"}],"name":"Unpaused","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"receiver","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Withdrawal","type":"event"},{"inputs":[{"components":[{"internalType":"uint256","name":"chainId","type":"uint256"},{"internalType":"bool","name":"isEnabled","type":"bool"}],"internalType":"struct GasMovr.ChainData[]","name":"_routes","type":"tuple[]"}],"name":"addRoutes","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address payable[]","name":"receivers","type":"address[]"},{"internalType":"uint256[]","name":"amounts","type":"uint256[]"},{"internalType":"bytes32[]","name":"srcChainTxHashes","type":"bytes32[]"},{"internalType":"uint256","name":"perUserGasAmount","type":"uint256"},{"internalType":"uint256","name":"maxLimit","type":"uint256"}],"name":"batchSendNativeToken","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"chainConfig","outputs":[{"internalType":"uint256","name":"chainId","type":"uint256"},{"internalType":"bool","name":"isEnabled","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"destinationChainId","type":"uint256"},{"internalType":"address","name":"_to","type":"address"}],"name":"depositNativeToken","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"chainId","type":"uint256"}],"name":"getChainData","outputs":[{"components":[{"internalType":"uint256","name":"chainId","type":"uint256"},{"internalType":"bool","name":"isEnabled","type":"bool"}],"internalType":"struct GasMovr.ChainData","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"sender","type":"address"}],"name":"grantSenderRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"paused","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"name":"processedHashes","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"sender","type":"address"}],"name":"revokeSenderRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address payable","name":"receiver","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"bytes32","name":"srcChainTxHash","type":"bytes32"},{"internalType":"uint256","name":"perUserGasAmount","type":"uint256"},{"internalType":"uint256","name":"maxLimit","type":"uint256"}],"name":"sendNativeToken","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"senders","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"chainId","type":"uint256"},{"internalType":"bool","name":"_isEnabled","type":"bool"}],"name":"setIsEnabled","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"setPause","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"setUnPause","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_to","type":"address"},{"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"withdrawBalance","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_to","type":"address"}],"name":"withdrawFullBalance","outputs":[],"stateMutability":"nonpayable","type":"function"},{"stateMutability":"payable","type":"receive"}]' 69 | --------------------------------------------------------------------------------