|
|
|
|
@@ -13,13 +13,12 @@ import requests
|
|
|
|
|
from chainlib.chain import ChainSpec
|
|
|
|
|
|
|
|
|
|
# local imports
|
|
|
|
|
from cic import Proof
|
|
|
|
|
from cic.actions.deploy import deploy
|
|
|
|
|
from cic.actions.types import Contract, Options
|
|
|
|
|
from cic.attachment import Attachment
|
|
|
|
|
from cic.meta import Meta
|
|
|
|
|
from cic.network import Network
|
|
|
|
|
from cic.token import Token
|
|
|
|
|
from cic.contract.components.proof import Proof
|
|
|
|
|
from cic.contract.components.attachment import Attachment
|
|
|
|
|
from cic.contract.components.meta import Meta
|
|
|
|
|
from cic.contract.components.network import Network
|
|
|
|
|
from cic.contract.components.token import Token
|
|
|
|
|
from cic.contract.contract import generate_contract, load_contract, deploy_contract
|
|
|
|
|
|
|
|
|
|
if TYPE_CHECKING:
|
|
|
|
|
from chainlib.cli.config import Config
|
|
|
|
|
@@ -62,237 +61,8 @@ def validate_args(_args):
|
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
CONTRACTS = [
|
|
|
|
|
{
|
|
|
|
|
"url": "https://gitlab.com/cicnet/eth-erc20/-/raw/master/python/giftable_erc20_token/data/GiftableToken",
|
|
|
|
|
"name": "Giftable Token",
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"url": "https://gitlab.com/cicnet/erc20-demurrage-token/-/raw/master/python/erc20_demurrage_token/data/DemurrageTokenSingleNocap",
|
|
|
|
|
"name": "Demurrage Token Single No Cap",
|
|
|
|
|
},
|
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
# Download File from Url
|
|
|
|
|
def download_file(url: str, directory: str, filename=None) -> (str, bytes):
|
|
|
|
|
os.makedirs(directory, exist_ok=True)
|
|
|
|
|
filename = filename if filename else url.split("/")[-1]
|
|
|
|
|
path = os.path.join(directory, filename)
|
|
|
|
|
if not os.path.exists(path):
|
|
|
|
|
log.debug(f"Downloading {filename}")
|
|
|
|
|
r = requests.get(url, allow_redirects=True)
|
|
|
|
|
open(path, "wb").write(r.content)
|
|
|
|
|
return path
|
|
|
|
|
return path
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def get_contract_args(data: list):
|
|
|
|
|
for item in data:
|
|
|
|
|
if item["type"] == "constructor":
|
|
|
|
|
return item["inputs"]
|
|
|
|
|
raise Exception("No constructor found in contract")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def print_contract_args(json_path: str):
|
|
|
|
|
json_data = json.load(open(json_path, encoding="utf-8"))
|
|
|
|
|
print("Contract Args:")
|
|
|
|
|
for contract_arg in get_contract_args(json_data):
|
|
|
|
|
print(
|
|
|
|
|
f"\t{contract_arg.get('name', '<no name>')} - {contract_arg.get('type', '<no type>')}"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def select_contract():
|
|
|
|
|
print("Contracts:")
|
|
|
|
|
print("\t C - Custom (path/url to contract)")
|
|
|
|
|
for idx, contract in enumerate(CONTRACTS):
|
|
|
|
|
print(f"\t {idx} - {contract['name']}")
|
|
|
|
|
|
|
|
|
|
val = input("Select contract (C,0,1..): ")
|
|
|
|
|
if val.isdigit() and int(val) < len(CONTRACTS):
|
|
|
|
|
contract = CONTRACTS[int(val)]
|
|
|
|
|
directory = f"./contracts/{contract['name']}"
|
|
|
|
|
bin_path = os.path.abspath(download_file(contract["url"] + ".bin", directory))
|
|
|
|
|
json_path = download_file(contract["url"] + ".json", directory)
|
|
|
|
|
elif val == "C":
|
|
|
|
|
possible_bin_location = input("Enter path/url to contract: ")
|
|
|
|
|
# possible_bin_location is path
|
|
|
|
|
if possible_bin_location[0] == "." or possible_bin_location[0] == "/":
|
|
|
|
|
if os.path.exists(possible_bin_location):
|
|
|
|
|
bin_path = os.path.abspath(possible_bin_location)
|
|
|
|
|
else:
|
|
|
|
|
raise Exception(f"File {possible_bin_location} does not exist")
|
|
|
|
|
|
|
|
|
|
possible_json_path = val.replace(".bin", ".json")
|
|
|
|
|
if os.path.exists(possible_json_path):
|
|
|
|
|
json_path = possible_json_path
|
|
|
|
|
# possible_bin_location is url
|
|
|
|
|
else:
|
|
|
|
|
bin_path = download_file(possible_bin_location, directory)
|
|
|
|
|
else:
|
|
|
|
|
print("Invalid selection")
|
|
|
|
|
exit(1)
|
|
|
|
|
contract_extra_args = []
|
|
|
|
|
contract_extra_args_types = []
|
|
|
|
|
|
|
|
|
|
if os.path.exists(json_path):
|
|
|
|
|
json_data = json.load(open(json_path, encoding="utf-8"))
|
|
|
|
|
for contract_arg in get_contract_args(json_data):
|
|
|
|
|
arg_name = contract_arg.get("name")
|
|
|
|
|
arg_type = contract_arg.get("type")
|
|
|
|
|
if arg_name not in ["_decimals", "_name", "_symbol"]:
|
|
|
|
|
val = input(f"Enter value for {arg_name} ({arg_type}): ")
|
|
|
|
|
contract_extra_args.append(val)
|
|
|
|
|
if arg_type == "uint128":
|
|
|
|
|
contract_extra_args_types.append("uint256")
|
|
|
|
|
else:
|
|
|
|
|
contract_extra_args_types.append(arg_type)
|
|
|
|
|
|
|
|
|
|
return {
|
|
|
|
|
"bin_path": bin_path,
|
|
|
|
|
"json_path": json_path,
|
|
|
|
|
"extra_args": contract_extra_args,
|
|
|
|
|
"extra_args_types": contract_extra_args_types,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def init_token(directory: str, code=""):
|
|
|
|
|
contract = select_contract()
|
|
|
|
|
code = contract["bin_path"]
|
|
|
|
|
contract_extra_args = contract["extra_args"]
|
|
|
|
|
contract_extra_args_types = contract["extra_args_types"]
|
|
|
|
|
|
|
|
|
|
name = input("Enter Token Name (Foo Token): ") or "Foo Token"
|
|
|
|
|
symbol = input("Enter Token Symbol (FOO): ") or "FOO"
|
|
|
|
|
precision = input("Enter Token Precision (6): ") or 6
|
|
|
|
|
supply = input("Enter Token Supply (0): ") or 0
|
|
|
|
|
|
|
|
|
|
contract_token = Token(
|
|
|
|
|
directory,
|
|
|
|
|
name=name,
|
|
|
|
|
symbol=symbol,
|
|
|
|
|
precision=precision,
|
|
|
|
|
extra_args=contract_extra_args,
|
|
|
|
|
extra_args_types=contract_extra_args_types,
|
|
|
|
|
supply=supply,
|
|
|
|
|
code=code,
|
|
|
|
|
)
|
|
|
|
|
contract_token.start()
|
|
|
|
|
return contract_token
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def init_proof(directory):
|
|
|
|
|
description = input("Enter Proof Description (None): ") or None
|
|
|
|
|
namespace = input("Enter Proof Namespace (ge): ") or "ge"
|
|
|
|
|
issuer = input("Enter Proof Issuer (None): ") or None
|
|
|
|
|
contract_proof = Proof(directory, description, namespace, issuer)
|
|
|
|
|
contract_proof.start()
|
|
|
|
|
return contract_proof
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def init_meta(directory):
|
|
|
|
|
name = input("Enter Name (None): ") or ""
|
|
|
|
|
country_code = input("Enter Country Code (KE): ") or "KE"
|
|
|
|
|
location = input("Enter Location (None): ") or ""
|
|
|
|
|
adding_contact_info = True
|
|
|
|
|
contact = {}
|
|
|
|
|
while adding_contact_info:
|
|
|
|
|
value = input("Enter contact info (e.g 'phone: +254723522718'): ") or None
|
|
|
|
|
if value:
|
|
|
|
|
data = value.split(":")
|
|
|
|
|
if len(data) != 2:
|
|
|
|
|
print("Invalid contact info, you must enter in the format 'key: value'")
|
|
|
|
|
continue
|
|
|
|
|
contact[data[0].strip()] = data[1].strip()
|
|
|
|
|
else:
|
|
|
|
|
adding_contact_info = False
|
|
|
|
|
contract_meta = Meta(
|
|
|
|
|
directory,
|
|
|
|
|
name=name,
|
|
|
|
|
country_code=country_code,
|
|
|
|
|
location=location,
|
|
|
|
|
contact=contact,
|
|
|
|
|
)
|
|
|
|
|
contract_meta.start()
|
|
|
|
|
return contract_meta
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def init_attachment(directory):
|
|
|
|
|
contract_attchment = Attachment(directory)
|
|
|
|
|
contract_attchment.start()
|
|
|
|
|
input(
|
|
|
|
|
f"Please add attachment files to '{os.path.abspath(os.path.join(directory,'attachments'))}' and then press ENTER to continue"
|
|
|
|
|
)
|
|
|
|
|
contract_attchment.load()
|
|
|
|
|
return contract_attchment
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def load_contract(directory) -> Contract:
|
|
|
|
|
token = Token(path=directory)
|
|
|
|
|
proof = Proof(path=directory)
|
|
|
|
|
meta = Meta(path=directory)
|
|
|
|
|
attachment = Attachment(path=directory)
|
|
|
|
|
network = Network(directory)
|
|
|
|
|
|
|
|
|
|
token.load()
|
|
|
|
|
proof.load()
|
|
|
|
|
meta.load()
|
|
|
|
|
attachment.load()
|
|
|
|
|
network.load()
|
|
|
|
|
return Contract(
|
|
|
|
|
token=token, proof=proof, meta=meta, attachment=attachment, network=network
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def init_network(
|
|
|
|
|
directory,
|
|
|
|
|
options: Options,
|
|
|
|
|
targets: List[str],
|
|
|
|
|
):
|
|
|
|
|
contract_network = Network(directory, targets=targets)
|
|
|
|
|
contract_network.start()
|
|
|
|
|
|
|
|
|
|
for target in targets:
|
|
|
|
|
m = importlib.import_module(f"cic.ext.{target}.start")
|
|
|
|
|
m.extension_start(
|
|
|
|
|
contract_network,
|
|
|
|
|
registry_address=options.contract_registry,
|
|
|
|
|
chain_spec=options.chain_spec,
|
|
|
|
|
rpc_provider=options.rpc_provider,
|
|
|
|
|
key_account_address=options.key_account,
|
|
|
|
|
)
|
|
|
|
|
contract_network.load()
|
|
|
|
|
return contract_network
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def generate(directory: str, target: str, options: Options) -> Contract:
|
|
|
|
|
if os.path.exists(directory):
|
|
|
|
|
contine = input(
|
|
|
|
|
"Directory already exists, Would you like to delete it? (y/n): "
|
|
|
|
|
)
|
|
|
|
|
if contine.lower() != "y":
|
|
|
|
|
print("Exiting")
|
|
|
|
|
exit(1)
|
|
|
|
|
else:
|
|
|
|
|
print(f"Deleted {directory}")
|
|
|
|
|
os.system(f"rm -rf {directory}")
|
|
|
|
|
os.makedirs(directory)
|
|
|
|
|
|
|
|
|
|
token = init_token(directory)
|
|
|
|
|
proof = init_proof(directory)
|
|
|
|
|
meta = init_meta(directory)
|
|
|
|
|
attachment = init_attachment(directory)
|
|
|
|
|
network = init_network(
|
|
|
|
|
directory,
|
|
|
|
|
options,
|
|
|
|
|
targets=[target],
|
|
|
|
|
)
|
|
|
|
|
return Contract(
|
|
|
|
|
token=token, proof=proof, meta=meta, attachment=attachment, network=network
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def get_options(config: Config, eargs) -> Options:
|
|
|
|
|
def get_options(config: Config, eargs):
|
|
|
|
|
# Defaults
|
|
|
|
|
default_contract_registry = config.get(
|
|
|
|
|
"CIC_REGISTRY_ADDRESS"
|
|
|
|
|
@@ -333,7 +103,7 @@ def get_options(config: Config, eargs) -> Options:
|
|
|
|
|
auth_keyfile_path = config.get("AUTH_KEYFILE_PATH")
|
|
|
|
|
auth_db_path = config.get("AUTH_DB_PATH")
|
|
|
|
|
|
|
|
|
|
options = Options(
|
|
|
|
|
options = [
|
|
|
|
|
auth_db_path,
|
|
|
|
|
auth_keyfile_path,
|
|
|
|
|
auth_passphrase,
|
|
|
|
|
@@ -344,14 +114,11 @@ def get_options(config: Config, eargs) -> Options:
|
|
|
|
|
metadata_endpoint,
|
|
|
|
|
default_wallet_keyfile,
|
|
|
|
|
default_wallet_passphrase,
|
|
|
|
|
)
|
|
|
|
|
]
|
|
|
|
|
print(options)
|
|
|
|
|
return options
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
ExtraArgs = {"skip_gen": str, "skip_deploy": str, "target": str, "path": str, "p": str}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@@ -362,22 +129,19 @@ def execute(config, eargs: ExtraArgs):
|
|
|
|
|
skip_gen = eargs.skip_gen
|
|
|
|
|
skip_deploy = eargs.skip_deploy
|
|
|
|
|
|
|
|
|
|
options = get_options(config, eargs)
|
|
|
|
|
|
|
|
|
|
if not skip_gen:
|
|
|
|
|
contract = generate(directory, target, options)
|
|
|
|
|
contract = generate_contract(directory, [target], config, interactive=True)
|
|
|
|
|
else:
|
|
|
|
|
contract = load_contract(directory)
|
|
|
|
|
|
|
|
|
|
print_contract(contract)
|
|
|
|
|
print(contract)
|
|
|
|
|
|
|
|
|
|
if not skip_deploy:
|
|
|
|
|
ready_to_deploy = input("Ready to deploy? (y/n): ")
|
|
|
|
|
if ready_to_deploy == "y":
|
|
|
|
|
deploy(
|
|
|
|
|
deploy_contract(
|
|
|
|
|
config=config,
|
|
|
|
|
contract_directory=directory,
|
|
|
|
|
options=options,
|
|
|
|
|
target=target,
|
|
|
|
|
)
|
|
|
|
|
print("Deployed")
|
|
|
|
|
|