-
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.
Merge pull request #1 from vzotova/draft
Draft of proof bot script using ape
- Loading branch information
Showing
7 changed files
with
242 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,14 @@ | ||
__pycache__ | ||
.env | ||
build/ | ||
venv/ | ||
.python-version | ||
bin/ | ||
.vscode/ | ||
.idea/ | ||
node_modules/ | ||
.build | ||
contracts/.cache | ||
env | ||
.cache/ | ||
dist/ |
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,14 @@ | ||
repos: | ||
- repo: https://github.com/akaihola/darker | ||
rev: 1.7.2 | ||
hooks: | ||
- id: darker | ||
args: ["--check"] | ||
stages: [push] | ||
- id: darker | ||
stages: [commit] | ||
|
||
- repo: https://github.com/astral-sh/ruff-pre-commit | ||
rev: 'v0.1.4' | ||
hooks: | ||
- id: ruff |
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,24 @@ | ||
# State Transfer Bot Polygon → Ethereum | ||
|
||
Stateless bot monitors GraphQL endpoint for new events `MessageSent` that occurs on Polygon network. And then transfers proof to Ethereum root contract. | ||
|
||
## Installation | ||
|
||
We use [Ape](https://docs.apeworx.io/ape/stable/index.html) as the testing and deployment framework of this project. | ||
|
||
### Configuring Pre-commit | ||
|
||
To install pre-commit locally: | ||
|
||
```bash | ||
pre-commit install | ||
``` | ||
|
||
## Example of usage | ||
|
||
```bash | ||
export WEB3_INFURA_PROJECT_ID=<Infura project ID> | ||
export APE_ACCOUNTS_BOT_PASSPHRASE=<Passphrase for account with alias BOT> | ||
|
||
ape run proof_bot --fx-root-tunnel 0x720754c84f0b1737801bf63c950914E0C1d4aCa2 --graphql-endpoint https://api.studio.thegraph.com/query/24143/polygonchildmumbai/version/latest --proof-generator https://proof-generator.polygon.technology/api/v1/mumbai/exit-payload/ --network :goerli:infura --account BOT | ||
``` |
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,14 @@ | ||
name: train45 | ||
contracts_folder: contracts | ||
|
||
plugins: | ||
- name: solidity | ||
- name: ape-etherscan | ||
|
||
solidity: | ||
version: 0.8.23 | ||
evm_version: paris | ||
|
||
ethereum: | ||
mainnet: | ||
transaction_acceptance_timeout: 600 # 10 minutes |
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 @@ | ||
pragma solidity ^0.8.0; | ||
|
||
interface IReceiver { | ||
function receiveMessage(bytes memory inputData) external; | ||
} |
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,6 @@ | ||
-i https://pypi.org/simple | ||
ape-infura==0.7.0 | ||
ape-solidity==0.7.1 | ||
ape-etherscan==0.7.1 | ||
eth-ape==0.7.7 | ||
eth-rlp==0.3.0 ; python_version >= '3.7' and python_version < '4' |
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,165 @@ | ||
#!/usr/bin/python3 | ||
|
||
from urllib.parse import urljoin | ||
|
||
import click | ||
import requests | ||
import rlp | ||
from ape import project | ||
from ape.api import AccountAPI | ||
from ape.cli import ConnectedProviderCommand, account_option | ||
from ape.contracts import ContractInstance | ||
from ape.exceptions import ContractLogicError | ||
from ape.logging import logger | ||
from eth_typing import HexStr | ||
from eth_utils import to_bytes, to_int | ||
|
||
EVENT_SIGNATURE = "0x8c5261668696ce22758910d05bab8f186d6eb247ceac2af2e82c7dc17669b036" | ||
EXIT_ALREADY_PROCESSED_ERROR = "EXIT_ALREADY_PROCESSED" | ||
|
||
|
||
def hex_to_bytes(data: str) -> bytes: | ||
return to_bytes(hexstr=HexStr(data)) | ||
|
||
|
||
def get_polygon_last_block_number( | ||
account: AccountAPI, fx_base_channel_root_tunnel: ContractInstance | ||
) -> int: | ||
""" | ||
Search in tx history for the last `receiveMessage` tx, | ||
extracts block number (from Polygon) using that data | ||
""" | ||
|
||
last_blocknumber = 0 | ||
for tx in account.history: | ||
if tx.method_called and tx.method_called.name == "receiveMessage": | ||
last_proof_data = hex_to_bytes(tx.transaction.model_dump()["data"]) | ||
last_proof = fx_base_channel_root_tunnel.decode_input(last_proof_data)[1][ | ||
"inputData" | ||
] | ||
decoded = rlp.decode(last_proof) | ||
blocknumber = to_int(decoded[2]) | ||
if blocknumber > last_blocknumber: | ||
last_blocknumber = blocknumber | ||
|
||
return last_blocknumber | ||
|
||
|
||
def get_message_sent_events(graphql_endpoint: str, last_blocknumber: int) -> list[dict]: | ||
""" | ||
Queries GraphQL endpoint to retrieve all new `MessageSent` events | ||
on Polygon network | ||
""" | ||
|
||
gql = ( | ||
""" | ||
query AllMessagesSent { | ||
messageSents(where: {blockNumber_gte: """ | ||
+ str(last_blocknumber) | ||
+ """}, orderBy: blockNumber) { | ||
transactionHash | ||
} | ||
} | ||
""" | ||
) | ||
|
||
s = requests.session() | ||
s.headers = {"Accept": "application/json", "Content-Type": "application/json"} | ||
|
||
response = s.post(graphql_endpoint, json={"query": gql}) | ||
|
||
data = response.json() | ||
messages = data["data"]["messageSents"] | ||
return messages | ||
|
||
|
||
def push_proof( | ||
account: AccountAPI, fx_base_channel_root_tunnel: ContractInstance, proof: bytes | ||
) -> bool: | ||
"""Sends `receiveMessage` tx with the provided proof""" | ||
|
||
try: | ||
fx_base_channel_root_tunnel.receiveMessage(proof, sender=account) | ||
return True | ||
except ContractLogicError as e: | ||
if e.message != EXIT_ALREADY_PROCESSED_ERROR: | ||
raise e | ||
logger.info("Transaction already processed") | ||
return False | ||
|
||
|
||
def get_and_push_proof( | ||
account: AccountAPI, | ||
fx_base_channel_root_tunnel: ContractInstance, | ||
messages: list[dict], | ||
event_signature: str, | ||
proof_generator: str, | ||
) -> int: | ||
""" | ||
Iterates over all new messages, checks proof for each of them | ||
and executes tx on Ethereum side of the channel | ||
""" | ||
|
||
processed = 0 | ||
for event in messages: | ||
txhash = event["transactionHash"] | ||
s = requests.session() | ||
response = s.get( | ||
urljoin(proof_generator, txhash), params={"eventSignature": event_signature} | ||
) | ||
if response.status_code != 200: | ||
logger.warning("Transaction is not checkpointed") | ||
return processed | ||
|
||
proof = response.json()["result"] | ||
if push_proof(account, fx_base_channel_root_tunnel, proof): | ||
processed += 1 | ||
|
||
return processed | ||
|
||
|
||
@click.command(cls=ConnectedProviderCommand) | ||
@account_option() | ||
@click.option( | ||
"--fx-root-tunnel", | ||
"-fxrt", | ||
help="Address of FxBaseRootTunnel contract", | ||
default=None, | ||
required=True, | ||
type=click.STRING, | ||
) | ||
@click.option( | ||
"--graphql-endpoint", | ||
"-ge", | ||
help="GraphQL endpoint", | ||
default=None, | ||
required=True, | ||
type=click.STRING, | ||
) | ||
@click.option( | ||
"--proof-generator", | ||
"-pg", | ||
help="Proof generator URI", | ||
default=None, | ||
required=True, | ||
type=click.STRING, | ||
) | ||
def cli(account, fx_root_tunnel, graphql_endpoint, proof_generator): | ||
"""Provides proof from Polygon network to Ethereum""" | ||
|
||
account.set_autosign(enabled=True) | ||
receiver = project.IReceiver.at(fx_root_tunnel) | ||
last_blocknumber = get_polygon_last_block_number(account, receiver) | ||
logger.debug("Last processed block number: %d", last_blocknumber) | ||
|
||
messages = get_message_sent_events(graphql_endpoint, last_blocknumber) | ||
logger.info("Got %d messages", len(messages)) | ||
|
||
if len(messages) == 0: | ||
logger.info("No new transactions") | ||
return | ||
|
||
processed = get_and_push_proof( | ||
account, receiver, messages, EVENT_SIGNATURE, proof_generator | ||
) | ||
logger.info("Processed %d transactions", processed) |