-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit 2211adf
Showing
7 changed files
with
362 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
db | ||
.env | ||
docker-compose.yml | ||
.git | ||
.ipynb_checkpoints |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
db | ||
.env | ||
docker-compose.yml | ||
.ipynb_checkpoints |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
FROM python:3.9-alpine | ||
|
||
RUN apk update && apk upgrade | ||
# GCC. | ||
RUN apk add --no-cache --virtual .build-deps gcc musl-dev | ||
|
||
RUN python -m pip install --upgrade pip | ||
COPY requirements.txt . | ||
RUN python -m pip install -r requirements.txt | ||
|
||
# Remove gcc. | ||
RUN apk del .build-deps | ||
# Remove cache. | ||
RUN python -m pip cache purge | ||
|
||
WORKDIR /faucet | ||
|
||
COPY bridge-faucet.py . | ||
|
||
ENTRYPOINT python bridge-faucet.py |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,80 @@ | ||
OmniBridge faucet service | ||
==== | ||
|
||
This service is monitoring transfers executed through the OmniBridge on the xDai chain and reward the tokens recipients with small amount of xdai. | ||
|
||
## Run by docker CLI | ||
|
||
1. Prepare `.env` file with the following (at least) variables definitons: | ||
|
||
```bash | ||
FAUCET_PRIVKEY=cafe...cafe | ||
JSON_DB_DIR=/db | ||
INITIAL_START_BLOCK=123 | ||
``` | ||
|
||
See below with the variables explanation. | ||
|
||
2. Create the directory where the faucet service will store its data. | ||
|
||
```bash | ||
mkdir ./db | ||
``` | ||
|
||
3. Run the service | ||
|
||
```bash | ||
docker run -ti --rm -v $(pwd)/db:/db --env-file .env omnibridge/bridge-faucet:latest | ||
``` | ||
|
||
_Note:_ the source mount point after the key `-v` is the directory created on the step 2. The destination mount point is the directory specified in the variable `JSON_DB_DIR`. | ||
|
||
## Run by docker-compose | ||
|
||
1. Create the directory where the faucet service will store its data. | ||
|
||
```bash | ||
mkdir ./db | ||
``` | ||
|
||
2. Initialize the `docker-compose.yml` file based on `docker-compose.yml.example`. Set proper values for the following variables (at least) there: `FAUCET_PRIVKEY`, `JSON_DB_DIR` and `INITIAL_START_BLOCK`. | ||
|
||
Make sure that the source mount point in the `volumes` section is the directory created on the step 1. | ||
|
||
See below with the variables explanation. | ||
|
||
3. Run the service | ||
|
||
```bash | ||
docker-compose up -d | ||
``` | ||
|
||
## Faucet configuration | ||
|
||
The following environment variables may be used to configure the faucet behavior: | ||
|
||
1. `XDAI_RPC` -- JSON RPC endpoint the faucet uses to monitor OB events and get data. **Default:** `https://xdai.poanetwork.dev`. | ||
|
||
2. `BSC_OB` -- an address of BSC-xDai OB mediator on the xDai side. **Default:** `0x59447362798334d3485c64D1e4870Fde2DDC0d75`. | ||
|
||
3. `ETH_OB` -- an address of ETH-xDai OB mediator on the xDai side. **Default:** `0xf6A78083ca3e2a662D6dd1703c939c8aCE2e268d`. | ||
|
||
4. `FAUCET_PRIVKEY` -- a private key of an account holding xdai to reward. **No default value!**. | ||
|
||
5. `GAS_PRICE` -- the gas price (in gwei) the faucet uses for reward transactions. **Default:** `1`. | ||
|
||
6. `GAS_LIMIT` -- the gas limit the faucet uses for reward transactions. **Default:** `30000`. | ||
|
||
7. `REWARD` -- amount of xdai used as reward. **Default:** `0.005`. | ||
|
||
8. `POLLING_INTERVAL` -- amount of time (in seconds) between two subsequent cycles to discover OB transfers and send rewards. **Default:** `60`. | ||
|
||
9. `INITIAL_START_BLOCK` -- a block the first faucet's attempt to discover OB transfers starts from. **No default value!**. | ||
10. `FINALIZATION_INTERVAL` -- a number of blocks starting from the chain head to consider the chain as finalized. **Default:** `12`. | ||
11. `JSON_DB_DIR` -- a directory where the faucet service keeps its data. **No default value!**. | ||
12. `JSON_START_BLOCK` -- a name of JSON file where the last observed block is stored. **Default:** `faucet_start_block.json`. | ||
13. `JSON_CONTRACTS` -- a name of JSON file where addresses of recipient-contracts are stored. **Default:** `xdai-contracts.json`. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,231 @@ | ||
#!/usr/bin/env python3 | ||
|
||
from json import load, dump | ||
from web3 import Web3, HTTPProvider | ||
from eth_account import Account | ||
from time import sleep | ||
from os import getenv | ||
from dotenv import load_dotenv | ||
from logging import basicConfig, info, INFO | ||
|
||
basicConfig(level=INFO) | ||
|
||
STOP_FILE = 'stop.tmp' | ||
|
||
dotenv_read = False | ||
|
||
while True: | ||
XDAI_RPC = getenv('XDAI_RPC', 'https://xdai.poanetwork.dev') | ||
|
||
BSC_OB = getenv('BSC_OB', '0x59447362798334d3485c64D1e4870Fde2DDC0d75') | ||
ETH_OB = getenv('ETH_OB', '0xf6A78083ca3e2a662D6dd1703c939c8aCE2e268d') | ||
|
||
FAUCET_PRIVKEY = getenv('FAUCET_PRIVKEY', None) | ||
|
||
GAS_PRICE = int(getenv('GAS_PRICE', 1)) | ||
GAS_LIMIT = int(getenv('GAS_LIMIT', 30000)) | ||
REWARD = float(getenv('REWARD', 0.005)) | ||
POLLING_INTERVAL = getenv('POLLING_INTERVAL', 60) | ||
|
||
#INITIAL_START_BLOCK = 16173518 | ||
INITIAL_START_BLOCK = int(getenv('INITIAL_START_BLOCK', 16190379)) | ||
FINALIZATION_INTERVAL = int(getenv('FINALIZATION_INTERVAL', 12)) # blocks | ||
|
||
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') | ||
|
||
if not FAUCET_PRIVKEY: | ||
if dotenv_read: | ||
break | ||
|
||
info('Environment is not configured') | ||
load_dotenv('./.env') | ||
dotenv_read = True | ||
else: | ||
break | ||
|
||
if not FAUCET_PRIVKEY: | ||
raise BaseException("Faucet's privkey is not provided. Check the configuration") | ||
|
||
info(f'XDAI_RPC = {XDAI_RPC}') | ||
info(f'BSC_OB = {BSC_OB}') | ||
info(f'ETH_OB = {ETH_OB}') | ||
info(f'FAUCET_PRIVKEY = ...') | ||
info(f'GAS_PRICE = {GAS_PRICE}') | ||
info(f'GAS_LIMIT = {GAS_LIMIT}') | ||
info(f'REWARD = {REWARD}') | ||
info(f'POLLING_INTERVAL = {POLLING_INTERVAL}') | ||
info(f'INITIAL_START_BLOCK = {INITIAL_START_BLOCK}') | ||
info(f'FINALIZATION_INTERVAL = {FINALIZATION_INTERVAL}') | ||
info(f'JSON_DB_DIR = {JSON_DB_DIR}') | ||
info(f'JSON_START_BLOCK = {JSON_START_BLOCK}') | ||
info(f'JSON_CONTRACTS = {JSON_CONTRACTS}') | ||
|
||
# event | ||
# TokensBridged(address token, address recipient, uint256 value, bytes32 messageId) | ||
ABI = """ | ||
[ | ||
{ | ||
"type":"event", | ||
"name":"TokensBridged", | ||
"inputs":[ | ||
{ | ||
"type":"address", | ||
"name":"token", | ||
"internalType":"address", | ||
"indexed":true | ||
}, | ||
{ | ||
"type":"address", | ||
"name":"recipient", | ||
"internalType":"address", | ||
"indexed":true | ||
}, | ||
{ | ||
"type":"uint256", | ||
"name":"value", | ||
"internalType":"uint256", | ||
"indexed":false | ||
}, | ||
{ | ||
"type":"bytes32", | ||
"name":"messageId", | ||
"internalType":"bytes32", | ||
"indexed":true | ||
} | ||
], | ||
"anonymous":false | ||
} | ||
] | ||
""" | ||
|
||
xdai_w3 = Web3(HTTPProvider(XDAI_RPC)) | ||
bsc_ob = xdai_w3.eth.contract(abi = ABI, address = BSC_OB) | ||
eth_ob = xdai_w3.eth.contract(abi = ABI, address = ETH_OB) | ||
|
||
faucet = Account.privateKeyToAccount(FAUCET_PRIVKEY) | ||
|
||
try: | ||
with open(f'{JSON_DB_DIR}/{JSON_START_BLOCK}') as f: | ||
tmp = load(f) | ||
start_block = int(tmp['start_block']) | ||
except IOError: | ||
info("no start block stored previously") | ||
start_block = INITIAL_START_BLOCK | ||
info(f'start block: {start_block}') | ||
|
||
while True: | ||
try: | ||
with open(f'{JSON_DB_DIR}/{STOP_FILE}') as f: | ||
info("Stopping faucet") | ||
break | ||
except IOError: | ||
pass | ||
|
||
try: | ||
last_block = xdai_w3.eth.getBlock('latest').number | ||
except: | ||
raise BaseException('Cannot get the latest block number') | ||
info(f'current last block: {last_block}') | ||
last_block = last_block - FINALIZATION_INTERVAL | ||
|
||
filter = bsc_ob.events.TokensBridged.build_filter() | ||
info(f'Looking for TokensBridged events on BSC-xDAI OB from {start_block} to {last_block}') | ||
try: | ||
bsc_logs = xdai_w3.eth.getLogs({'fromBlock': start_block, | ||
'toBlock': last_block, | ||
'address': filter.address, | ||
'topics': filter.topics}) | ||
except: | ||
raise BaseException('Cannot get BSC-xDAI OB OB logs') | ||
info(f'Found {len(bsc_logs)} TokensBridged events on BSC-xDAI OB') | ||
|
||
filter = eth_ob.events.TokensBridged.build_filter() | ||
info(f'Looking for TokensBridged events on ETH-xDAI OB from {start_block} to {last_block}') | ||
try: | ||
eth_logs = xdai_w3.eth.getLogs({'fromBlock': start_block, | ||
'toBlock': last_block, | ||
'address': filter.address, | ||
'topics': filter.topics}) | ||
except: | ||
raise BaseException('Cannot get ETH-xDAI OB OB logs') | ||
info(f'Found {len(eth_logs)} TokensBridged events on ETH-xDAI OB') | ||
|
||
recipients = set() | ||
|
||
for log in bsc_logs: | ||
recipient = bsc_ob.events.TokensBridged().processLog(log).args.recipient | ||
recipients.add(recipient) | ||
info(f'Identified {len(recipients)} tokens recipients from BSC-xDAI OB events') | ||
|
||
tmp = len(recipients) | ||
for log in eth_logs: | ||
recipient = eth_ob.events.TokensBridged().processLog(log).args.recipient | ||
recipients.add(recipient) | ||
info(f'Identified {len(recipients) - tmp} tokens recipients from ETH-xDAI OB events') | ||
|
||
try: | ||
with open(f'{JSON_DB_DIR}/{JSON_CONTRACTS}') as f: | ||
contracts = load(f) | ||
except IOError: | ||
info("no contracts identified previously") | ||
contracts = {} | ||
|
||
endowing = [] | ||
for recipient in recipients: | ||
if recipient in contracts: | ||
continue | ||
code = xdai_w3.eth.getCode(recipient) | ||
if code != b'': | ||
contracts[recipient] = True | ||
continue | ||
balance = xdai_w3.eth.getBalance(recipient) | ||
if balance == 0: | ||
info(f'{recipient} balance is zero') | ||
endowing.append(recipient) | ||
info(f'found {len(endowing)} accounts for reward') | ||
|
||
with open(f'{JSON_DB_DIR}/{JSON_CONTRACTS}', 'w') as json_file: | ||
dump(contracts, json_file) | ||
|
||
balance_error = False | ||
|
||
if len(endowing) > 0: | ||
try: | ||
faucet_balance = xdai_w3.eth.getBalance(faucet.address) | ||
except: | ||
raise BaseException("Cannot get faucet balance") | ||
info(f'faucet balance: {faucet_balance}') | ||
|
||
if faucet_balance > len(endowing) * GAS_LIMIT * Web3.toWei(GAS_PRICE, 'gwei'): | ||
try: | ||
nonce = xdai_w3.eth.getTransactionCount(faucet.address) | ||
except: | ||
raise BaseException("Cannot get transactions count of faucet's account") | ||
info(f'starting nonce: {nonce}') | ||
for recipient in endowing: | ||
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, | ||
} | ||
rawtx = faucet.signTransaction(tx) | ||
sent_tx_hash = xdai_w3.eth.sendRawTransaction(rawtx.rawTransaction) | ||
info(f'{recipient} rewarded by {Web3.toHex(sent_tx_hash)}') | ||
nonce += 1 | ||
sleep(0.1) | ||
else: | ||
info(f'not enough balance on the faucet {faucet.address}') | ||
balance_error = True | ||
|
||
if not balance_error: | ||
start_block = last_block + 1 | ||
with open(f'{JSON_DB_DIR}/{JSON_START_BLOCK}', 'w') as json_file: | ||
dump({'start_block': start_block}, json_file) | ||
|
||
sleep(POLLING_INTERVAL) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
version: "3.9" | ||
|
||
services: | ||
faucet: | ||
image: omnibridge/bridge-faucet:latest | ||
container_name: bridge-faucet | ||
environment: | ||
# faucet account's private key must be here (without 0x) | ||
- FAUCET_PRIVKEY=cafe...cafe | ||
- JSON_DB_DIR=/db | ||
- INITIAL_START_BLOCK=123 | ||
volumes: | ||
- ./db:/db | ||
restart: unless-stopped | ||
logging: | ||
driver: "json-file" | ||
options: | ||
max-size: "100m" | ||
max-file: "1" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
eth-account==0.5.4 | ||
web3==5.17.0 | ||
python-dotenv==0.17.1 |