Skip to content

Commit

Permalink
Add fee rewards manager to possim
Browse files Browse the repository at this point in the history
Adds ability to deploy compiled contracts, this should be favoured
instead of compiling contracts with solc, it's more reliable because we
can use the exact same bytecode that is deployed in the Ethereum
blockchain.

For the fee manager contract we need 2 contracts, first the library and
then the main contract, we deploy the library contract and substitute a
placeholder inside the compiled binary with the library's address.
  • Loading branch information
enriquefynn committed Dec 19, 2023
1 parent 5f9a71f commit 0704d20
Show file tree
Hide file tree
Showing 5 changed files with 10,735 additions and 1 deletion.
8 changes: 8 additions & 0 deletions Tiltfile
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,14 @@ local_resource(
allow_parallel=True,
)

# Deploy fee manager library and main contract.
local_resource(
"deploy-fee-manager-contracts",
cmd="python3 -m eth_possim deploy-fee-manager-contracts",
deps=lh_beacon_nodes,
allow_parallel=True,
)

# Deploy validator nodes
for idx in range(cfg["cl"]["lh_node_count"]):
node_name = "val-lh-{}".format(idx)
Expand Down
53 changes: 52 additions & 1 deletion eth_possim/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
from eth_possim.config import initialise_privatenet
from eth_possim.config.load import load_configuration
from eth_possim.config.patch import patch_cl_cfg
from eth_possim.contracts import deploy_contract_onchain
from eth_possim.contracts import deploy_contract_onchain, deploy_compiled_contract
from eth_possim.deposit import load_initial_deposit_tree
from eth_possim.utils import ensure_dir_exists

Expand Down Expand Up @@ -73,6 +73,57 @@ def deploy_batch_deposit_contract(rpc: str):
with open("./.data/configuration.yaml", "w") as f:
yaml.dump(cfg, f)

@cli.command()
@click.option("--rpc", help="RPC endpoint URL address.", default="")
def deploy_fee_manager_contracts(rpc: str):
"""Deploys the fee manager contracts: library and contract."""

with open("./.data/configuration.yaml", "r") as f:
cfg = yaml.safe_load(f)
if not rpc:
rpc = f"http://localhost:{cfg['haproxy']['el']['port_geth_rpc']}"

fee_manager_library_address = deploy_compiled_contract(
cfg=cfg,
rpc=rpc,
foundry_json_path=f"{cfg['resources']}/ethereum_compiled_contracts/CalculateAndSendRewards.json",
)

# Patch and write back `configuration.yaml`
cfg["cl"]["fee_manager_library_address"] = fee_manager_library_address

fee_manager_address = deploy_compiled_contract(
cfg=cfg,
rpc=rpc,
foundry_json_path=f"{cfg['resources']}/ethereum_compiled_contracts/FeeRewardsManager.json",
args=[2800],
libraries=[("__$c56d76a1417c078a963cba4fa22c45184c$__", fee_manager_library_address)]
)
cfg["cl"]["fee_manager_address"] = fee_manager_address

with open("./.data/configuration.yaml", "w") as f:
yaml.dump(cfg, f)

@cli.command()
@click.option("--rpc", help="RPC endpoint URL address.", default="")
@click.option("--path", help="Path to the contract source.")
@click.option("--cfg-key-address", help="Key in which to save the contract address.")
@click.option("--library", multiple=True, type=(str, str), help="Libraries to be replaced in the bytecode contract in the format key value")
@click.argument("args", nargs=-1)
def deploy_contract_bytecode(rpc: str, path: str, cfg_key_address: str, args: list, library: list):
"""Deploys a contract by the compiled bytecode and abi."""

with open("./.data/configuration.yaml", "r") as f:
cfg = yaml.safe_load(f)
if not rpc:
rpc = f"http://localhost:{cfg['haproxy']['el']['port_geth_rpc']}"

contract_address = deploy_compiled_contract(cfg=cfg, rpc=rpc, foundry_json_path=path, args=args, libraries=library)

# Patch and write back `configuration.yaml`
cfg["cl"][cfg_key_address] = contract_address
with open("./.data/configuration.yaml", "w") as f:
yaml.dump(cfg, f)

@cli.command()
@click.option("--rpc", help="RPC endpoint URL address.", default="")
Expand Down
46 changes: 46 additions & 0 deletions eth_possim/contracts.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,58 @@
import binascii
import json
import logging
import solcx
import web3
import re
from typing import List, Tuple


logger = logging.getLogger(__name__)

def deploy_compiled_contract(cfg: dict, rpc: str, foundry_json_path: str, args: list = [], libraries: List[Tuple[str, str]] = []) -> str:
with open(foundry_json_path, "r") as f:
foundry_json = json.loads(f.read())

bytecode_str = foundry_json["bytecode"]["object"][2:]
for library in libraries:
# Skip 0x from the library address.
bytecode_str = bytecode_str.replace(library[0], library[1][2:])
bytecode = binascii.unhexlify(bytecode_str)

abi = foundry_json["abi"]

w3 = web3.Web3(web3.Web3.HTTPProvider(rpc))
w3.middleware_onion.inject(
web3.middleware.geth_poa_middleware,
layer=0,
)

contract = w3.eth.contract(abi=abi, bytecode=bytecode)
account = w3.eth.account.from_key(cfg["el"]["funder"]["private_key"])
w3.eth.default_account = account
nonce = w3.eth.get_transaction_count(account.address)

deploy_tx = contract.constructor(*args).build_transaction(
{
"chainId": cfg["el"]["chain_id"],
# "gas": Let the function estimate gas.
# "gasPrice": Let the function estimate gas price.
"from": account.address,
"nonce": nonce,
}
)
signed_txn = w3.eth.account.sign_transaction(
deploy_tx,
private_key=cfg["el"]["funder"]["private_key"],
)
w3.eth.send_raw_transaction(signed_txn.rawTransaction)
tx_receipt = w3.eth.wait_for_transaction_receipt(signed_txn.hash)

logger.info(
f"Contract from '{foundry_json_path}' was published at address '{tx_receipt['contractAddress']}' [block: {tx_receipt['blockNumber']}]"
)

return tx_receipt["contractAddress"]

def deploy_contract_onchain(
cfg: dict, rpc: str, path: str, name: str, args: list = []
Expand Down
Loading

0 comments on commit 0704d20

Please sign in to comment.