Skip to content
This repository has been archived by the owner on Dec 1, 2023. It is now read-only.

Add Account.declare #207

Merged
merged 22 commits into from
Oct 21, 2022
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 22 additions & 17 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,8 @@ Creating artifacts/abis/ to store compilation artifacts

### `deploy`

> NOTICE: this method doesn't use an account, which will be deprecated very soon as StarkNet makes deployments from accounts mandatory.

```sh
nile deploy contract --alias my_contract

Expand All @@ -124,23 +126,6 @@ A few things to notice here:
3. The `--alias` parameter lets me create an unique identifier for future interactions, if no alias is set then the contract's address can be used as identifier
4. By default Nile works on local, but you can use the `--network` parameter to interact with `mainnet`, `goerli`, and the default `localhost`.

### `declare`

```sh
nile declare contract --alias my_contract

🚀 Declaring contract
⏳ Declaration of contract successfully sent at 0x07ec10eb0758f7b1bc5aed0d5b4d30db0ab3c087eba85d60858be46c1a5e4680
📦 Registering declaration as my_contract in localhost.declarations.txt
```

A few things to notice here:

1. `nile declare <contract_name>` looks for an artifact with the same name
2. This created a `localhost.declarations.txt` file storing all data related to my declarations
3. The `--alias` parameter lets me create an unique identifier for future interactions, if no alias is set then the contract's address can be used as identifier
4. By default Nile works on local, but you can use the `--network` parameter to interact with `mainnet`, `goerli`, and the default `localhost`.

### `setup`

Deploy an Account associated with a given private key.
Expand Down Expand Up @@ -188,6 +173,26 @@ Some things to note:
- `max_fee` defaults to `0`. Add `--max_fee <max_fee>` to set the maximum fee for the transaction
- `network` defaults to the `localhost`. Add `--network <network>` to change the network for the transaction

### `declare`
martriay marked this conversation as resolved.
Show resolved Hide resolved

Very similar to `send`, but for declaring a contract based on its name through an account.

```sh
nile declare <private_key_alias> contract --alias my_contract

🚀 Declaring contract
⏳ Successfully sent declaration of contract as 0x07ec10eb0758f7b1bc5aed0d5b4d30db0ab3c087eba85d60858be46c1a5e4680
🧾 Transaction hash: 0x7222604b048632326f6a016ccb16fbdea7e926cd9e2354544800667a970aee4
📦 Registering declaration as my_contract in localhost.declarations.txt
```

A few things to notice here:

1. `nile declare <private_key_alias> <contract_name>` looks for an artifact with name `<contract_name>`
2. This creates or updates a `localhost.declarations.txt` file storing all data related to your declarations
3. The `--alias` parameter lets you create a unique identifier for future interactions, if no alias is set then the contract's address can be used as identifier
4. By default Nile works on local, but you can use the `--network` parameter to interact with `mainnet`, `goerli`, and the default `localhost`.

### `call` and `invoke`

Using `call` and `invoke`, we can perform read and write operations against our local node (or public one using the `--network mainnet` parameter). The syntax is:
Expand Down
18 changes: 14 additions & 4 deletions src/nile/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
from nile.core.call_or_invoke import call_or_invoke as call_or_invoke_command
from nile.core.clean import clean as clean_command
from nile.core.compile import compile as compile_command
from nile.core.declare import declare as declare_command
from nile.core.deploy import deploy as deploy_command
from nile.core.init import init as init_command
from nile.core.install import install as install_command
Expand Down Expand Up @@ -88,12 +87,23 @@ def deploy(artifact, arguments, network, alias):


@cli.command()
@click.argument("artifact", nargs=1)
@click.argument("signer", nargs=1)
@click.argument("contract_name", nargs=1)
@click.option("--max_fee", nargs=1)
@network_option
@click.option("--alias")
def declare(artifact, network, alias):
@click.option("--directory")
martriay marked this conversation as resolved.
Show resolved Hide resolved
def declare(
signer, contract_name, network, max_fee, alias=None, contracts_directory=None
):
"""Declare StarkNet smart contract."""
declare_command(artifact, network, alias)
account = Account(signer, network)
account.declare(
contract_name,
alias=alias,
max_fee=max_fee,
contracts_directory=contracts_directory,
)


@cli.command()
Expand Down
66 changes: 57 additions & 9 deletions src/nile/common.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
"""nile common module."""
import json
import logging
import os
import re
import subprocess
Expand All @@ -13,6 +14,10 @@
ACCOUNTS_FILENAME = "accounts.json"
NODE_FILENAME = "node.json"
RETRY_AFTER_SECONDS = 30
UNIVERSAL_DEPLOYER_ADDRESS = (
# subject to change
"0x414aa016992e868fa22e21bd104757e280c83ecb260fe9bccd1caee2f7f590e"
)


def _get_gateway():
Expand Down Expand Up @@ -47,29 +52,72 @@ def get_all_contracts(ext=None, directory=None):


def run_command(
contract_name, network, overriding_path=None, operation="deploy", arguments=None
operation,
network,
contract_name=None,
arguments=None,
inputs=None,
signature=None,
max_fee=None,
overriding_path=None,
martriay marked this conversation as resolved.
Show resolved Hide resolved
):
"""Execute CLI command with given parameters."""
base_path = (
overriding_path if overriding_path else (BUILD_DIRECTORY, ABIS_DIRECTORY)
)
contract = f"{base_path[0]}/{contract_name}.json"
command = ["starknet", operation, "--contract", contract]
command = ["starknet", operation]

if arguments:
if contract_name is not None:
base_path = (
overriding_path if overriding_path else (BUILD_DIRECTORY, ABIS_DIRECTORY)
)
contract = f"{base_path[0]}/{contract_name}.json"
command.append("--contract")
command.append(contract)

if inputs is not None:
command.append("--inputs")
command.extend(prepare_params(arguments))
command.extend(prepare_params(inputs))

if signature is not None:
command.append("--signature")
command.extend(prepare_params(signature))

if max_fee is not None:
command.append("--max_fee")
command.append(max_fee)

if arguments is not None:
command.extend(arguments)

if network == "mainnet":
os.environ["STARKNET_NETWORK"] = "alpha-mainnet"
elif network == "goerli":
os.environ["STARKNET_NETWORK"] = "alpha-goerli"
else:
command.append(f"--feeder_gateway_url={GATEWAYS.get(network)}")
command.append(f"--gateway_url={GATEWAYS.get(network)}")

command.append("--no_wallet")

return subprocess.check_output(command)
try:
return subprocess.check_output(command).strip().decode("utf-8")
except subprocess.CalledProcessError:
p = subprocess.Popen(command, stderr=subprocess.PIPE)
_, error = p.communicate()
err_msg = error.decode()

if "max_fee must be bigger than 0" in err_msg:
logging.error(
"""
\n😰 Whoops, looks like max fee is missing. Try with:\n
--max_fee=`MAX_FEE`
"""
)
elif "transactions should go through the __execute__ entrypoint." in err_msg:
logging.error(
"\n\n😰 Whoops, looks like you're not using an account. Try with:\n"
"\nnile send [OPTIONS] SIGNER CONTRACT_NAME METHOD [PARAMS]"
)

return ""


def get_nonce(contract_address, network):
Expand Down
56 changes: 54 additions & 2 deletions src/nile/core/account.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,12 @@
import os

from dotenv import load_dotenv
from starkware.starknet.compiler.compile import compile_starknet_files

from nile import accounts, deployments
from nile.common import get_nonce
from nile.common import CONTRACTS_DIRECTORY, UNIVERSAL_DEPLOYER_ADDRESS, get_nonce
martriay marked this conversation as resolved.
Show resolved Hide resolved
from nile.core.call_or_invoke import call_or_invoke
from nile.core.declare import declare
from nile.core.deploy import deploy

try:
Expand Down Expand Up @@ -63,9 +65,59 @@ def deploy(self):

return address, index

def declare(
martriay marked this conversation as resolved.
Show resolved Hide resolved
self, contract_name, max_fee, nonce=None, alias=None, contracts_directory=None
martriay marked this conversation as resolved.
Show resolved Hide resolved
):
"""Declare a contract through an Account contract."""
if contracts_directory is None:
contracts_directory = CONTRACTS_DIRECTORY

if nonce is None:
nonce = get_nonce(self.address, self.network)

if max_fee is None:
max_fee = 0
else:
max_fee = int(max_fee)

contract_class = compile_starknet_files(
files=[f"{contracts_directory}/{contract_name}.cairo"], debug_info=True
martriay marked this conversation as resolved.
Show resolved Hide resolved
)

sig_r, sig_s = self.signer.sign_declare(
sender=self.address,
contract_class=contract_class,
nonce=nonce,
max_fee=max_fee,
)

return declare(
sender=self.address,
contract_name=contract_name,
signature=[sig_r, sig_s],
alias=alias,
network=self.network,
max_fee=max_fee,
)

def deploy_contract(
self, class_hash, salt, unique, calldata, max_fee, deployer_address=None
martriay marked this conversation as resolved.
Show resolved Hide resolved
):
"""Deploy a contract through an Account contract."""
return self.send(
to=deployer_address or UNIVERSAL_DEPLOYER_ADDRESS,
method="deployContract",
calldata=[class_hash, salt, unique, len(calldata), *calldata],
max_fee=max_fee,
)

def send(self, to, method, calldata, max_fee, nonce=None):
martriay marked this conversation as resolved.
Show resolved Hide resolved
"""Execute a tx going through an Account contract."""
target_address, _ = next(deployments.load(to, self.network)) or to
try:
target_address, _ = next(deployments.load(to, self.network))
except StopIteration:
target_address = to

calldata = [int(x) for x in calldata]

if nonce is None:
Expand Down
62 changes: 11 additions & 51 deletions src/nile/core/call_or_invoke.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,7 @@
"""Command to call or invoke StarkNet smart contracts."""
import logging
import os
import subprocess

from nile import deployments
from nile.common import GATEWAYS, prepare_params
from nile.common import prepare_params, run_command


def call_or_invoke(
Expand All @@ -13,9 +10,7 @@ def call_or_invoke(
"""Call or invoke functions of StarkNet smart contracts."""
address, abi = next(deployments.load(contract, network))

command = [
"starknet",
type,
arguments = [
"--address",
address,
"--abi",
Expand All @@ -24,48 +19,13 @@ def call_or_invoke(
method,
]

if network == "mainnet":
os.environ["STARKNET_NETWORK"] = "alpha-mainnet"
elif network == "goerli":
os.environ["STARKNET_NETWORK"] = "alpha-goerli"
else:
command.append(f"--feeder_gateway_url={GATEWAYS.get(network)}")
command.append(f"--gateway_url={GATEWAYS.get(network)}")
inputs = prepare_params(params) if len(params) > 0 else None

params = prepare_params(params)

if len(params) > 0:
command.append("--inputs")
command.extend(params)

if signature is not None:
command.append("--signature")
command.extend(signature)

if max_fee is not None:
command.append("--max_fee")
command.append(max_fee)

command.append("--no_wallet")

try:
return subprocess.check_output(command).strip().decode("utf-8")
except subprocess.CalledProcessError:
p = subprocess.Popen(command, stderr=subprocess.PIPE)
_, error = p.communicate()
err_msg = error.decode()

if "max_fee must be bigger than 0" in err_msg:
logging.error(
"""
\n😰 Whoops, looks like max fee is missing. Try with:\n
--max_fee=`MAX_FEE`
"""
)
elif "transactions should go through the __execute__ entrypoint." in err_msg:
logging.error(
"\n\n😰 Whoops, looks like you're not using an account. Try with:\n"
"\nnile send [OPTIONS] SIGNER CONTRACT_NAME METHOD [PARAMS]"
)

return ""
return run_command(
operation=type,
network=network,
inputs=inputs,
arguments=arguments,
signature=signature,
max_fee=max_fee,
)
26 changes: 23 additions & 3 deletions src/nile/core/declare.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,37 @@
from nile.common import DECLARATIONS_FILENAME, parse_information, run_command


def declare(contract_name, network, alias=None, overriding_path=None):
def declare(
sender,
contract_name,
signature,
network,
alias=None,
overriding_path=None,
max_fee=None,
):
"""Declare StarkNet smart contracts."""
logging.info(f"🚀 Declaring {contract_name}")

if alias_exists(alias, network):
file = f"{network}.{DECLARATIONS_FILENAME}"
raise Exception(f"Alias {alias} already exists in {file}")

output = run_command(contract_name, network, overriding_path, operation="declare")
arguments = ["--sender", sender]
martriay marked this conversation as resolved.
Show resolved Hide resolved
max_fee = "0" if max_fee is None else str(max_fee)

output = run_command(
operation="declare",
network=network,
contract_name=contract_name,
arguments=arguments,
signature=signature,
max_fee=max_fee,
overriding_path=overriding_path,
)

class_hash, tx_hash = parse_information(output)
logging.info(f"⏳ Declaration of {contract_name} successfully sent at {class_hash}")
logging.info(f"⏳ Successfully sent declaration of {contract_name} as {class_hash}")
logging.info(f"🧾 Transaction hash: {tx_hash}")

deployments.register_class_hash(class_hash, network, alias)
Expand Down
Loading