From a046c3742a5e3069708c9631b403234d2be68e16 Mon Sep 17 00:00:00 2001 From: Ville Sundell Date: Wed, 11 Oct 2017 12:15:21 +0300 Subject: [PATCH 1/3] distributetokens: Added argument --gas-price with default value of 30 Now the script accepts --gas-price argument in Gweis, defaulting to 30. Gweis were chosen over weis, because with weis user can accidentally create too cheap transactions which would lead to many pending (and never mined) transactions (most of the tools report gas in Gwei). --- ico/cmd/distributetokens.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/ico/cmd/distributetokens.py b/ico/cmd/distributetokens.py index e863f686..186a620e 100644 --- a/ico/cmd/distributetokens.py +++ b/ico/cmd/distributetokens.py @@ -30,7 +30,8 @@ @click.option('--issuer-address', nargs=1, help='The address of the issuer contract - leave out for the first run to deploy a new issuer contract', required=False, default=None) @click.option('--master-address', nargs=1, help='The team multisig wallet address that does StandardToken.approve() for the issuer contract', required=False, default=None) @click.option('--allow-zero/--no-allow-zero', default=False, help='Stops the script if a zero amount row is encountered') -def main(chain, address, token, csv_file, limit, start_from, issuer_address, address_column, amount_column, allow_zero, master_address): +@click.option('--gas-price', nargs=1, help='Gas price for a transaction in Gweis', default=30) +def main(chain, address, token, csv_file, limit, start_from, issuer_address, address_column, amount_column, allow_zero, master_address, gas_price): """Distribute tokens to centrally issued crowdsale participant or bounty program participants. Reads in distribution data as CSV. Then uses Issuer contract to distribute tokens. @@ -77,7 +78,7 @@ def main(chain, address, token, csv_file, limit, start_from, issuer_address, add decimal_multiplier = 10**decimals - transaction = {"from": address} + transaction = {"from": address, "gasPrice": gas_price * 1000000000} Issuer = c.provider.get_base_contract_factory('Issuer') if not issuer_address: @@ -170,7 +171,7 @@ def main(chain, address, token, csv_file, limit, start_from, issuer_address, add transaction = { "from": address, - "gasPrice": int(web3.eth.gasPrice * 1.5) + "gasPrice": gas_price * 1000000000 } tokens = int(tokens) From 8dbac1a736ee489420b5f1c74d938a47d6ae90a9 Mon Sep 17 00:00:00 2001 From: Ville Sundell Date: Wed, 11 Oct 2017 12:25:49 +0300 Subject: [PATCH 2/3] etherscan: Changed Solidity version to v0.4.15+commit.bbb8e64f Solidity version needs to be updated for https://etherscan.io/verifyContract2. --- ico/etherscan.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ico/etherscan.py b/ico/etherscan.py index 34e5c698..f3f03744 100644 --- a/ico/etherscan.py +++ b/ico/etherscan.py @@ -21,7 +21,7 @@ def _fill_in_textarea_value(browser, splinter_elem, value): -def verify_contract(project: Project, chain_name: str, address: str, contract_name, contract_filename: str, constructor_args: str, libraries: dict, optimization=True, optimizer_runs=200, compiler: str="v0.4.8+commit.60cc1668", browser_driver="chrome") -> str: +def verify_contract(project: Project, chain_name: str, address: str, contract_name, contract_filename: str, constructor_args: str, libraries: dict, optimization=True, optimizer_runs=200, compiler: str="v0.4.15+commit.bbb8e64f", browser_driver="chrome") -> str: """Semi-automated contract verified on Etherscan. Uses a web browser + Selenium auto fill to verify contracts. From d9e308ff22556a8f40909b1f89ec0f759d1337e0 Mon Sep 17 00:00:00 2001 From: Ville Sundell Date: Wed, 11 Oct 2017 12:31:10 +0300 Subject: [PATCH 3/3] deploycontracts: Added sanitycheck for the git repository Now, if the script sees that the repository is not clean (not committed), it does not proceed. Also, it will abort on non-master branches (by default, possible to skip by using ICO_DEPLOY_ANYWAY env), and also it will abort if you haven't pulled for a week. The deployment script is also adding the commit hash to the source verified by Etherscan. Also added crowdsales/*.py to .gitignore for easier tracking of changes. --- .gitignore | 1 + ico/cmd/deploycontracts.py | 4 +++- ico/deploy.py | 2 -- ico/git.py | 41 ++++++++++++++++++++++++++++++++++++++ ico/importexpand.py | 5 ++++- requirements.txt | 1 + 6 files changed, 50 insertions(+), 4 deletions(-) create mode 100644 ico/git.py diff --git a/.gitignore b/.gitignore index 29d52ea2..e3768675 100644 --- a/.gitignore +++ b/.gitignore @@ -75,6 +75,7 @@ registrar.json # Crowdsale definitions crowdsales/*.yml +crowdsales/*.py *.csv # Customer test cases diff --git a/ico/cmd/deploycontracts.py b/ico/cmd/deploycontracts.py index 7cc28ed0..0677a396 100644 --- a/ico/cmd/deploycontracts.py +++ b/ico/cmd/deploycontracts.py @@ -4,7 +4,7 @@ from populus import Project from ico.deploy import deploy_crowdsale_from_file - +from ico.git import git_sanitycheck @click.command() @click.option('--deployment-name', nargs=1, default="mainnet", help='YAML section name we are deploying. Usual options include "mainnet" or "kovan"', required=True) @@ -27,6 +27,8 @@ def main(deployment_file, deployment_name, address): * https://github.com/TokenMarketNet/ico/blob/master/crowdsales/example.yml """ + git_sanitycheck() + project = Project() deploy_crowdsale_from_file(project, deployment_file, deployment_name, address) print("All done! Enjoy your decentralized future.") diff --git a/ico/deploy.py b/ico/deploy.py index 92c40620..aa28d306 100644 --- a/ico/deploy.py +++ b/ico/deploy.py @@ -286,5 +286,3 @@ def deploy_crowdsale_from_file(project: Project, yaml_filename: str, deployment_ with project.get_chain(chain_name) as chain: web3 = chain.web3 return _deploy_contracts(project, chain, web3, yaml_filename, chain_data, deploy_address) - - diff --git a/ico/git.py b/ico/git.py new file mode 100644 index 00000000..9da3f737 --- /dev/null +++ b/ico/git.py @@ -0,0 +1,41 @@ +from git import Repo +import os +import time + +join = os.path.join +repo = Repo(os.getcwd()) +assert not repo.bare + +repo.config_reader() + +def git_sanitycheck(): + git_ismaster() + git_isold() + git_isdirty() + +def git_isdirty(): + if repo.is_dirty(): + raise RuntimeError("The repository is not committed, won't continue. Please commit.") + + return + +def git_ismaster(): + # User can override git_isold checking for a week + if ((float(os.getenv("ICO_DEPLOY_ANYWAY", 0)) + 604800) > time.time()): + return True + + if (repo.active_branch.commit != repo.heads.master.commit): + raise RuntimeError("This branch is not 'master'. Please switch to master, or use the following command to postpone this check for a week:\nexport ICO_DEPLOY_ANYWAY=" + str(time.time())) + +def git_isold(): + git_root = repo.git.rev_parse("--show-toplevel") + latest_pull = os.stat(git_root + "/.git/FETCH_HEAD").st_mtime + deadline = latest_pull + 604800 # One week from latest commit + + if (time.time() > deadline): + raise RuntimeError("You haven't pulled for a week. Please do git pull.") + else: + return + +def git_current_commit(): + return repo.active_branch.commit diff --git a/ico/importexpand.py b/ico/importexpand.py index f9d2ccf7..9f191ed3 100644 --- a/ico/importexpand.py +++ b/ico/importexpand.py @@ -3,6 +3,7 @@ Mainly need for EtherScan verification service. """ import os +from ico.git import git_current_commit from typing import Tuple from populus import Project @@ -83,4 +84,6 @@ def expand_contract_imports(project: Project, contract_filename: str) -> Tuple[s :return: Tuple[final expanded source, set of processed filenames] """ exp = Expander(project) - return exp.expand_file(contract_filename), exp.processed_imports + commitline = "// (C) 2017 TokenMarket Ltd. (https://github.com/TokenMarketNet/ico/blob/master/LICENSE.txt) Commit: " + str(git_current_commit()) + "\n" + + return commitline + exp.expand_file(contract_filename), exp.processed_imports diff --git a/requirements.txt b/requirements.txt index 689735ff..45945698 100644 --- a/requirements.txt +++ b/requirements.txt @@ -10,6 +10,7 @@ eth-testrpc==1.3.0 ethereum==1.6.1 ethereum-abi-utils==0.4.0 ethereum-utils==0.3.2 +gitpython==2.1.7 idna==2.5 Jinja2==2.9.6 json-rpc==1.10.3