Skip to content

Commit

Permalink
Refacto chain definition (kkrt-labs#598)
Browse files Browse the repository at this point in the history
Time spent on this PR: 0.5 days

## Pull request type

Please check the type of change your PR introduces:

- [ ] Bugfix
- [ ] Feature
- [ ] Code style update (formatting, renaming)
- [x] Refactoring (no functional changes, no api changes)
- [ ] Build related changes
- [ ] Documentation content changes
- [ ] Other (please describe):

## What is the current behavior?

In the scripts/constants.py file, there are a bunch of parameters that
need to be updated whenever we need to add a new chain.

## What is the new behavior?

The const definition has been updated to instead be a list of chain,
each with all their parameters.

## Other information

<!-- Any other information that is important to this PR such as
screenshots of how the component looks before and after the change. -->
  • Loading branch information
ClementWalter authored Jun 21, 2023
1 parent c439cc5 commit 50db72c
Show file tree
Hide file tree
Showing 6 changed files with 109 additions and 105 deletions.
2 changes: 1 addition & 1 deletion .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
GITHUB_TOKEN=

# An infura RPC key for mainnet/testnet/testnet2
RPC_KEY=
INFURA_KEY=
# The Sharing RPC URL
SHARINGAN_RPC_URL=

Expand Down
2 changes: 1 addition & 1 deletion scripts/compile_kakarot.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
# %% Main
async def main():
# %% Compile
logger.info(f"ℹ️ Compiling contracts for network {NETWORK}")
logger.info(f"ℹ️ Compiling contracts for network {NETWORK['name']}")
initial_time = datetime.now()
for contract in COMPILED_CONTRACTS:
logger.info(f"⏳ Compiling {contract}")
Expand Down
154 changes: 86 additions & 68 deletions scripts/constants.py
Original file line number Diff line number Diff line change
@@ -1,97 +1,115 @@
import logging
import os
import re
from enum import Enum
from math import ceil, log
from pathlib import Path

from dotenv import load_dotenv
from eth_keys import keys
from starknet_py.net.full_node_client import FullNodeClient

logging.basicConfig()
logger = logging.getLogger(__name__)
logger.setLevel(logging.INFO)
load_dotenv()

ETH_TOKEN_ADDRESS = 0x49D36570D4E46F48E99674BD3FCC84644DDD6B96F7C741B1562B82F9E004DC7
EVM_PRIVATE_KEY = os.getenv("EVM_PRIVATE_KEY")
EVM_ADDRESS = (
EVM_PRIVATE_KEY
and keys.PrivateKey(
bytes.fromhex(EVM_PRIVATE_KEY[2:])
).public_key.to_checksum_address()
)

NETWORK = os.getenv("STARKNET_NETWORK", "devnet")
NETWORK = (
"testnet"
if re.match(r".*(testnet|goerli)$", NETWORK, flags=re.I)
else "testnet2"
if re.match(r".*(testnet|goerli)-?2$", NETWORK, flags=re.I)
else "mainnet"
if re.match(r".*(mainnet).*", NETWORK, flags=re.I)
else "sharingan"
if re.match(r".*(sharingan).*", NETWORK, flags=re.I)
else "katana"
if re.match(r".*(katana).*", NETWORK, flags=re.I)
else "madara"
if re.match(r".*(madara).*", NETWORK, flags=re.I)
else "devnet"
)
STARKSCAN_URLS = {
"mainnet": "https://starkscan.co",
"testnet": "https://testnet.starkscan.co",
"testnet2": "https://testnet-2.starkscan.co",
"devnet": "https://devnet.starkscan.co",
"sharingan": "https://starknet-madara.netlify.app/#/explorer/query",
"katana": "",
"madara": "",
}
STARKSCAN_URL = STARKSCAN_URLS[NETWORK]

if not os.getenv("RPC_KEY") and NETWORK in ["mainnet", "testnet", "testnet2"]:
raise ValueError(f"RPC_KEY env variable is required when targeting {NETWORK}")
RPC_URLS = {
"mainnet": f"https://starknet-mainnet.infura.io/v3/{os.getenv('RPC_KEY')}",
"testnet": f"https://starknet-goerli.infura.io/v3/{os.getenv('RPC_KEY')}",
"testnet2": f"https://starknet-goerli2.infura.io/v3/{os.getenv('RPC_KEY')}",
"devnet": "http://127.0.0.1:5050/rpc",
"sharingan": os.getenv("SHARINGAN_RPC_URL"),
"katana": "http://127.0.0.1:5050",
"madara": "http://127.0.0.1:9944",
}
RPC_CLIENT = FullNodeClient(node_url=RPC_URLS[NETWORK])


class ChainId(Enum):
mainnet = int.from_bytes(b"SN_MAIN", "big")
testnet = int.from_bytes(b"SN_GOERLI", "big")
testnet2 = int.from_bytes(b"SN_GOERLI2", "big")
devnet = int.from_bytes(b"SN_GOERLI", "big")
sharingan = int.from_bytes(b"SN_GOERLI", "big")
katana = int.from_bytes(b"KATANA", "big")
madara = int.from_bytes(b"SN_GOERLI", "big")


BUILD_DIR = Path("build")
BUILD_DIR.mkdir(exist_ok=True, parents=True)
SOURCE_DIR = Path("src")
CONTRACTS = {p.stem: p for p in list(SOURCE_DIR.glob("**/*.cairo"))}

NETWORKS = {
"mainnet": {
"name": "mainnet",
"explorer_url": "https://starkscan.co",
"rpc_url": f"https://starknet-mainnet.infura.io/v3/{os.getenv('INFURA_KEY')}",
"chain_id": ChainId.mainnet,
},
"testnet": {
"name": "testnet",
"explorer_url": "https://testnet.starkscan.co",
"rpc_url": f"https://starknet-goerli.infura.io/v3/{os.getenv('INFURA_KEY')}",
"chain_id": ChainId.testnet,
},
"testnet2": {
"name": "testnet2",
"explorer_url": "https://testnet-2.starkscan.co",
"rpc_url": f"https://starknet-goerli2.infura.io/v3/{os.getenv('INFURA_KEY')}",
"chain_id": ChainId.testnet2,
},
"devnet": {
"name": "devnet",
"explorer_url": "",
"rpc_url": "http://127.0.0.1:5050/rpc",
"chain_id": ChainId.testnet,
},
"katana": {
"name": "katana",
"explorer_url": "",
"rpc_url": "http://127.0.0.1:5050",
"chain_id": ChainId.katana,
},
"madara": {
"name": "madara",
"explorer_url": "",
"rpc_url": "http://127.0.0.1:9944",
"chain_id": ChainId.testnet,
},
"sharingan": {
"name": "sharingan",
"explorer_url": "",
"rpc_url": os.getenv("SHARINGAN_RPC_URL"),
"chain_id": ChainId.testnet,
},
}

ACCOUNT_ADDRESS = os.environ.get(
f"{NETWORK.upper()}_ACCOUNT_ADDRESS"
) or os.environ.get("ACCOUNT_ADDRESS")
PRIVATE_KEY = os.environ.get(f"{NETWORK.upper()}_PRIVATE_KEY") or os.environ.get(
"PRIVATE_KEY"
NETWORK = NETWORKS[os.getenv("STARKNET_NETWORK", "devnet")]
NETWORK["account_address"] = os.environ.get(
f"{NETWORK['name'].upper()}_ACCOUNT_ADDRESS"
)
if NETWORK["account_address"] is None:
logger.warning(
f"⚠️ {NETWORK['name'].upper()}_ACCOUNT_ADDRESS not set, defaulting to ACCOUNT_ADDRESS"
)
NETWORK["account_address"] = os.getenv("ACCOUNT_ADDRESS")
NETWORK["private_key"] = os.environ.get(f"{NETWORK['name'].upper()}_PRIVATE_KEY")
if NETWORK["private_key"] is None:
logger.warning(
f"⚠️ {NETWORK['name'].upper()}_PRIVATE_KEY not set, defaulting to PRIVATE_KEY"
)
NETWORK["private_key"] = os.getenv("PRIVATE_KEY")

DEPLOYMENTS_DIR = Path("deployments") / NETWORK
DEPLOYMENTS_DIR.mkdir(exist_ok=True, parents=True)
RPC_CLIENT = FullNodeClient(node_url=NETWORK["rpc_url"])

# TODO: get CHAIN_ID from RPC endpoint when starknet-py doesn't expect an enum
CHAIN_ID = getattr(ChainId, NETWORK)
KAKAROT_CHAIN_ID = 1263227476 # KKRT (0x4b4b5254) in ASCII
ETH_TOKEN_ADDRESS = 0x49D36570D4E46F48E99674BD3FCC84644DDD6B96F7C741B1562B82F9E004DC7
SOURCE_DIR = Path("src")
CONTRACTS = {p.stem: p for p in list(SOURCE_DIR.glob("**/*.cairo"))}

BUILD_DIR = Path("build")
BUILD_DIR.mkdir(exist_ok=True, parents=True)
DEPLOYMENTS_DIR = Path("deployments") / NETWORK["name"]
DEPLOYMENTS_DIR.mkdir(exist_ok=True, parents=True)
COMPILED_CONTRACTS = [
{"contract_name": "kakarot", "is_account_contract": False},
{"contract_name": "blockhash_registry", "is_account_contract": False},
{"contract_name": "contract_account", "is_account_contract": False},
{"contract_name": "externally_owned_account", "is_account_contract": True},
{"contract_name": "proxy", "is_account_contract": False},
]

KAKAROT_CHAIN_ID = 1263227476 # KKRT (0x4b4b5254) in ASCII
EVM_PRIVATE_KEY = os.getenv("EVM_PRIVATE_KEY")
EVM_ADDRESS = (
EVM_PRIVATE_KEY
and keys.PrivateKey(
bytes.fromhex(EVM_PRIVATE_KEY[2:])
).public_key.to_checksum_address()
)

logger.info(
f"ℹ️ Connected to CHAIN_ID {NETWORK['chain_id'].value.to_bytes(ceil(log(NETWORK['chain_id'].value, 256)), 'big')} "
f"with RPC {RPC_CLIENT.url}"
)
9 changes: 1 addition & 8 deletions scripts/deploy_kakarot.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,12 @@
# %% Imports
import logging
from asyncio import run
from math import ceil, log

from scripts.constants import (
CHAIN_ID,
COMPILED_CONTRACTS,
ETH_TOKEN_ADDRESS,
EVM_ADDRESS,
NETWORK,
RPC_CLIENT,
)
from scripts.utils.starknet import (
declare,
Expand All @@ -30,11 +27,7 @@
# %% Main
async def main():
# %% Declarations
logger.info(
f"ℹ️ Connected to CHAIN_ID {CHAIN_ID.value.to_bytes(ceil(log(CHAIN_ID.value, 256)), 'big')} "
f"with RPC {RPC_CLIENT.url}"
)
if NETWORK == "madara":
if NETWORK["name"] == "madara":
await deploy_starknet_account(amount=100)
account = await get_starknet_account()
logger.info(f"ℹ️ Using account {hex(account.address)} as deployer")
Expand Down
5 changes: 2 additions & 3 deletions scripts/utils/kakarot.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@

from scripts.artifacts import fetch_deployments
from scripts.constants import (
CHAIN_ID,
EVM_ADDRESS,
EVM_PRIVATE_KEY,
KAKAROT_CHAIN_ID,
Expand All @@ -36,7 +35,7 @@
logger = logging.getLogger(__name__)
logger.setLevel(logging.INFO)

if NETWORK not in ["devnet", "madara", "katana"]:
if NETWORK["name"] not in ["devnet", "madara", "katana"]:
fetch_deployments()
KAKAROT_ADDRESS = get_deployments()["kakarot"]["address"]
FOUNDRY_FILE = toml.loads((Path(__file__).parents[2] / "foundry.toml").read_text())
Expand Down Expand Up @@ -172,7 +171,7 @@ async def get_eoa(
return Account(
address=starknet_address,
client=RPC_CLIENT,
chain=CHAIN_ID,
chain=NETWORK["chain_id"],
key_pair=KeyPair(private_key, address),
)

Expand Down
42 changes: 18 additions & 24 deletions scripts/utils/starknet.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,26 +31,19 @@
from starkware.starknet.public.abi import get_selector_from_name

from scripts.constants import (
ACCOUNT_ADDRESS,
BUILD_DIR,
CHAIN_ID,
CONTRACTS,
DEPLOYMENTS_DIR,
ETH_TOKEN_ADDRESS,
NETWORK,
PRIVATE_KEY,
RPC_CLIENT,
SOURCE_DIR,
STARKSCAN_URL,
)

logging.basicConfig()
logger = logging.getLogger(__name__)
logger.setLevel(logging.INFO)

_account_address = ACCOUNT_ADDRESS
_private_key = PRIVATE_KEY


def int_to_uint256(value):
value = int(value)
Expand All @@ -63,15 +56,13 @@ async def get_starknet_account(
address=None,
private_key=None,
) -> Account:
global _account_address
global _private_key
address = address or _account_address
address = address or NETWORK["account_address"]
if address is None:
raise ValueError(
"address was not given in arg nor in env variable, see README.md#Deploy"
)
address = int(address, 16)
private_key = private_key or _private_key
private_key = private_key or NETWORK["private_key"]
if private_key is None:
raise ValueError(
"private_key was not given in arg nor in env variable, see README.md#Deploy"
Expand Down Expand Up @@ -113,7 +104,7 @@ async def get_starknet_account(
return Account(
address=address,
client=RPC_CLIENT,
chain=CHAIN_ID,
chain=NETWORK["chain_id"],
key_pair=key_pair,
)

Expand Down Expand Up @@ -142,7 +133,7 @@ async def fund_address(address: Union[int, str], amount: float):
"""
address = int(address, 16) if isinstance(address, str) else address
amount = amount * 1e18
if NETWORK == "devnet":
if NETWORK["name"] == "devnet":
response = requests.post(
f"http://127.0.0.1:5050/mint",
json={"address": hex(address), "amount": amount},
Expand All @@ -162,7 +153,7 @@ async def fund_address(address: Union[int, str], amount: float):
address, int_to_uint256(amount)
)
# TODO: remove when madara has a regular default account
if NETWORK == "madara" and account.address == 1:
if NETWORK["name"] == "madara" and account.address == 1:
transaction = Invoke(
calldata=[
prepared.to_addr,
Expand Down Expand Up @@ -242,7 +233,7 @@ def get_alias(contract_name):


def get_tx_url(tx_hash: int) -> str:
return f"{STARKSCAN_URL}/tx/0x{tx_hash:064x}"
return f"{NETWORK['explorer_url']}/tx/0x{tx_hash:064x}"


def compile_contract(contract):
Expand All @@ -256,7 +247,7 @@ def compile_contract(contract):
str(SOURCE_DIR),
"--no_debug_info",
*(["--account_contract"] if contract["is_account_contract"] else []),
*(["--disable_hint_validation"] if NETWORK == "devnet" else []),
*(["--disable_hint_validation"] if NETWORK["name"] == "devnet" else []),
],
capture_output=True,
)
Expand Down Expand Up @@ -290,9 +281,7 @@ async def deploy_starknet_account(private_key=None, amount=1) -> Account:
)
class_hash = await declare("OpenzeppelinAccount")
salt = random.randint(0, 2**251)
global _private_key
global _account_address
private_key = private_key or _private_key
private_key = private_key or NETWORK["private_key"]
if private_key is None:
raise ValueError(
"private_key was not given in arg nor in env variable, see README.md#Deploy"
Expand All @@ -314,7 +303,7 @@ async def deploy_starknet_account(private_key=None, amount=1) -> Account:
salt=salt,
key_pair=key_pair,
client=RPC_CLIENT,
chain=CHAIN_ID,
chain=NETWORK["chain_id"],
constructor_calldata=constructor_calldata,
max_fee=int(1e17),
)
Expand All @@ -323,8 +312,8 @@ async def deploy_starknet_account(private_key=None, amount=1) -> Account:
logger.warning("⚠️ Transaction REJECTED")

logger.info(f"✅ Account deployed at address {hex(res.account.address)}")
_account_address = hex(res.account.address)
_private_key = hex(key_pair.private_key)
NETWORK["account_address"] = hex(res.account.address)
NETWORK["private_key"] = hex(key_pair.private_key)
return res.account


Expand Down Expand Up @@ -439,10 +428,15 @@ async def wait_for_transaction(*args, **kwargs):
elapsed = 0
check_interval = kwargs.get(
"check_interval",
0.1 if NETWORK in ["devnet", "katana"] else 1 if NETWORK == "madara" else 15,
0.1
if NETWORK["name"] in ["devnet", "katana"]
else 6
if NETWORK["name"] in ["madara", "sharingan"]
else 15,
)
max_wait = kwargs.get(
"max_wait", 60 * 5 if NETWORK not in ["devnet", "katana", "madara"] else 30
"max_wait",
60 * 5 if NETWORK["name"] not in ["devnet", "katana", "madara"] else 30,
)
transaction_hash = args[0] if args else kwargs["tx_hash"]
status = None
Expand Down

0 comments on commit 50db72c

Please sign in to comment.