From f98dfceaf082b6469b81183aab7d52ba0e546dea Mon Sep 17 00:00:00 2001 From: Jordy Romuald <87231934+JordyRo1@users.noreply.github.com> Date: Thu, 21 Dec 2023 12:01:16 +0100 Subject: [PATCH] Feeder Gateway depreciation (#96) * feat: update to rpc * feat: change gateway to rpc * Env var changed * sepolia deployments/declarations * Katana instance run on CI * update ci: install katana * fix: katana installation * fix: katana bug * actions checkout * debug dojo * alternative installation * poetry install * fmt * clean code * specify python version * env variables * unnecessary code * secret permissions * indent * revert * dojo config * scarb * fix: typo * fix: review comments * fix: use public rpc * fix: readme * Update README.md --------- Co-authored-by: 0xevolve --- .env.example | 10 +- .github/workflows/run_scripts.yml | 48 ++++++++++ .gitignore | 1 + README.md | 28 +++--- deployments/mainnet/declarations.json | 2 +- deployments/sepolia/declarations.json | 9 ++ deployments/sepolia/deployments.json | 22 +++++ scripts/add_pairs.py | 16 +++- scripts/deploy_pragma.py | 27 ++++-- scripts/deploy_randomness.py | 46 +++++++--- scripts/deploy_summary_stats.py | 16 +++- scripts/katana.sh | 24 +++++ scripts/register_publishers.py | 19 +++- scripts/remove_publishers.py | 15 ++- scripts/remove_source.py | 17 +++- scripts/test_randomness.py | 28 ++++-- scripts/upgrade_pragma.py | 19 +++- scripts/utils/constants.py | 60 ++++++------ scripts/utils/starknet.py | 116 +++++++++++++++--------- src/randomness/example_randomness.cairo | 35 ++++++- 20 files changed, 426 insertions(+), 132 deletions(-) create mode 100644 .github/workflows/run_scripts.yml create mode 100644 deployments/sepolia/declarations.json create mode 100644 deployments/sepolia/deployments.json create mode 100755 scripts/katana.sh diff --git a/.env.example b/.env.example index 84a6410f..90c145c8 100644 --- a/.env.example +++ b/.env.example @@ -4,9 +4,6 @@ GITHUB_TOKEN= # Network configuration STARKNET_NETWORK=devnet -# An Infura API key (TODO: run a Starknode) -RPC_KEY= - # All addresses are hex strings MAINNET_ACCOUNT_ADDRESS= MAINNET_PRIVATE_KEY= @@ -23,3 +20,10 @@ DEVNET_PRIVATE_KEY= # These will be used if above variables are not set for the given network ACCOUNT_ADDRESS= PRIVATE_KEY= + +# RPC URL used by deployment scripts +RPC_URL= + +# Account with --seed=1 +KATANA_ACCOUNT_ADDRESS=0x7b245927f60309cb2fdcbc3f7d37a21ce5bc05a4305104b43b72b91aa3f53e1 +KATANA_PRIVATE_KEY=0x100801800000000310080180000000010030000000000005106801800206800 diff --git a/.github/workflows/run_scripts.yml b/.github/workflows/run_scripts.yml new file mode 100644 index 00000000..84ed9891 --- /dev/null +++ b/.github/workflows/run_scripts.yml @@ -0,0 +1,48 @@ +name: Run scripts + +on: pull_request + +jobs: + run_scripts: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + + + - name: Install rust + run: | + curl https://sh.rustup.rs -sSf | sh -s -- -y + + - name: Install dojoup + run: | + curl -L https://install.dojoengine.org | bash + + - name: Install dojo + run: |- + /home/runner/.config/.dojo/bin/dojoup + sudo mv /home/runner/.config/.dojo/bin/katana /usr/local/bin/ + + - name: Make script executable + run: chmod +x scripts/katana.sh + + # Set up Python and install Poetry + - name: Set up Python and install Poetry + uses: actions/setup-python@v2 + with: + python-version: '3.9' + - run: pip3 install poetry + + # Install Python dependencies with Poetry + - name: Install dependencies + run: poetry install + + # Set up Scarb + - uses: software-mansion/setup-scarb@v1 + - run: scarb build + + # Run Katana and scripts + - name: Run Katana and scripts + env: + KATANA_ACCOUNT_ADDRESS: ${{ secrets.KATANA_ACCOUNT_ADDRESS }} + KATANA_PRIVATE_KEY: ${{ secrets.KATANA_PRIVATE_KEY }} + run: bash scripts/katana.sh diff --git a/.gitignore b/.gitignore index 900fcb53..ad749517 100644 --- a/.gitignore +++ b/.gitignore @@ -28,6 +28,7 @@ coverage/* # Scripts artifacts blockhashes.json deployed_addresses.json +deployments/katana # thoth relateds stuff output-callgraph/* diff --git a/README.md b/README.md index 66e3250b..bac10eda 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,7 @@ Overview --- - Account contract mostly use for testing purposes and as a reference. -- Admin contract will be replaced by Argent's multisig as it gets released. +- Ownable contract used for access control. - Entry & Data Structures defines data structures used within the protocol along with generic aggregations methods. It is designed from the ground up to ensure that adding new entry types is done seamlessly without involving any breaking changes. - Operations defines a few utilities libraries (time series, sorting, bits manipulation) that will be used for different aggregation methods and optimizing storage operations. - Oracle is the main entrypoint of the protocol. It is the contract that end developers will interact with to fetch any kind of data. It's been thought and built for retro-compatibility and heavily leverages unique aspects of Cairo, notably enums, traits and generics. @@ -46,11 +46,11 @@ Deployment addresses This repo will gradually replace the previous Pragma implementation in Cairo 0 which you can find [here](https://github.com/Astraly-Labs/pragma-contracts). -**Starknet Testnet** -- Oracle : [0x06df335982dddce41008e4c03f2546fa27276567b5274c7d0c1262f3c2b5d167](https://goerli.voyager.online/contract/0x06df335982dddce41008e4c03f2546fa27276567b5274c7d0c1262f3c2b5d167) -- Publisher Registry : [0x552e96b3a9078a453ab7012ed76325bedef7b0cc5647a8fafe985e658e31d86](https://goerli.voyager.online/contract/0x552e96b3a9078a453ab7012ed76325bedef7b0cc5647a8fafe985e658e31d86) -- Summary Stats : [0x3bcd9362bfe80456a7ee66f5a569457cb21adc3f82129420fa12453abb7f353](https://goerli.voyager.online/contract/0x3bcd9362bfe80456a7ee66f5a569457cb21adc3f82129420fa12453abb7f353) -- VRF : [0x693d551265f0be7ccb3c869c64b5920929caaf486497788d43cb37dd17d6be6](https://goerli.voyager.online/contract/0x693d551265f0be7ccb3c869c64b5920929caaf486497788d43cb37dd17d6be6) +**Starknet Sepolia** +- Oracle : [0x5acaa0ef3a6837c98ddeae516fa3f6022018cf646914d921842e46fc00c35d3](https://goerli.voyager.online/contract/0x5acaa0ef3a6837c98ddeae516fa3f6022018cf646914d921842e46fc00c35d3) +- Publisher Registry : [0x732b618bc92e3919e28dd3b3aff21079b2507c1f51b8a194ce90300e995cb](https://goerli.voyager.online/contract/0x732b618bc92e3919e28dd3b3aff21079b2507c1f51b8a194ce90300e995cb) +- Summary Stats : [0x185b147a3e03043e0232ec6fcf17143614481cc29a505c7d9c4b29e98c1f14e](https://goerli.voyager.online/contract/0x185b147a3e03043e0232ec6fcf17143614481cc29a505c7d9c4b29e98c1f14e) +- VRF : [0x556bd6988d786777bb5b1ee337c63f60f30d8200818b59b29e54afc27266a46](https://goerli.voyager.online/contract/0x556bd6988d786777bb5b1ee337c63f60f30d8200818b59b29e54afc27266a46) **Starknet Mainnet** - Oracle : [0x2a85bd616f912537c50a49a4076db02c00b29b2cdc8a197ce92ed1837fa875b](https://voyager.online/contract/0x2a85bd616f912537c50a49a4076db02c00b29b2cdc8a197ce92ed1837fa875b) @@ -58,6 +58,12 @@ This repo will gradually replace the previous Pragma implementation in Cairo 0 w - Summary Stats : [0x49eefafae944d07744d07cc72a5bf14728a6fb463c3eae5bca13552f5d455fd](https://voyager.online/contract/0x49eefafae944d07744d07cc72a5bf14728a6fb463c3eae5bca13552f5d455fd) - VRF : 🔜 +**Starknet Goerli (Deprecated)** +- Oracle : [0x06df335982dddce41008e4c03f2546fa27276567b5274c7d0c1262f3c2b5d167](https://goerli.voyager.online/contract/0x06df335982dddce41008e4c03f2546fa27276567b5274c7d0c1262f3c2b5d167) +- Publisher Registry : [0x552e96b3a9078a453ab7012ed76325bedef7b0cc5647a8fafe985e658e31d86](https://goerli.voyager.online/contract/0x552e96b3a9078a453ab7012ed76325bedef7b0cc5647a8fafe985e658e31d86) +- Summary Stats : [0x3bcd9362bfe80456a7ee66f5a569457cb21adc3f82129420fa12453abb7f353](https://goerli.voyager.online/contract/0x3bcd9362bfe80456a7ee66f5a569457cb21adc3f82129420fa12453abb7f353) +- VRF : [0x693d551265f0be7ccb3c869c64b5920929caaf486497788d43cb37dd17d6be6](https://goerli.voyager.online/contract/0x693d551265f0be7ccb3c869c64b5920929caaf486497788d43cb37dd17d6be6) + Local Deployment --- @@ -80,15 +86,15 @@ scarb build 3. Deploy contracts & setup -Make sure your local devnet is running, see latest instructions [here](https://0xspaceshard.github.io/starknet-devnet/docs/intro). +Make sure your local devnet is running, see latest instructions [here](https://book.dojoengine.org/toolchain/katana/reference.html). -You can also specify a different network by setting `STARKNET_NETWORK` to a different value e.g `testnet | mainnet`. +You can also specify a different network by setting `STARKNET_NETWORK` to a different value e.g `sepolia | testnet | mainnet`. ```bash -STARKNET_NETWORK=devnet poetry run python3 scripts/deploy_pragma.py -STARKNET_NETWORK=devnet poetry run python3 scripts/deploy_summary_stats.py -STARKNET_NETWORK=devnet poetry run python3 scripts/register_publishers.py +STARKNET_NETWORK=katana poetry run python3 scripts/deploy_pragma.py +STARKNET_NETWORK=katana poetry run python3 scripts/deploy_summary_stats.py +STARKNET_NETWORK=katana poetry run python3 scripts/register_publishers.py ``` diff --git a/deployments/mainnet/declarations.json b/deployments/mainnet/declarations.json index cd67e6f9..44c849e9 100644 --- a/deployments/mainnet/declarations.json +++ b/deployments/mainnet/declarations.json @@ -1,6 +1,6 @@ { "pragma_Oracle": "0x40a2430b48587833abc2e912335cffd863c010e2c798d005d9bcace56a156fc", - "pragma_Admin": "0x60203a3ff6a165f70e5ce47a82c500d3a5b79097e0db32ec34b0959d8e8af54", + "pragma_Ownable": "0x60203a3ff6a165f70e5ce47a82c500d3a5b79097e0db32ec34b0959d8e8af54", "pragma_PublisherRegistry": "0x306288971002bd7906e3a607d504dfb28dcbdc7655a115984e567dce3b67e8f", "pragma_SummaryStats": "0x3b5bf1c2f918de14620b3c63e2c6543cf0a138df258bce3d03e3fb0b7d2183a", "pragma_Randomness": "0x3b5d93672493bdd1c0881bfa590c6cea59e4cec8659272ea340da5b8067016c", diff --git a/deployments/sepolia/declarations.json b/deployments/sepolia/declarations.json new file mode 100644 index 00000000..b8d4f548 --- /dev/null +++ b/deployments/sepolia/declarations.json @@ -0,0 +1,9 @@ +{ + "pragma_Oracle": "0x40a2430b48587833abc2e912335cffd863c010e2c798d005d9bcace56a156fc", + "pragma_Ownable": "0x7b50a7a9bbb75d08248135003f1c87f0d44ba23018dc0ca480ac37a901531f3", + "pragma_PublisherRegistry": "0x306288971002bd7906e3a607d504dfb28dcbdc7655a115984e567dce3b67e8f", + "pragma_SummaryStats": "0x3b5bf1c2f918de14620b3c63e2c6543cf0a138df258bce3d03e3fb0b7d2183a", + "pragma_Randomness": "0x1a4de6fd18f5358f9c815ca98b08c707c77e6583f10e5ebf83daa6541217e2a", + "pragma_ExampleRandomness": "0x521a9f8d2db3faf168854902adb6bda2648d1de695c3cfa88946080ec567cbb", + "pragma_YieldCurve": "0x55a7af6d2df5730c784ec31ca9f71e66f19e8cb78359d23f51aececcc1c04eb" +} \ No newline at end of file diff --git a/deployments/sepolia/deployments.json b/deployments/sepolia/deployments.json new file mode 100644 index 00000000..ce3b0e93 --- /dev/null +++ b/deployments/sepolia/deployments.json @@ -0,0 +1,22 @@ +{ + "pragma_PublisherRegistry": { + "address": "0x732b618bc92e3919e28dd3b3aff21079b2507c1f51b8a194ce90300e995cb", + "tx": "0x696821818ebcaf3052d9af16b2e3250dfea98fb4ab8d9caccd1f7e8ffced82f" + }, + "pragma_Oracle": { + "address": "0x5acaa0ef3a6837c98ddeae516fa3f6022018cf646914d921842e46fc00c35d3", + "tx": "0x6700eac1d61330b9831384e358985681ccaf90eea540f299f9fc58550372e17" + }, + "pragma_SummaryStats": { + "address": "0x185b147a3e03043e0232ec6fcf17143614481cc29a505c7d9c4b29e98c1f14e", + "tx": "0x5586671f1d7d1e7744ca88b19927ab12ef9ac233d70f2f9bbed650c32bcd38f" + }, + "pragma_Randomness": { + "address": "0x556bd6988d786777bb5b1ee337c63f60f30d8200818b59b29e54afc27266a46", + "tx": "0x201c3bfe0ea8d3476f97d7430d947e804e58c9184ee80ffab2a2d0582a7aaf2" + }, + "pragma_ExampleRandomness": { + "address": "0x6b95c03d088974e17cc87a3b9b2650d7b0c7098e2ec968f6d852249e808f5dc", + "tx": "0x226ec4801c9b3e7428ffcbec16c75bb3cf7c90ed2c9e189ac8d310e41157665" + } +} \ No newline at end of file diff --git a/scripts/add_pairs.py b/scripts/add_pairs.py index 183cf596..a5f8891d 100644 --- a/scripts/add_pairs.py +++ b/scripts/add_pairs.py @@ -21,12 +21,15 @@ call, ) from pragma.core.types import Currency, Pair - +import argparse +import os +from dotenv import load_dotenv logging.basicConfig() logger = logging.getLogger(__name__) logger.setLevel(logging.INFO) +load_dotenv() currencies_to_add = [] pairs_to_add = [ Pair("ETH/USDT", "ETH", "USDT"), @@ -35,6 +38,14 @@ async def main(): + parser = argparse.ArgumentParser(description="Deploy contracts to Katana") + parser.add_argument('--port', type=int, help='Port number(not required)', required=False) + args = parser.parse_args() + if os.getenv("STARKNET_NETWORK") == "katana" and args.port is None: + logger.warning( + f"⚠️ --port not set, defaulting to 5050" + ) + args.port = 5050 # Add Currencies for currency in currencies_to_add: print(currency.to_dict()) @@ -42,12 +53,13 @@ async def main(): "pragma_Oracle", "add_currency", currency.serialize(), + port = args.port ) logger.info(f"Added currency {currency} with tx hash {hex(tx_hash)}") # Add Pairs for pair in pairs_to_add: - tx_hash = await invoke("pragma_Oracle", "add_pair", pair.serialize()) + tx_hash = await invoke("pragma_Oracle", "add_pair", pair.serialize(), port= args.port) logger.info(f"Added pair {pair} with tx hash {hex(tx_hash)}") diff --git a/scripts/deploy_pragma.py b/scripts/deploy_pragma.py index 6d7f995d..948606b1 100644 --- a/scripts/deploy_pragma.py +++ b/scripts/deploy_pragma.py @@ -2,13 +2,15 @@ import logging from asyncio import run from math import ceil, log - +import argparse from scripts.utils.constants import ( COMPILED_CONTRACTS, currencies, NETWORK, pairs, ) +import os +from dotenv import load_dotenv from scripts.utils.starknet import ( dump_declarations, dump_deployments, @@ -21,6 +23,7 @@ call ) +load_dotenv() logging.basicConfig() logger = logging.getLogger(__name__) logger.setLevel(logging.INFO) @@ -28,28 +31,39 @@ # %% Main async def main(): + + #Retrieve port from parser + parser = argparse.ArgumentParser(description="Deploy contracts to Katana") + parser.add_argument('--port', type=int, help='Port number(not required)', required=False) + args = parser.parse_args() + if os.getenv("STARKNET_NETWORK") == "katana" and args.port is None: + logger.warning( + f"⚠️ --port not set, defaulting to 5050" + ) + args.port = 5050 # %% Declarations chain_id = NETWORK["chain_id"] logger.info( f"ℹ️ Connected to CHAIN_ID { chain_id }" ) - account = await get_starknet_account() + account = await get_starknet_account(port= args.port) logger.info(f"ℹ️ Using account {hex(account.address)} as deployer") class_hash = { - contract["contract_name"]: await declare_v2(contract["contract_name"]) + contract["contract_name"]: await declare_v2(contract["contract_name"], args.port) for contract in COMPILED_CONTRACTS } dump_declarations(class_hash) # %% Deployments class_hash = get_declarations() - await get_eth_contract() + await get_eth_contract(port= args.port) deployments = {} deployments["pragma_PublisherRegistry"] = await deploy_v2( "pragma_PublisherRegistry", - account.address, # owner + account.address, # owner, + port =args.port ) new_currencies = [currency.to_dict() for currency in currencies] @@ -61,8 +75,9 @@ async def main(): deployments["pragma_PublisherRegistry"]["address"], # publisher_registry new_currencies, new_pairs, + port =args.port ) - + dump_deployments(deployments) logger.info("✅ Deployment Completed") diff --git a/scripts/deploy_randomness.py b/scripts/deploy_randomness.py index 7e3df386..d8e823df 100644 --- a/scripts/deploy_randomness.py +++ b/scripts/deploy_randomness.py @@ -2,7 +2,9 @@ import logging from asyncio import run from math import ceil, log - +import os +from dotenv import load_dotenv +import argparse from scripts.utils.constants import ( COMPILED_CONTRACTS, currencies, @@ -19,6 +21,8 @@ dump_declarations ) +load_dotenv() + logging.basicConfig() logger = logging.getLogger(__name__) logger.setLevel(logging.INFO) @@ -26,29 +30,45 @@ # %% Main async def main(): + parser = argparse.ArgumentParser(description="Deploy contracts to Katana") + parser.add_argument('--port', type=int, help='Port number(not required)', required=False) + args = parser.parse_args() + if os.getenv("STARKNET_NETWORK") == "katana" and args.port is None: + logger.warning( + f"⚠️ --port not set, defaulting to 5050" + ) + args.port = 5050 # %% Declarations chain_id = NETWORK["chain_id"] logger.info( f"ℹ️ Connected to CHAIN_ID { chain_id }" ) - account = await get_starknet_account() + account = await get_starknet_account(port= args.port) logger.info(f"ℹ️ Using account {hex(account.address)} as deployer") - # class_hash = { - # contract["contract_name"]: await declare_v2(contract["contract_name"]) - # for contract in COMPILED_CONTRACTS - # } - # dump_declarations(class_hash) + if args.port is not None: + class_hash = { + contract["contract_name"]: await declare_v2(contract["contract_name"], port=args.port) + for contract in COMPILED_CONTRACTS + } + dump_declarations(class_hash) # %% Deployment + # deployments = get_deployments() + # deployments["pragma_Randomness"] = await deploy_v2( + # "pragma_Randomness", + # int(os.getenv("TESTNET_ACCOUNT_ADDRESS"), 16), + # 2061139992776959994838533810929826594222370735645675137341826408353556487187, + # ETH_TOKEN_ADDRESS, + # int(deployments["pragma_Oracle"]["address"], 16), + # port=args.port + # ) deployments = get_deployments() - deployments["pragma_Randomness"] = await deploy_v2( - "pragma_Randomness", - int("0x03e437FB56Bb213f5708Fcd6966502070e276c093ec271aA33433b89E21fd31f", 16), - int("0xfcfc1eda34191fdb06acb883c4b5d8a70db47711252c26d77094053160ba5e", 16), - ETH_TOKEN_ADDRESS, - int(deployments["pragma_Oracle"]["address"], 16), + deployments["pragma_ExampleRandomness"] = await deploy_v2( + "pragma_ExampleRandomness", + int("0x5faa12cb652c1ec3cf667e651d001d8155653d8d8ad2d1ab92bd965e081a605", 16), + port = args.port ) dump_deployments(deployments) diff --git a/scripts/deploy_summary_stats.py b/scripts/deploy_summary_stats.py index 79afef56..116f5920 100644 --- a/scripts/deploy_summary_stats.py +++ b/scripts/deploy_summary_stats.py @@ -2,7 +2,9 @@ import logging from asyncio import run from math import ceil, log - +import argparse +import os +from dotenv import load_dotenv from scripts.utils.constants import ( COMPILED_CONTRACTS, currencies, @@ -20,15 +22,24 @@ logger = logging.getLogger(__name__) logger.setLevel(logging.INFO) +load_dotenv() # %% Main async def main(): + parser = argparse.ArgumentParser(description="Deploy contracts to Katana") + parser.add_argument('--port', type=int, help='Port number(not required)', required=False) + args = parser.parse_args() + if os.getenv("STARKNET_NETWORK") == "katana" and args.port is None: + logger.warning( + f"⚠️ --port not set, defaulting to 5050" + ) + args.port = 5050 # %% Declarations chain_id = NETWORK["chain_id"] logger.info( f"ℹ️ Connected to CHAIN_ID { chain_id }" ) - account = await get_starknet_account() + account = await get_starknet_account(port = args.port) logger.info(f"ℹ️ Using account {hex(account.address)} as deployer") # %% Deployment @@ -37,6 +48,7 @@ async def main(): deployments["pragma_SummaryStats"] = await deploy_v2( "pragma_SummaryStats", int(deployments["pragma_Oracle"]["address"], 16), # oracle address + port = args.port ) dump_deployments(deployments) diff --git a/scripts/katana.sh b/scripts/katana.sh new file mode 100755 index 00000000..a89bbb47 --- /dev/null +++ b/scripts/katana.sh @@ -0,0 +1,24 @@ +#!/bin/bash + +# Find available port using Python +PORT=$(python3 -c 'import socket; s=socket.socket(); s.bind(("", 0)); print(s.getsockname()[1]); s.close()') + +echo "Starting katana on port $PORT" +katana --chain-id SN_GOERLI --host 127.0.0.1 --port $PORT --accounts "1" --seed "1" & + +while ! nc -z localhost $PORT; do + sleep 0.1 # wait for 1/10 of the second before check again +done + +# Run your Python script +echo "Running deploy_pragma.py with port $PORT" +STARKNET_NETWORK=katana poetry run python3 scripts/deploy_pragma.py --port $PORT +STARKNET_NETWORK=katana poetry run python3 scripts/add_pairs.py --port $PORT +STARKNET_NETWORK=katana poetry run python3 scripts/register_publishers.py --port $PORT +STARKNET_NETWORK=katana poetry run python3 scripts/deploy_summary_stats.py --port $PORT +STARKNET_NETWORK=katana poetry run python3 scripts/test_randomness.py --port $PORT +STARKNET_NETWORK=katana poetry run python3 scripts/upgrade_pragma.py --port $PORT +# STARKNET_NETWORK=katana poetry run python3 scripts/remove_source.py --port $PORT +# STARKNET_NETWORK=katana poetry run python3 scripts/remove_publishers.py --port $PORT + + diff --git a/scripts/register_publishers.py b/scripts/register_publishers.py index bbd32e61..27ca01a1 100644 --- a/scripts/register_publishers.py +++ b/scripts/register_publishers.py @@ -9,6 +9,9 @@ NETWORK, pairs, ) +import os +from dotenv import load_dotenv +import argparse from scripts.utils.starknet import ( dump_declarations, dump_deployments, @@ -23,6 +26,7 @@ str_to_felt, ) +load_dotenv() logging.basicConfig() logger = logging.getLogger(__name__) logger.setLevel(logging.INFO) @@ -125,15 +129,23 @@ # %% Main async def main(): + parser = argparse.ArgumentParser(description="Deploy contracts to Katana") + parser.add_argument('--port', type=int, help='Port number', required=False) + args = parser.parse_args() + if os.getenv("STARKNET_NETWORK") == "katana" and args.port is None: + logger.warning( + f"⚠️ --port not set, defaulting to 5050" + ) + args.port = 5050 for publisher, sources, address in zip( publishers, publishers_sources, publisher_address ): (existing_address,) = await call( - "pragma_PublisherRegistry", "get_publisher_address", publisher + "pragma_PublisherRegistry", "get_publisher_address", publisher, port = args.port ) if existing_address == 0: tx_hash = await invoke( - "pragma_PublisherRegistry", "add_publisher", [publisher, address] + "pragma_PublisherRegistry", "add_publisher", [publisher, address],port = args.port ) logger.info(f"Registered new publisher {publisher} with tx {hex(tx_hash)}") elif existing_address != address: @@ -143,7 +155,7 @@ async def main(): return (existing_sources,) = await call( - "pragma_PublisherRegistry", "get_publisher_sources", publisher + "pragma_PublisherRegistry", "get_publisher_sources", publisher,port = args.port ) new_sources = [x for x in sources if str_to_felt(x) not in existing_sources] if len(new_sources) > 0: @@ -151,6 +163,7 @@ async def main(): "pragma_PublisherRegistry", "add_sources_for_publisher", [publisher, len(new_sources), *new_sources], + port = args.port ) logger.info( f"Registered sources {new_sources} for publisher {publisher} with tx {hex(tx_hash)}" diff --git a/scripts/remove_publishers.py b/scripts/remove_publishers.py index 37559390..60b02361 100644 --- a/scripts/remove_publishers.py +++ b/scripts/remove_publishers.py @@ -22,7 +22,11 @@ get_deployments, str_to_felt, ) +import os +import argparse +from dotenv import load_dotenv +load_dotenv() logging.basicConfig() logger = logging.getLogger(__name__) logger.setLevel(logging.INFO) @@ -37,11 +41,20 @@ # %% Main async def main(): + parser = argparse.ArgumentParser(description="Deploy contracts to Katana") + parser.add_argument('--port', type=int, help='Port number(not required)', required=False) + args = parser.parse_args() + if os.getenv("STARKNET_NETWORK") == "katana" and args.port is None: + logger.warning( + f"⚠️ --port not set, defaulting to 5050" + ) + args.port = 5050 for publisher in publishers: await invoke( "pragma_PublisherRegistry", "remove_publisher", - [str_to_felt(publisher)]) + [str_to_felt(publisher)], + port = args.port) logger.info(f"ℹ️ Removed publisher {publisher}.") diff --git a/scripts/remove_source.py b/scripts/remove_source.py index 84037532..8d8bfb81 100644 --- a/scripts/remove_source.py +++ b/scripts/remove_source.py @@ -7,6 +7,9 @@ COMPILED_CONTRACTS, NETWORK, ) +import os +import argparse +from dotenv import load_dotenv from scripts.utils.starknet import ( dump_declarations, dump_deployments, @@ -20,10 +23,12 @@ str_to_felt ) + from pragma.core.types import DataType, DataTypes from starknet_py.serialization.data_serializers.enum_serializer import EnumSerializer +load_dotenv() logging.basicConfig() logger = logging.getLogger(__name__) logger.setLevel(logging.INFO) @@ -33,11 +38,19 @@ # %% Main async def main(): + parser = argparse.ArgumentParser(description="Deploy contracts to Katana") + parser.add_argument('--port', type=int, help='Port number(not required)', required=False) + args = parser.parse_args() + if os.getenv("STARKNET_NETWORK") == "katana" and args.port is None: + logger.warning( + f"⚠️ --port not set, defaulting to 5050" + ) + args.port = 5050 # %% Declarations chain_id = NETWORK["chain_id"] logger.info(f"ℹ️ Connected to CHAIN_ID { chain_id }") - account = await get_starknet_account() + account = await get_starknet_account(port=args.port) logger.info(f"ℹ️ Using account {hex(account.address)} as upgrader") for pair_id in pair_ids: @@ -49,7 +62,7 @@ async def main(): "Pair ID must be string (will be converted to felt) or integer" ) - tx_hash = await invoke("pragma_Oracle", "remove_source", ["AVNU", 0, pair_id]) + tx_hash = await invoke("pragma_Oracle", "remove_source", ["AVNU", 0, pair_id], port=args.port) logger.info(f"Removed source for pair {pair_id} with tx {hex(tx_hash)}") logger.info(f"Upgraded the oracle contract with tx {hex(tx_hash)}") diff --git a/scripts/test_randomness.py b/scripts/test_randomness.py index 9b66550c..44a41998 100644 --- a/scripts/test_randomness.py +++ b/scripts/test_randomness.py @@ -9,6 +9,9 @@ NETWORK, pairs, ) +import os +import argparse +from dotenv import load_dotenv from scripts.utils.starknet import ( dump_deployments, get_deployments, @@ -18,6 +21,7 @@ dump_declarations ) +load_dotenv() logging.basicConfig() logger = logging.getLogger(__name__) logger.setLevel(logging.INFO) @@ -25,19 +29,28 @@ # %% Main async def main(): + parser = argparse.ArgumentParser(description="Deploy contracts to Katana") + parser.add_argument('--port', type=int, help='Port number(not required)', required=False) + args = parser.parse_args() + if os.getenv("STARKNET_NETWORK") == "katana" and args.port is None: + logger.warning( + f"⚠️ --port not set, defaulting to 5050" + ) + args.port = 5050 # %% Declarations chain_id = NETWORK["chain_id"] logger.info( f"ℹ️ Connected to CHAIN_ID { chain_id }" ) - account = await get_starknet_account() + account = await get_starknet_account(port = args.port) logger.info(f"ℹ️ Using account {hex(account.address)} as deployer") - - class_hash = { - contract["contract_name"]: await declare_v2(contract["contract_name"]) - for contract in COMPILED_CONTRACTS - } - dump_declarations(class_hash) + + if args.port is not None: + class_hash = { + contract["contract_name"]: await declare_v2(contract["contract_name"],port = args.port) + for contract in COMPILED_CONTRACTS + } + dump_declarations(class_hash) # %% Deployment @@ -45,6 +58,7 @@ async def main(): deployments["pragma_ExampleRandomness"] = await deploy_v2( "pragma_ExampleRandomness", int(deployments["pragma_Randomness"]["address"], 16), + port = args.port ) dump_deployments(deployments) diff --git a/scripts/upgrade_pragma.py b/scripts/upgrade_pragma.py index 20e85383..2d0fff8e 100755 --- a/scripts/upgrade_pragma.py +++ b/scripts/upgrade_pragma.py @@ -18,7 +18,12 @@ declare_v2, call, ) +import os +import argparse +from dotenv import load_dotenv + +load_dotenv() logging.basicConfig() logger = logging.getLogger(__name__) logger.setLevel(logging.INFO) @@ -26,21 +31,29 @@ # %% Main async def main(): + parser = argparse.ArgumentParser(description="Deploy contracts to Katana") + parser.add_argument('--port', type=int, help='Port number(not required)', required=False) + args = parser.parse_args() + if os.getenv("STARKNET_NETWORK") == "katana" and args.port is None: + logger.warning( + f"⚠️ --port not set, defaulting to 5050" + ) + args.port = 5050 # %% Declarations chain_id = NETWORK["chain_id"] logger.info(f"ℹ️ Connected to CHAIN_ID { chain_id }") - account = await get_starknet_account() + account = await get_starknet_account(port = args.port) logger.info(f"ℹ️ Using account {hex(account.address)} as upgrader") declarations = get_declarations() cur_class_hash = declarations["pragma_Oracle"] logger.info(f"ℹ️ Current implementation hash: {cur_class_hash}") - new_implementation_hash = await declare_v2("pragma_Oracle") + new_implementation_hash = await declare_v2("pragma_Oracle", port = args.port) logger.info(f"ℹ️ New implementation hash: {new_implementation_hash}") - tx_hash = await invoke("pragma_Oracle", "upgrade", [new_implementation_hash]) + tx_hash = await invoke("pragma_Oracle", "upgrade", [new_implementation_hash], port = args.port) declarations["pragma_Oracle"] = new_implementation_hash dump_declarations(declarations) diff --git a/scripts/utils/constants.py b/scripts/utils/constants.py index 1d3e1843..79cf815b 100644 --- a/scripts/utils/constants.py +++ b/scripts/utils/constants.py @@ -5,7 +5,6 @@ from dotenv import load_dotenv from starknet_py.net.full_node_client import FullNodeClient -from starknet_py.net.gateway_client import GatewayClient from starknet_py.net.models.chains import StarknetChainId from pragma.core.types import Currency, Pair from typing import List @@ -16,36 +15,35 @@ logger = logging.getLogger(__name__) logger.setLevel(logging.INFO) -ETH_TOKEN_ADDRESS = 0x49D36570D4E46F48E99674BD3FCC84644DDD6B96F7C741B1562B82F9E004DC7 +MAX_FEE = 70000000000000000 # 0.07 ETH + +ETH_TOKEN_ADDRESS = "0x49D36570D4E46F48E99674BD3FCC84644DDD6B96F7C741B1562B82F9E004DC7" NETWORKS = { "mainnet": { "name": "mainnet", - "feeder_gateway_url": "https://alpha-mainnet.starknet.io/feeder_gateway", - "gateway_url": "https://alpha-mainnet.starknet.io/gateway", + "rpc_url": f"https://starknet-mainnet.public.blastapi.io", }, "testnet": { "name": "testnet", "explorer_url": "https://testnet.starkscan.co", - "rpc_url": f"https://starknet-goerli.infura.io/v3/{os.getenv('INFURA_KEY')}", - "feeder_gateway_url": "https://alpha4.starknet.io/feeder_gateway", - "gateway_url": "https://alpha4.starknet.io/gateway", + "rpc_url": f"https://starknet-testnet.public.blastapi.io", }, "devnet": { "name": "devnet", "explorer_url": "https://devnet.starkscan.co", "rpc_url": "http://127.0.0.1:5050/rpc", - "feeder_gateway_url": "http://localhost:5050/feeder_gateway", - "gateway_url": "http://localhost:5050/gateway", }, - # "katana": { - # "name": "katana", - # "explorer_url": "", - # "rpc_url": "http://127.0.0.1:5050", - # "devnet": True, - # "check_interval": 0.1, - # "max_wait": 1, - # }, + "sepolia": { + "name": "sepolia", + "explorer_url": "https://sepolia.starkscan.co/", + "rpc_url": "https://starknet-sepolia.public.blastapi.io", + }, + "katana": { + "name": "katana", + "explorer_url": "", + "rpc_url": "http://127.0.0.1:5050/rpc", + }, } NETWORK = NETWORKS[os.getenv("STARKNET_NETWORK", "devnet")] @@ -65,19 +63,19 @@ NETWORK["private_key"] = os.getenv("PRIVATE_KEY") if NETWORK["name"] == "mainnet": NETWORK["chain_id"] = StarknetChainId.MAINNET -elif NETWORK["name"] == "testnet2": - StarknetChainId.TESTNET2 +elif NETWORK["name"] == "sepolia": + NETWORK[ + "chain_id" + ] = 393402133025997798000961 # TODO: replace with starknet_py upgrade else: NETWORK["chain_id"] = StarknetChainId.TESTNET -GATEWAY_CLIENT = GatewayClient( - net={ - "feeder_gateway_url": NETWORK["feeder_gateway_url"], - "gateway_url": NETWORK["gateway_url"], - } +FULLNODE_CLIENT = FullNodeClient( + node_url=NETWORK["rpc_url"], ) + BUILD_DIR = Path("target/dev") BUILD_DIR.mkdir(exist_ok=True, parents=True) @@ -89,7 +87,7 @@ COMPILED_CONTRACTS = [ {"contract_name": "pragma_Oracle", "is_account_contract": False}, - {"contract_name": "pragma_Admin", "is_account_contract": False}, + {"contract_name": "pragma_Ownable", "is_account_contract": False}, {"contract_name": "pragma_PublisherRegistry", "is_account_contract": False}, {"contract_name": "pragma_SummaryStats", "is_account_contract": False}, {"contract_name": "pragma_Randomness", "is_account_contract": False}, @@ -161,22 +159,22 @@ "RETH", 18, 0, - 0x0319111a5037cbec2b3e638cc34a3474e2d2608299f3e62866e9cc683208c610, - 0xae78736cd615f374d3085123a210448e74fc6393, + 0x0319111A5037CBEC2B3E638CC34A3474E2D2608299F3E62866E9CC683208C610, + 0xAE78736CD615F374D3085123A210448E74FC6393, ), Currency( "LUSD", 18, 0, - 0x070a76fd48ca0ef910631754d77dd822147fe98a569b826ec85e3c33fde586ac, - 0x5f98805a4e8be255a32880fdec7f6728c6568ba0, + 0x070A76FD48CA0EF910631754D77DD822147FE98A569B826EC85E3C33FDE586AC, + 0x5F98805A4E8BE255A32880FDEC7F6728C6568BA0, ), Currency( "UNI", 18, 0, - 0x049210ffc442172463f3177147c1aeaa36c51d152c1b0630f2364c300d4f48ee, - 0x1f9840a85d5af5bf1d1762f925bdaddc4201f984, + 0x049210FFC442172463F3177147C1AEAA36C51D152C1B0630F2364C300D4F48EE, + 0x1F9840A85D5AF5BF1D1762F925BDADDC4201F984, ), ] pairs = [ diff --git a/scripts/utils/starknet.py b/scripts/utils/starknet.py index ae2c420a..adbd76fa 100644 --- a/scripts/utils/starknet.py +++ b/scripts/utils/starknet.py @@ -5,13 +5,14 @@ import logging from pathlib import Path -import requests from caseconverter import snakecase from starknet_py.contract import Contract from starknet_py.net.account.account import Account from starknet_py.net.client_models import Call from starknet_py.net.signer.stark_curve_signer import KeyPair from starkware.starknet.public.abi import get_selector_from_name +from starknet_py.net.full_node_client import FullNodeClient + from starknet_py.common import create_casm_class, create_sierra_compiled_contract from starknet_py.hash.casm_class_hash import compute_casm_class_hash @@ -23,11 +24,13 @@ # CONTRACTS, DEPLOYMENTS_DIR, ETH_TOKEN_ADDRESS, + MAX_FEE, NETWORK, - GATEWAY_CLIENT, + FULLNODE_CLIENT, # SOURCE_DIR, ) + logging.basicConfig() logger = logging.getLogger(__name__) logger.setLevel(logging.INFO) @@ -39,6 +42,7 @@ def int_to_uint256(value): high = value >> 128 return {"low": low, "high": high} + def str_to_felt(text): if text.upper() != text: logger.warning(f"Converting lower to uppercase for str_to_felt: {text}") @@ -47,15 +51,17 @@ def str_to_felt(text): return int.from_bytes(b_text, "big") -async def get_starknet_account( - address=None, - private_key=None, -) -> Account: +def get_katana_fullnode_client(port): + return FullNodeClient(node_url=f"http://127.0.0.1:{port}/rpc") + + +async def get_starknet_account(address=None, private_key=None, port=None) -> Account: 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 NETWORK["private_key"] if private_key is None: @@ -63,30 +69,38 @@ async def get_starknet_account( "private_key was not given in arg nor in env variable, see README.md#Deploy" ) key_pair = KeyPair.from_private_key(int(private_key, 16)) - - return Account( - address=address, - client=GATEWAY_CLIENT, - chain=NETWORK["chain_id"], - key_pair=key_pair, - ) + if port is None: + return Account( + address=address, + client=FULLNODE_CLIENT, + chain=NETWORK["chain_id"], + key_pair=key_pair, + ) + else: + return Account( + address=address, + client=get_katana_fullnode_client(port=port), + chain=NETWORK["chain_id"], + key_pair=key_pair, + ) -async def get_eth_contract() -> Contract: +async def get_eth_contract(port=None) -> Contract: return Contract( ETH_TOKEN_ADDRESS, json.loads((Path("scripts") / "utils" / "erc20.json").read_text())["abi"], - await get_starknet_account(), + await get_starknet_account(port=port), ) -async def get_contract(contract_name) -> Contract: +async def get_contract(contract_name, port=None) -> Contract: return Contract( get_deployments()[contract_name]["address"], json.loads(get_artifact(contract_name).read_text())["abi"], - await get_starknet_account(), + await get_starknet_account(port=port), ) + def dump_declarations(declarations): json.dump( {name: hex(class_hash) for name, class_hash in declarations.items()}, @@ -109,8 +123,12 @@ def dump_deployments(deployments): { name: { **deployment, - "address": hex(deployment["address"]) if type(deployment["address"]) == int else deployment["address"], - "tx": hex(deployment["tx"]) if type(deployment["tx"]) == int else deployment["tx"], + "address": hex(deployment["address"]) + if type(deployment["address"]) == int + else deployment["address"], + "tx": hex(deployment["tx"]) + if type(deployment["tx"]) == int + else deployment["tx"], } for name, deployment in deployments.items() }, @@ -134,21 +152,27 @@ def get_alias(contract_name): def get_tx_url(tx_hash: int) -> str: return f"{NETWORK['explorer_url']}/tx/0x{tx_hash:064x}" + def get_sierra_artifact(contract_name): return BUILD_DIR / f"{contract_name}.sierra.json" + def get_casm_artifact(contract_name): return BUILD_DIR / f"{contract_name}.casm.json" + def get_abi(contract_name): sierra_artifact = get_sierra_artifact(contract_name) contract_compiled_sierra = Path(sierra_artifact).read_text() - return create_sierra_compiled_contract(compiled_contract = contract_compiled_sierra).abi + return create_sierra_compiled_contract( + compiled_contract=contract_compiled_sierra + ).abi + -async def declare_v2(contract_name): +async def declare_v2(contract_name, port=None): logger.info(f"ℹ️ Declaring {contract_name}") - # contract_compiled_casm is a string containing the content of the starknet-sierra-compile (.casm file) + # contract_compiled_casm is a string containing the content of the starknet-sierra-compile (.casm file) casm_artifact = get_casm_artifact(contract_name) contract_compiled_casm = Path(casm_artifact).read_text() casm_class = create_casm_class(contract_compiled_casm) @@ -158,21 +182,24 @@ async def declare_v2(contract_name): sierra_artifact = get_sierra_artifact(contract_name) contract_compiled_sierra = Path(sierra_artifact).read_text() sierra_class = create_sierra_compiled_contract(contract_compiled_sierra) - sierra_class_hash= compute_sierra_class_hash(sierra_class) + sierra_class_hash = compute_sierra_class_hash(sierra_class) # Check has not been declared before + fullnode_client = ( + FULLNODE_CLIENT if port is None else get_katana_fullnode_client(port=port) + ) try: - await GATEWAY_CLIENT.get_class_by_hash(class_hash=sierra_class_hash) + await fullnode_client.get_class_by_hash(class_hash=sierra_class_hash) logger.info(f"✅ Class already declared, skipping") return sierra_class_hash except Exception: pass # Create Declare v2 transaction - account = await get_starknet_account() + account = await get_starknet_account(port=port) declare_v2_transaction = await account.sign_declare_v2_transaction( compiled_contract=contract_compiled_sierra, compiled_class_hash=casm_class_hash, - max_fee=int(1e17), + max_fee=MAX_FEE, ) # Send Declare v2 transaction @@ -182,21 +209,22 @@ async def declare_v2(contract_name): logger.info(f"✅ {contract_name} class hash: {hex(resp.class_hash)}") return resp.class_hash -async def deploy_v2(contract_name, *args): + +async def deploy_v2(contract_name, *args, port=None): logger.info(f"ℹ️ Deploying {contract_name}") - account = await get_starknet_account() + account = await get_starknet_account(port=port) sierra_class_hash = get_declarations()[contract_name] abi = get_abi(contract_name) - + deploy_result = await Contract.deploy_contract( account=account, class_hash=sierra_class_hash, abi=json.loads(abi), constructor_args=list(args), cairo_version=1, - max_fee=int(1e17), + max_fee=MAX_FEE, ) await deploy_result.wait_for_acceptance() @@ -212,17 +240,19 @@ async def deploy_v2(contract_name, *args): } -async def invoke(contract_name, function_name, inputs, address=None): - account = await get_starknet_account() +async def invoke(contract_name, function_name, inputs, address=None, port=None): + account = await get_starknet_account(port=port) deployments = get_deployments() call = Call( - to_addr=int(deployments[contract_name]["address"], 16) if address is None else address, - selector=get_selector_from_name(function_name), - calldata=inputs + to_addr=int(deployments[contract_name]["address"], 16) + if address is None + else address, + selector=get_selector_from_name(function_name), + calldata=inputs, ) print("call", call) logger.info(f"ℹ️ Invoking {contract_name}.{function_name}({json.dumps(inputs)})") - response = await account.execute(calls=call, max_fee=int(1e17)) + response = await account.execute(calls=call, max_fee=MAX_FEE) await account.client.wait_for_tx(response.transaction_hash) logger.info( f"✅ {contract_name}.{function_name} invoked at tx: %s", @@ -230,6 +260,7 @@ async def invoke(contract_name, function_name, inputs, address=None): ) return response.transaction_hash + async def invoke_cairo0(contract_name, function_name, *inputs, address=None): account = await get_starknet_account() deployments = get_deployments() @@ -238,22 +269,23 @@ async def invoke_cairo0(contract_name, function_name, *inputs, address=None): json.load(open(get_artifact(contract_name)))["abi"], account, ) - call = contract.functions[function_name].prepare(*inputs, max_fee=int(1e17)) + call = contract.functions[function_name].prepare(*inputs, max_fee=MAX_FEE) logger.info(f"ℹ️ Invoking {contract_name}.{function_name}({json.dumps(inputs)})") - response = await account.execute(call, max_fee=int(1e17)).wait_for_acceptance() + response = await account.execute(call, max_fee=MAX_FEE).wait_for_acceptance() logger.info( f"✅ {contract_name}.{function_name} invoked at tx: %s", hex(response.transaction_hash), ) return response.transaction_hash -async def call(contract_name, function_name, *inputs, address=None): + +async def call(contract_name, function_name, *inputs, address=None, port=None): deployments = get_deployments() - account = await get_starknet_account() + account = await get_starknet_account(port=port) contract = Contract( deployments[contract_name]["address"] if address is None else address, json.loads(get_abi(contract_name=contract_name)), account, - cairo_version=1 + cairo_version=1, ) - return await contract.functions[function_name].call(*inputs) \ No newline at end of file + return await contract.functions[function_name].call(*inputs) diff --git a/src/randomness/example_randomness.cairo b/src/randomness/example_randomness.cairo index a834414a..46acaf9c 100644 --- a/src/randomness/example_randomness.cairo +++ b/src/randomness/example_randomness.cairo @@ -19,6 +19,7 @@ trait IExampleRandomness { random_words: Span, calldata: Array ); + fn withdraw_funds(ref self: TContractState, receiver: ContractAddress); } #[starknet::contract] @@ -26,8 +27,11 @@ mod ExampleRandomness { use super::{ContractAddress, IExampleRandomness}; use starknet::info::{get_block_number, get_caller_address, get_contract_address}; use pragma::randomness::randomness::{IRandomnessDispatcher, IRandomnessDispatcherTrait}; + use pragma::admin::admin::Ownable; use array::{ArrayTrait, SpanTrait}; - use openzeppelin::token::erc20::{ERC20, interface::{IERC20Dispatcher, IERC20DispatcherTrait}}; + use openzeppelin::token::erc20::interface::{ + ERC20CamelABIDispatcher, ERC20CamelABIDispatcherTrait + }; use traits::{TryInto, Into}; #[storage] @@ -49,6 +53,7 @@ mod ExampleRandomness { return last_random; } + fn request_my_randomness( ref self: ContractState, seed: u64, @@ -59,10 +64,15 @@ mod ExampleRandomness { calldata: Array ) { let randomness_contract_address = self.randomness_contract_address.read(); + let randomness_dispatcher = IRandomnessDispatcher { + contract_address: randomness_contract_address + }; + let caller = get_caller_address(); + let compute_fees = randomness_dispatcher.compute_premium_fee(caller); // Approve the randomness contract to transfer the callback fee // You would need to send some ETH to this contract first to cover the fees - let eth_dispatcher = IERC20Dispatcher { + let eth_dispatcher = ERC20CamelABIDispatcher { contract_address: 0x049d36570d4e46f48e99674bd3fcc84644ddd6b96f7c741b1562b82f9e004dc7 // ETH Contract Address .try_into() .unwrap() @@ -74,9 +84,6 @@ mod ExampleRandomness { ); // Request the randomness - let randomness_dispatcher = IRandomnessDispatcher { - contract_address: randomness_contract_address - }; let request_id = randomness_dispatcher .request_random( seed, callback_address, callback_fee_limit, publish_delay, num_words, calldata @@ -88,6 +95,17 @@ mod ExampleRandomness { return (); } + fn withdraw_funds(ref self: ContractState, receiver: ContractAddress) { + assert_only_admin(); + let eth_dispatcher = ERC20CamelABIDispatcher { + contract_address: 0x049d36570d4e46f48e99674bd3fcc84644ddd6b96f7c741b1562b82f9e004dc7 // ETH Contract Address + .try_into() + .unwrap() + }; + let balance = eth_dispatcher.balanceOf(get_contract_address()); + eth_dispatcher.transfer(receiver, balance); + } + fn receive_random_words( ref self: ContractState, @@ -123,4 +141,11 @@ mod ExampleRandomness { return (); } } + + fn assert_only_admin() { + let state: Ownable::ContractState = Ownable::unsafe_new_contract_state(); + let admin = Ownable::OwnableImpl::owner(@state); + let caller = get_caller_address(); + assert(caller == admin, 'Unauthorized'); + } }