diff --git a/README.md b/README.md index 20bbe8f..5dc1ed1 100644 --- a/README.md +++ b/README.md @@ -59,12 +59,17 @@ The following environment variables may be used to configure the faucet behavior 4. `MOONS_EXT` - an address of Rinkeby-xDai ERC20-to-ERC677 mediator for MOONs tokens on the xDai side. **Default:** `0x1E0507046130c31DEb20EC2f870ad070Ff266079`. 5. `BRICKS_EXT` - an address of Rinkeby-xDai ERC20-to-ERC677 mediator for BRICKs tokens on the xDai side. **Default:** `0xf85b17E64Bc788D0CB1A8c8C87c0d74e520c2A54`. 6. `FAUCET_PRIVKEY` - a private key of an account holding xdai to reward. **No default value!**. -7. `GAS_PRICE` - the gas price (in gwei) the faucet uses for reward transactions. **Default:** `1`. -8. `GAS_LIMIT` - the gas limit the faucet uses for reward transactions. **Default:** `30000`. -9. `REWARD` - amount of xdai used as reward. **Default:** `0.005`. -10. `POLLING_INTERVAL` - amount of time (in seconds) between two subsequent cycles to discover OB transfers and send rewards. **Default:** `60`. -11. `INITIAL_START_BLOCK` - a block the first faucet's attempt to discover OB transfers starts from. **No default value!**. -12. `FINALIZATION_INTERVAL` - a number of blocks starting from the chain head to consider the chain as finalized. **Default:** `12`. -13. `JSON_DB_DIR` - a directory where the faucet service keeps its data. **No default value!**. -14. `JSON_START_BLOCK` - a name of JSON file where the last observed block is stored. **Default:** `faucet_start_block.json`. -15. `JSON_CONTRACTS` - a name of JSON file where addresses of recipient-contracts are stored. **Default:** `xdai-contracts.json`. \ No newline at end of file +7. `GAS_PRICE` - the gas price (in gwei) the faucet uses for reward transactions (pre-EIP1559 transactions). `-1` means to use EIP1559 transactions. **Default:** `-1`. +8. `HISTORICAL_BASE_FEE_DEPTH` - number of recent blocks to estimate the base fee as per gas (EIP1559 related). **Default:** `20`. +9. `MAX_PRIORITY_FEE` - the priority fee per gas (in gwei) used by the faucet (EIP1559 related). **Default:** `1`. +10. `MAX_FEE_RATIO` - a coefficient to adjust the base fee per gas acquired from the historical data (EIP1559 related). **Default:** `1.3`. +11. `FEE_LIMIT` - the higher bound of max fee per gas (in gwei) which is used in order to avoid expensive transactions (EIP1559 related). **Default:** `150`. +12. `GAS_LIMIT` - the gas limit the faucet uses for reward transactions. **Default:** `30000`. +13. `REWARD` - amount of xdai used as reward. **Default:** `0.005`. +14. `POLLING_INTERVAL` - amount of time (in seconds) between two subsequent cycles to discover OB transfers and send rewards. **Default:** `60`. +15. `INITIAL_START_BLOCK` - a block the first faucet's attempt to discover OB transfers starts from. **No default value!**. +16. `FINALIZATION_INTERVAL` - a number of blocks starting from the chain head to consider the chain as finalized. **Default:** `12`. +17. `JSON_DB_DIR` - a directory where the faucet service keeps its data. **No default value!**. +18. `JSON_START_BLOCK` - a name of JSON file where the last observed block is stored. **Default:** `faucet_start_block.json`. +19. `JSON_CONTRACTS` - a name of JSON file where addresses of recipient-contracts are stored. **Default:** `xdai-contracts.json`. +20. `TEST_TO_SEND` - make a transaction to itself just after running the service. **Default:** `false`. \ No newline at end of file diff --git a/bridge-faucet.py b/bridge-faucet.py index 09acc81..4eff3f6 100644 --- a/bridge-faucet.py +++ b/bridge-faucet.py @@ -7,6 +7,8 @@ from os import getenv from dotenv import load_dotenv from logging import basicConfig, info, INFO +from requests import post +from statistics import median, mean basicConfig(level=INFO) @@ -26,7 +28,12 @@ FAUCET_PRIVKEY = getenv('FAUCET_PRIVKEY', None) - GAS_PRICE = int(getenv('GAS_PRICE', 1)) + GAS_PRICE = float(getenv('GAS_PRICE', -1)) + HISTORICAL_BASE_FEE_DEPTH = int(getenv('HISTORICAL_BASE_FEE_DEPTH', 20)) + MAX_PRIORITY_FEE = float(getenv('MAX_PRIORITY_FEE', 1)) + MAX_FEE_RATIO = float(getenv('MAX_FEE_RATIO', 1.3)) + FEE_LIMIT = float(getenv('FEE_LIMIT', 150)) + GAS_LIMIT = int(getenv('GAS_LIMIT', 30000)) REWARD = float(getenv('REWARD', 0.005)) POLLING_INTERVAL = getenv('POLLING_INTERVAL', 60) @@ -38,6 +45,8 @@ JSON_DB_DIR = getenv('JSON_DB_DIR', '.') JSON_START_BLOCK = getenv('JSON_START_BLOCK', 'faucet_start_block.json') JSON_CONTRACTS = getenv('JSON_CONTRACTS', 'xdai-contracts.json') + + TEST_TO_SEND = getenv('TEST_TO_SEND', False) if not FAUCET_PRIVKEY: if dotenv_read: @@ -60,6 +69,10 @@ info(f'BRICKS_EXT = {BRICKS_EXT}') info(f'FAUCET_PRIVKEY = ...') info(f'GAS_PRICE = {GAS_PRICE}') +info(f'HISTORICAL_BASE_FEE_DEPTH = {HISTORICAL_BASE_FEE_DEPTH}') +info(f'MAX_PRIORITY_FEE = {MAX_PRIORITY_FEE}') +info(f'MAX_FEE_RATIO = {MAX_FEE_RATIO}') +info(f'FEE_LIMIT = {FEE_LIMIT}') info(f'GAS_LIMIT = {GAS_LIMIT}') info(f'REWARD = {REWARD}') info(f'POLLING_INTERVAL = {POLLING_INTERVAL}') @@ -68,6 +81,7 @@ info(f'JSON_DB_DIR = {JSON_DB_DIR}') info(f'JSON_START_BLOCK = {JSON_START_BLOCK}') info(f'JSON_CONTRACTS = {JSON_CONTRACTS}') +info(f'TEST_TO_SEND = {TEST_TO_SEND}') # event # TokensBridged(address token, address recipient, uint256 value, bytes32 messageId) @@ -146,6 +160,8 @@ faucet = Account.privateKeyToAccount(FAUCET_PRIVKEY) +sending_tested = False + try: with open(f'{JSON_DB_DIR}/{JSON_START_BLOCK}') as f: tmp = load(f) @@ -264,6 +280,11 @@ contracts = {} endowing = [] + if TEST_TO_SEND and not sending_tested: + endowing.append(faucet.address) + info(f'activated testmode to send a transaction') + sending_tested = True + for recipient in recipients: if recipient in contracts: continue @@ -289,7 +310,36 @@ raise BaseException("Cannot get faucet balance") info(f'faucet balance: {faucet_balance}') - if faucet_balance > len(endowing) * GAS_LIMIT * Web3.toWei(GAS_PRICE, 'gwei'): + if GAS_PRICE < 0: + try: + resp = post(XDAI_RPC, + headers = {'Content-Type': 'application/json'}, + json={'method': 'eth_feeHistory', + 'params': [HISTORICAL_BASE_FEE_DEPTH, "latest"], + 'id':1 + }) + except: + raise BaseException("Cannot get historical fee data") + + if resp.status_code == 200: + fee_hist = resp.json()['result'] + else: + raise BaseException(f'Getting historical fee data did not succeed: {resp.status_code}, {resp.text}') + + base_fee_hist = list(map(lambda h: Web3.toInt(hexstr=h), fee_hist['baseFeePerGas'])) + historical_base_fee = max(median(base_fee_hist), int(mean(base_fee_hist))) + info(f'Base fee based on historical data: {Web3.fromWei(historical_base_fee, "gwei")}') + + recommended_priority_fee = Web3.toWei(MAX_PRIORITY_FEE, 'gwei') + + max_gas_price = min(int(historical_base_fee * MAX_FEE_RATIO) + recommended_priority_fee, + Web3.toWei(FEE_LIMIT, 'gwei')) + info(f'Suggested max fee per gas: {Web3.fromWei(max_gas_price, "gwei")}') + info(f'Suggested priority fee per gas: {Web3.fromWei(recommended_priority_fee, "gwei")}') + else: + max_gas_price = Web3.toWei(GAS_PRICE, 'gwei') + + if faucet_balance > len(endowing) * GAS_LIMIT * max_gas_price: try: nonce = xdai_w3.eth.getTransactionCount(faucet.address) except: @@ -299,12 +349,16 @@ tx = { 'nonce': nonce, 'gas': GAS_LIMIT, - 'gasPrice': Web3.toWei(GAS_PRICE, 'gwei'), 'data': b'Rewarded for OmniBridge transaction', 'chainId': 100, 'value': Web3.toWei(REWARD, 'ether'), 'to': recipient, } + if GAS_PRICE < 0: + tx['maxFeePerGas'] = max_gas_price + tx['maxPriorityFeePerGas'] = recommended_priority_fee + else: + tx['gasPrice'] = max_gas_price rawtx = faucet.signTransaction(tx) sent_tx_hash = xdai_w3.eth.sendRawTransaction(rawtx.rawTransaction) info(f'{recipient} rewarded by {Web3.toHex(sent_tx_hash)}') diff --git a/requirements.txt b/requirements.txt index 0503e68..e418647 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,3 +1,5 @@ -eth-account==0.5.4 -web3==5.17.0 -python-dotenv==0.17.1 \ No newline at end of file +eth-account==0.5.6 +web3==5.24.0 +python-dotenv==0.17.1 +requests +statistics