def participate_early(chain, web3: Web3, presale_address: str, crowdsale_address: str, deploy_address: str) -> int: """Move funds over early. .. note :: Crowdsale contract checks the participate whitelist by invest address, not by msg.sender. This process will open the presale investors an ability to participate to the crowdsale early, bypassing the retail investor start time. However they could also top up their existing preico accounts, so this is largerly no issue. """ updated = 0 PresaleFundCollector = chain.contract_factories.PresaleFundCollector presale = PresaleFundCollector(address=presale_address) Crowdsale = chain.contract_factories.Crowdsale crowdsale = Crowdsale(address=crowdsale_address) # Make sure presale is correctly set txid = presale.transact({ "from": deploy_address }).setCrowdsale(crowdsale.address) logger.info("Setting presale crowdsale address to %s on txid", crowdsale.address, txid) check_succesful_tx(web3, txid) # Double check presale has a presale price set MilestonePricing = chain.contract_factories.MilestonePricing pricing_strategy = MilestonePricing( address=crowdsale.call().pricingStrategy()) if not pricing_strategy.call().preicoAddresses(presale.address): raise RuntimeError( "Was not listed as presale address for pricing: {}".format( presale.address)) for i in range(0, presale.call().investorCount()): investor = presale.call().investors(i) if presale.call().balances(investor) > 0: logger.info("Whitelisting for %s to crowdsale %s", investor, crowdsale.address) txid = crowdsale.transact({ "from": deploy_address }).setEarlyParicipantWhitelist(investor, True) logger.info("Broadcasting whitelist transaction %s", txid) logger.info("Moving funds for %s to presale %s", investor, presale.address) txid = presale.transact({ "from": deploy_address }).parcipateCrowdsaleInvestor(investor) logger.info("Broadcasting transaction %s", txid) check_succesful_tx(web3, txid) updated += 1 return updated
def main(chain, address, name, symbol, supply, minting_agent, release_agent): """Rebuild a relaunched CrowdsaleToken contract. Example: deploy-token --chain=ropsten --address=0x3c2d4e5eae8c4a31ccc56075b5fd81307b1627c6 --name="MikkoToken 2.0" --symbol=MOO --release-agent=0x3c2d4e5eae8c4a31ccc56075b5fd81307b1627c6 --supply=100000 """ raise NotImplementedError() project = Project() with project.get_chain(chain) as c: web3 = c.web3 print("Web3 provider is", web3.currentProvider) print("Owner address is", address) print("Owner balance is", from_wei(web3.eth.getBalance(address), "ether"), "ETH") # Goes through geth account unlock process if needed if is_account_locked(web3, address): request_account_unlock(c, address, None) transaction = {"from": address} args = [name, symbol, supply] # This does deployment with all dependencies linked in print("Starting contract deployment") contract, txhash = c.provider.deploy_contract( 'CrowdsaleToken', deploy_transaction=transaction, deploy_args=args) print("Contract address is", contract.address) # This is needed for Etherscan contract verification # https://etherscanio.freshdesk.com/support/solutions/articles/16000053599-contract-verification-constructor-arguments data = get_constructor_arguments(contract, args) print("CrowdsaleToken constructor arguments is", data) if release_agent: print("Setting release agent to", release_agent) txid = contract.transact(transaction).setReleaseAgent( release_agent) check_succesful_tx(web3, txid) if minting_agent: print("Setting minting agent") txid = contract.transact(transaction).setMintAgent( minting_agent, True) check_succesful_tx(web3, txid) # Do some contract reads to see everything looks ok print("Token owner:", contract.call().owner()) print("Token minting finished:", contract.call().mintingFinished()) print("Token released:", contract.call().released()) print("Token release agent:", contract.call().releaseAgent()) print("All done! Enjoy your decentralized future.")
def deploy(project: Project, chain, chain_name, web3: Web3, address: str, token: Contract, freeze_ends_at: int, tokens_to_be_allocated: int): # TODO: Fix Populus support this via an deploy argument if "JSONFile" in chain.registrar.registrar_backends: del chain.registrar.registrar_backends["JSONFile"] freeze_ends_at = int(freeze_ends_at) assert freeze_ends_at > 0 assert tokens_to_be_allocated > 0 # Create issuer contract args = [ address, freeze_ends_at, token.address, tokens_to_be_allocated, ] transaction = { "from": address, } print("Deploying new TokenVault contract", args) token_vault, txhash = chain.provider.deploy_contract( "TokenVault", deploy_transaction=transaction, deploy_args=args) check_succesful_tx(web3, txhash) const_args = get_constructor_arguments(token_vault, args) fname = "TokenVault.sol" browser_driver = "chrome" verify_contract(project=project, libraries={}, chain_name=chain_name, address=token_vault.address, contract_name="TokenVault", contract_filename=fname, constructor_args=const_args, browser_driver=browser_driver) link = get_etherscan_link(chain_name, token_vault.address) print("TokenVault verified contract is", link) return token_vault
def lock(chain, web3: Web3, address: str, token: Contract, vault_address: str): TokenVault = chain.contract_factories.TokenVault token_vault = TokenVault(address=vault_address) print("Locking vault ", token_vault.address) print("Tokens expected", token_vault.call().tokensToBeAllocated()) print("Tokens allocated", token_vault.call().tokensAllocatedTotal()) print("Tokens hold", token_vault.call().getBalance()) if not(token_vault.call().tokensToBeAllocated() == token_vault.call().tokensAllocatedTotal() == token_vault.call().getBalance()): sys.exit("Vault token balances mismatch") transaction = { "from": address, } print("Locking vault") txid = token_vault.transact(transaction).lock() check_succesful_tx(web3, txid)
def lock(chain, web3: Web3, address: str, token: Contract, vault_address: str): TokenVault = chain.provider.get_base_contract_factory('TokenVault') token_vault = TokenVault(address=vault_address) print("Locking vault ", token_vault.address) print("Tokens expected", token_vault.functions.tokensToBeAllocated().call()) print("Tokens allocated", token_vault.functions.tokensAllocatedTotal().call()) print("Tokens hold", token_vault.functions.getBalance().call()) if not(token_vault.functions.tokensToBeAllocated().call() == token_vault.functions.tokensAllocatedTotal().call() == token_vault.functions.getBalance().call()): sys.exit("Vault token balances mismatch") transaction = { "from": address, } print("Locking vault") txid = token_vault.transact(transaction).lock() check_succesful_tx(web3, txid)
def deploy(project: Project, chain, chain_name, web3: Web3, address: str, token: Contract, freeze_ends_at: int, tokens_to_be_allocated: int): # TODO: Fix Populus support this via an deploy argument if "JSONFile" in chain.registrar.registrar_backends: del chain.registrar.registrar_backends["JSONFile"] freeze_ends_at = int(freeze_ends_at) assert freeze_ends_at > 0 assert tokens_to_be_allocated > 0 # Create issuer contract args = [ address, freeze_ends_at, token.address, tokens_to_be_allocated, ] transaction = { "from": address, } print("Deploying new TokenVault contract", args) token_vault, txhash = chain.provider.deploy_contract("TokenVault", deploy_transaction=transaction, deploy_args=args) check_succesful_tx(web3, txhash) const_args = get_constructor_arguments(token_vault, args) fname = "TokenVault.sol" browser_driver = "chrome" verify_contract( project=project, libraries={}, chain_name=chain_name, address=token_vault.address, contract_name="TokenVault", contract_filename=fname, constructor_args=const_args, browser_driver=browser_driver) link = get_etherscan_link(chain_name, token_vault.address) print("TokenVault verified contract is", link) return token_vault
def participate_early(chain, web3: Web3, presale_address: str, crowdsale_address: str, deploy_address: str, start=0, end=32, timeout=300) -> int: """Move funds over early. .. note :: Crowdsale contract checks the participate whitelist by invest address, not by msg.sender. This process will open the presale investors an ability to participate to the crowdsale early, bypassing the retail investor start time. However they could also top up their existing preico accounts, so this is largerly no issue. :param start: Move only n investors (for testing purposes) :param end: Move only n investors (for testing purposes) """ updated = 0 PresaleFundCollector = get_contract_by_name(chain, "PresaleFundCollector") presale = PresaleFundCollector(address=presale_address) Crowdsale = PresaleFundCollector = get_contract_by_name(chain, "Crowdsale") crowdsale = Crowdsale(address=crowdsale_address) # Make sure presale is correctly set txid = presale.transact({"from": deploy_address}).setCrowdsale(crowdsale.address) logger.info("Setting presale crowdsale address to %s on txid", crowdsale.address, txid) check_succesful_tx(web3, txid, timeout=timeout) # Double check presale has a presale price set MilestonePricing = get_contract_by_name(chain, "MilestonePricing") pricing_strategy = MilestonePricing(address=crowdsale.call().pricingStrategy()) if not pricing_strategy.call().preicoAddresses(presale.address): raise RuntimeError("Was not listed as presale address for pricing: {}".format(presale.address)) for i in range(start, min(end, presale.call().investorCount())): investor = presale.call().investors(i) if presale.call().balances(investor) > 0: print("Whitelisting for {} to crowdsale {}".format(investor, crowdsale.address)) txid = crowdsale.transact({"from": deploy_address}).setEarlyParicipantWhitelist(investor, True) print("Broadcasting whitelist transaction {}".format(txid)) check_succesful_tx(web3, txid, timeout=timeout) funds = from_wei(presale.call().balances(investor), "ether") print("Moving funds {} ETH for investor {} to presale {}".format(funds, investor, presale.address)) txid = presale.transact({"from": deploy_address}).participateCrowdsaleInvestor(investor) print("Broadcasting transaction {}".format(txid)) check_succesful_tx(web3, txid, timeout=timeout) updated += 1 else: print("Investor already handled: {}".format(investor)) return updated
def main(chain, address, token, csv_file, limit, start_from, issuer_address, address_column, amount_column, allow_zero, master_address): """Distribute tokens to centrally issued crowdsale participant. Reads in distribution data as CSV. Then uses Issuer contract to distribute tokens. All token counts are multiplied by token contract decimal specifier. E.g. if CSV has amount 15.5, token has 2 decimal places, we will issue out 1550 raw token amount. To speed up the issuance, transactions are verified in batches. Each batch is 16 transactions at a time. Example (first run): distribute-tokens --chain=kovan --address=0x001FC7d7E506866aEAB82C11dA515E9DD6D02c25 --token=0x1644a421ae0a0869bac127fa4cce8513bd666705 --csv-file=input.csv --allow-zero --address-column="Ethereum address" --amount-column="Golden tickets earned" Example (second run, continue after first run was interrupted): distribute-tokens --chain=kovan --address=0x001FC7d7E506866aEAB82C11dA515E9DD6D02c25 --token=0x1644a421ae0a0869bac127fa4cce8513bd666705 --csv-file=input.csv --allow-zero --address-column="Ethereum address" --amount-column="Golden tickets earned" --issuer-address=0x2c9877534f62c8b40aebcd08ec9f54d20cb0a945 """ project = Project() with project.get_chain(chain) as c: web3 = c.web3 print("Web3 provider is", web3.currentProvider) print("Owner address is", address) print("Owner balance is", from_wei(web3.eth.getBalance(address), "ether"), "ETH") # Goes through geth account unlock process if needed if is_account_locked(web3, address): request_account_unlock(c, address, timeout=3600*6) assert not is_account_locked(web3, address) Token = c.provider.get_base_contract_factory('CentrallyIssuedToken') token = Token(address=token) print("Total supply is", token.call().totalSupply()) print("Upgrade master is", token.call().upgradeMaster()) print("Owner token balance master is", token.call().balanceOf(address)) decimals = token.call().decimals() print("Token decimal places is", decimals) assert decimals >= 0 decimal_multiplier = 10**decimals transaction = {"from": address} Issuer = c.provider.get_base_contract_factory('Issuer') if not issuer_address: # TODO: Fix Populus support this via an deploy argument if "JSONFile" in c.registrar.registrar_backends: del c.registrar.registrar_backends["JSONFile"] # Create issuer contract assert master_address, "You need to give master-address" args = [address, master_address, token.address] print("Deploying new issuer contract", args) issuer, txhash = c.provider.deploy_contract("Issuer", deploy_transaction=transaction, deploy_args=args) check_succesful_tx(web3, txhash) const_args = get_constructor_arguments(issuer, args) chain_name = chain fname = "Issuer.sol" browser_driver = "chrome" verify_contract( project=project, libraries={}, # TODO: Figure out how to pass around chain_name=chain_name, address=issuer.address, contract_name="Issuer", contract_filename=fname, constructor_args=const_args, # libraries=runtime_data["contracts"][name]["libraries"], browser_driver=browser_driver) link = get_etherscan_link(chain_name, issuer.address) print("Issuer verified contract is", link) else: print("Reusing existing issuer contract") issuer = Issuer(address=issuer_address) print("Issuer contract is", issuer.address) print("Currently issued", issuer.call().issuedCount()) if not master_address: sys.exit("Please use Token.approve() to give some allowance for the issuer contract by master address") allowance = token.call().allowance(master_address, issuer.address) print("Issuer allowance", allowance) if allowance == 0 or not master_address: sys.exit("Please use Token.approve() to give some allowance for the issuer contract by master address") print("Reading data", csv_file) with open(csv_file, "rt") as inp: reader = csv.DictReader(inp) rows = [row for row in reader] # Check that we have unique addresses uniq_addresses = set() for row in rows: addr = row[address_column].strip() if addr in uniq_addresses: raise RuntimeError("Address appears twice in input data", addr) uniq_addresses.add(addr) # Start distribution start_time = time.time() start_balance = from_wei(web3.eth.getBalance(address), "ether") tx_to_confirm = [] # List of txids to confirm tx_batch_size = 16 # How many transactions confirm once print("Total rows", len(rows)) for i in range(start_from, min(start_from+limit, len(rows))): data = rows[i] addr = data[address_column].strip() tokens = Decimal(data[amount_column].strip()) tokens *= decimal_multiplier end_balance = from_wei(web3.eth.getBalance(address), "ether") spent = start_balance - end_balance if tokens == 0: if not allow_zero: raise RuntimeError("Encountered zero amount") else: continue # http://stackoverflow.com/a/19965088/315168 if not tokens % 1 == 0: raise RuntimeError("Could not issue tokens because after multiplication was not integer") tokens = int(tokens) print("Row", i, "giving", tokens, "to", addr, "issuer", issuer.address, "time passed", time.time() - start_time, "ETH passed", spent) if issuer.call().issued(addr): print("Already issued, skipping") continue txid = issuer.transact(transaction).issue(addr, tokens) tx_to_confirm.append(txid) # Confirm N transactions when batch max size is reached if len(tx_to_confirm) >= tx_batch_size: check_multiple_succesful_txs(web3, tx_to_confirm) tx_to_confirm = [] # Confirm dangling transactions check_multiple_succesful_txs(web3, tx_to_confirm) end_balance = from_wei(web3.eth.getBalance(address), "ether") print("Deployment cost is", start_balance - end_balance, "ETH") print("All done! Enjoy your decentralized future.")
print("crowdsale maximumSellableTokens is ", crowdsale.call().maximumSellableTokens()) print("crowdsale minimumFundingGoal", crowdsale.call().minimumFundingGoal()) print("crowdsale starts at ", crowdsale.call().startsAt()) print("crowdsale ends at ", crowdsale.call().endsAt()) print("crowdsale state ", crowdsale.call().getState()) print("crowdsale pricing ", crowdsale.call().pricingStrategy()) print("crowdsale pricing ", pricingStrategy.call().isSane(crowdsale.address)) print("crowdsale finalize ", crowdsale.call().finalizeAgent()) print("crowdsale finalize ", finalizeAgent.call().isSane()) print("crowdsale isfinalize ", finalizeAgent.call().isFinalizeAgent()) print("token mint", token.call().mintAgents(finalizeAgent.address)) print("token release", token.call().releaseAgent()) #print("add to early participate..") #txid = crowdsale.call().setEarlyParicipantWhitelist(participant_address, True) #print("TXID", txid) #check_succesful_tx(web3, txid) #txid = crowdsale.estimateGas({"from": participant_address, "value": to_wei("10", "ether")}, "gasPrice": "0x095799F9F8BFACE").invest(participant_address) txid = crowdsale.transact({ "from": participant_address, "value": to_wei("50", "ether"), "gasPrice": "0x3E8" }).invest(participant_address) #txid = crowdsale.transact({"from": participant_address, "value": '0x016345785d8a0000', "gas": '0x1388', "gasPrice": '0x4563918244F40000'}).invest(participant_address) print("TXID", txid) check_succesful_tx(web3, txid)
def _confirm_tx(txid): check_succesful_tx(web3, txid)
def main(chain, address, contract_name, name, symbol, supply, decimals, minting_agent, release_agent, verify, verify_filename, master_address): """Deploy a single crowdsale token contract. THIS COMMAND IS DEPRECATED. PLEASE USE deploy-contracts instead. Examples: deploy-token --chain=ropsten --address=0x3c2d4e5eae8c4a31ccc56075b5fd81307b1627c6 --name="MikkoToken 2.0" --symbol=MOO --release-agent=0x3c2d4e5eae8c4a31ccc56075b5fd81307b1627c6 --supply=100000 deploy-token --chain=kovan --contract-name="CentrallyIssuedToken" --address=0x001FC7d7E506866aEAB82C11dA515E9DD6D02c25 --name="TestToken" --symbol=MOO --supply=916 --decimals=0 --verify --verify-filename=CentrallyIssuedToken.sol """ project = Project() with project.get_chain(chain) as c: web3 = c.web3 print("Web3 provider is", web3.currentProvider) print("Deployer address is", address) print("Deployer balance is", from_wei(web3.eth.getBalance(address), "ether"), "ETH") # Goes through geth account unlock process if needed if is_account_locked(web3, address): request_account_unlock(c, address, None) decimal_multiplier = 10**decimals transaction = {"from": address} args = [name, symbol, supply * decimal_multiplier, decimals] if contract_name == "CentrallyIssuedToken": # TODO: Generalize settings contract args # This sets the upgrade master args = [address] + args # Make sure Populus does not pull up any cached instances of deployed contracts # TODO: Fix Populus support this via an deploy argument if "JSONFile" in c.registrar.registrar_backends: del c.registrar.registrar_backends["JSONFile"] print("Starting contract deployment") # This does deployment with all dependencies linked in contract, txhash = c.provider.deploy_contract( contract_name, deploy_transaction=transaction, deploy_args=args) check_succesful_tx(web3, txhash) print("Contract address is", contract.address) # This is needed for Etherscan contract verification # https://etherscanio.freshdesk.com/support/solutions/articles/16000053599-contract-verification-constructor-arguments const_args = get_constructor_arguments(contract, args) print("CrowdsaleToken constructor arguments is", const_args) if release_agent: print("Setting release agent to", release_agent) txid = contract.functions.setReleaseAgent(release_agent).transact( transaction) check_succesful_tx(web3, txid) if minting_agent: print("Setting minting agent") txid = contract.functions.setMintAgent(minting_agent, True).transact(transaction) check_succesful_tx(web3, txid) if master_address: print("Moving upgrade master to a team multisig wallet", master_address) txid = contract.functions.setUpgradeMaster( master_address).transact({"from": address}) check_succesful_tx(web3, txid) print("Moving total supply a team multisig wallet", master_address) contract.functions.transfer( master_address, contract.functions.totalSupply().call()).transact( {"from": address}) check_succesful_tx(web3, txid) if verify: chain_name = chain fname = verify_filename browser_driver = "chrome" verify_contract( project=project, libraries={}, # TODO: Figure out how to pass around chain_name=chain_name, address=contract.address, contract_name=contract_name, contract_filename=fname, constructor_args=const_args, # libraries=runtime_data["contracts"][name]["libraries"], browser_driver=browser_driver) link = get_etherscan_link(chain_name, contract.address) print("Verified contract is", link) print("Token supply:", contract.functions.totalSupply().call()) # Do some contract reads to see everything looks ok try: print("Token owner:", contract.functions.owner().call()) except ValueError: pass # No owner try: print("Token upgradeMaster:", contract.functions.upgradeMaster().call()) except ValueError: pass try: print("Token minting finished:", contract.functions.mintingFinished().call()) except ValueError: pass try: print("Token released:", contract.functions.released().call()) print("Token release agent:", contract.functions.releaseAgent().call()) except ValueError: pass print("All done! Enjoy your decentralized future.")
def main(chain, address, token, csv_file, limit, start_from, issuer_address, address_column, amount_column, external_id_column, allow_addresless, master_address, gas_price, solc_version): """Distribute tokens to centrally issued crowdsale participant or bounty program participants using an external key. Reads in distribution data as CSV. Then uses Issuer contract to distribute tokens. All token counts are multiplied by token contract decimal specifier. E.g. if CSV has amount 15.5, token has 2 decimal places, we will issue out 1550 raw token amount. The external id uniquely identifies participants. This is different from the distribute-tokens where the Ethereum address uniquely identifies participants. To speed up the issuance, transactions are verified in batches. Each batch is 16 transactions at a time. First have a issuer contract created: distribute-tokens-ext-id \ --chain=mainnet \ --address=0xccba4928c4e9d10242788d9cf144d865348c6c7f \ --token=0x1a7a8bd9106f2b8d977e08582dc7d24c723ab0db \ --master-address=0xa684a3371e0d46bca4a6db1ff538a44f1440a855 \ --csv-file=data.csv \ --address-column="Ethereum address" \ --amount-column="Token amount" \ --external-id-column="External ID number" \ --solc-version="v0.4.16+commit.d7661dd9" \ --gas-price=100 Then perform EIP-20 approve() to give tokens to the issuer contract deployed in the last command. Then run the distribution: distribute-tokens-ext-id \ --chain=mainnet \ --address=0xccba4928c4e9d10242788d9cf144d865348c6c7f \ --token=0x1a7a8bd9106f2b8d977e08582dc7d24c723ab0db \ --master-address=0xa684a3371e0d46bca4a6db1ff538a44f1440a855 \ --issuer-address=0x60cfb02266310e66dd99b1635e702c519a564726 \ --csv-file=combined.csv \ --address-column="Address" \ --amount-column="Amount" \ --gas-price=70 \ --start-from=670 """ project = Project() with project.get_chain(chain) as c: web3 = c.web3 print("Web3 provider is", web3.currentProvider) print("Deployer account address is", address) print("Deployer account balance is", from_wei(web3.eth.getBalance(address), "ether"), "ETH") # Goes through geth account unlock process if needed if is_account_locked(web3, address): request_account_unlock(c, address, timeout=3600 * 6) assert not is_account_locked(web3, address) Token = c.provider.get_base_contract_factory('CentrallyIssuedToken') token = Token(address=token) print("Token is", token.address) print("Total supply is", token.functions.totalSupply().call()) print("Upgrade master is", token.functions.upgradeMaster().call()) print("Deployer account token balance is", token.functions.balanceOf(address).call()) decimals = token.functions.decimals().call() print("Token decimal places is", decimals) assert decimals >= 0 decimal_multiplier = 10**decimals if gas_price: gas_price = int(gas_price) * 10**9 else: gas_price = web3.eth.gasPrice * 2 transaction = {"from": address, "gasPrice": gas_price} print("Using gas price of", gas_price / 10**9, "GWei") IssuerWithId = c.provider.get_base_contract_factory('IssuerWithId') if not issuer_address: # TODO: Fix Populus support this via an deploy argument if "JSONFile" in c.registrar.registrar_backends: del c.registrar.registrar_backends["JSONFile"] # Create issuer contract assert master_address, "You need to give master-address" args = [address, master_address, token.address] print("Deploying new issuer contract", args, "transaction parameters", transaction) issuer, txhash = c.provider.deploy_contract( "IssuerWithId", deploy_transaction=transaction, deploy_args=args) print("Deployment transaction is", txhash) print("Waiting contract to be deployed") check_succesful_tx(web3, txhash) const_args = get_constructor_arguments(issuer, args) print("Contract constructor arguments are", const_args) chain_name = chain fname = "IssuerWithId.sol" browser_driver = "chrome" verify_contract( project=project, libraries={}, # TODO: Figure out how to pass around chain_name=chain_name, address=issuer.address, contract_name="IssuerWithId", contract_filename=fname, constructor_args=const_args, browser_driver=browser_driver, compiler=solc_version) link = get_etherscan_link(chain_name, issuer.address) print("Issuer verified contract is", link) else: print("Using existing issuer contract") issuer = IssuerWithId(address=issuer_address) print("Issuer contract is", issuer.address) print("Currently issued", issuer.functions.issuedCount().call()) if not master_address: sys.exit( "Please use Token.approve() to give some allowance for the issuer contract by master address" ) allowance = token.functions.allowance(master_address, issuer.address).call() print("Issuer allowance", allowance) if allowance == 0 or not master_address: sys.exit( "Please use Token.approve() to give some allowance for the issuer contract by master address" ) print("Reading data", csv_file) with open(csv_file, "rt") as inp: reader = csv.DictReader(inp) rows = [row for row in reader] # Prevalidate addresses # For distributetokens.py this is done by combine-csv # Here we do it inline and make skip addresses that are not valid. for idx, row in enumerate(rows): addr = row[address_column].strip() try: if addr: validate_ethereum_address(addr) except ValueError as e: print("Invalid Ethereum address on row:", idx + 1, "address:", addr, "reason:", str(e), "external_id:", row[external_id_column]) # Proceed regardless of invalid data row[address_column] = "" # Start distribution start_time = time.time() start_balance = from_wei(web3.eth.getBalance(address), "ether") tx_to_confirm = [] # List of txids to confirm tx_batch_size = 16 # How many transactions confirm once print("Total rows", len(rows)) for i in range(start_from, min(start_from + limit, len(rows))): data = rows[i] addr = data[address_column].strip() external_id = data[external_id_column].strip() tokens = Decimal(data[amount_column].strip()) tokens *= decimal_multiplier end_balance = from_wei(web3.eth.getBalance(address), "ether") spent = start_balance - end_balance if addr == "": if not allow_addresless: raise RuntimeError("Encountered missing address") else: continue if not external_id: raise RuntimeError("Missing external id on row #{}".format(i + 1)) # http://stackoverflow.com/a/19965088/315168 if not tokens % 1 == 0: raise RuntimeError( "Could not issue tokens because after multiplication was not integer" ) transaction = { "from": address, "gasPrice": gas_price, "gas": 100000, # Use 100k gas unit limit } tokens = int(tokens) external_id = int(external_id) if not external_id > 0: raise RuntimeError( "External id must be a positive integer on row #{}".format( i + 1)) print("Row", i, "giving", tokens, "to", addr, "issuer", issuer.address, "time passed", time.time() - start_time, "ETH passed", spent, "gas price", transaction["gasPrice"] / (10**9)) if issuer.functions.issued(external_id).call(): print("Already issued, skipping") continue txid = issuer.functions.issue(addr, tokens, external_id).transact(transaction) tx_to_confirm.append(txid) # Confirm N transactions when batch max size is reached if len(tx_to_confirm) >= tx_batch_size: check_multiple_succesful_txs(web3, tx_to_confirm) tx_to_confirm = [] # Confirm dangling transactions check_multiple_succesful_txs(web3, tx_to_confirm) end_balance = from_wei(web3.eth.getBalance(address), "ether") print("Deployment cost is", start_balance - end_balance, "ETH") print("All done! Enjoy your decentralized future.")
def main(chain, address, contract_name, name, symbol, supply, decimals, minting_agent, release_agent, verify, verify_filename, master_address): """Deploy a single crowdsale token contract. Examples: deploy-token --chain=ropsten --address=0x3c2d4e5eae8c4a31ccc56075b5fd81307b1627c6 --name="MikkoToken 2.0" --symbol=MOO --release-agent=0x3c2d4e5eae8c4a31ccc56075b5fd81307b1627c6 --supply=100000 deploy-token --chain=kovan --contract-name="CentrallyIssuedToken" --address=0x001FC7d7E506866aEAB82C11dA515E9DD6D02c25 --name="TestToken" --symbol=MOO --supply=916 --decimals=0 --verify --verify-filename=CentrallyIssuedToken.sol """ project = Project() with project.get_chain(chain) as c: web3 = c.web3 print("Web3 provider is", web3.currentProvider) print("Deployer address is", address) print("Deployer balance is", from_wei(web3.eth.getBalance(address), "ether"), "ETH") # Goes through geth account unlock process if needed if is_account_locked(web3, address): request_account_unlock(c, address, None) decimal_multiplier = 10 ** decimals transaction = {"from": address} args = [name, symbol, supply * decimal_multiplier, decimals] if contract_name == "CentrallyIssuedToken": # TODO: Generalize settings contract args # This sets the upgrade master args = [address] + args # Make sure Populus does not pull up any cached instances of deployed contracts # TODO: Fix Populus support this via an deploy argument if "JSONFile" in c.registrar.registrar_backends: del c.registrar.registrar_backends["JSONFile"] print("Starting contract deployment") # This does deployment with all dependencies linked in contract, txhash = c.provider.deploy_contract(contract_name, deploy_transaction=transaction, deploy_args=args) check_succesful_tx(web3, txhash) print("Contract address is", contract.address) # This is needed for Etherscan contract verification # https://etherscanio.freshdesk.com/support/solutions/articles/16000053599-contract-verification-constructor-arguments const_args = get_constructor_arguments(contract, args) print("CrowdsaleToken constructor arguments is", const_args) if release_agent: print("Setting release agent to", release_agent) txid = contract.transact(transaction).setReleaseAgent(release_agent) check_succesful_tx(web3, txid) if minting_agent: print("Setting minting agent") txid = contract.transact(transaction).setMintAgent(minting_agent, True) check_succesful_tx(web3, txid) if master_address: print("Moving upgrade master to a team multisig wallet", master_address) txid = contract.transact({"from": address}).setUpgradeMaster(master_address) check_succesful_tx(web3, txid) print("Moving total supply a team multisig wallet", master_address) contract.transact({"from": address}).transfer(master_address, contract.call().totalSupply()) check_succesful_tx(web3, txid) if verify: chain_name = chain fname = verify_filename browser_driver = "chrome" verify_contract( project=project, libraries={}, # TODO: Figure out how to pass around chain_name=chain_name, address=contract.address, contract_name=contract_name, contract_filename=fname, constructor_args=const_args, # libraries=runtime_data["contracts"][name]["libraries"], browser_driver=browser_driver) link = get_etherscan_link(chain_name, contract.address) print("Verified contract is", link) print("Token supply:", contract.call().totalSupply()) # Do some contract reads to see everything looks ok try: print("Token owner:", contract.call().owner()) except ValueError: pass # No owner try: print("Token upgradeMaster:", contract.call().upgradeMaster()) except ValueError: pass try: print("Token minting finished:", contract.call().mintingFinished()) except ValueError: pass try: print("Token released:", contract.call().released()) print("Token release agent:", contract.call().releaseAgent()) except ValueError: pass print("All done! Enjoy your decentralized future.")
def main(chain, hot_wallet_address, csv_file, limit, start_from, address_column, amount_column, id_column, state_file): """Distribute ETh refunds. Reads in funds distribution data as CSV. Then sends funds from a local address. The refund status is stored as a JSON file. Example: refund --chain=kovan --hot-wallet-address=0x001fc7d7e506866aeab82c11da515e9dd6d02c25 --csv-file=refunds.csv --address-column="Refund address" --amount-column="ETH" --id-column="Email" --start-from=0 --limit=2 --state-file=refund-state.json Example CSV data: .. code-block:: csv Email,ETH,Refund address [email protected],61.52,0x0078EF811B6564c996fD10012579633B1a518b9D [email protected],111.21,0xf0b91641CCe2ADB4c0D7B90c54E7eE96CCCBc3d1 [email protected],61.52,0x0dAbC71Faa8982bF23eE2c4979d22536F5101065 [email protected],61.52,0x0B8EceBc18153166Beec1b568D510B55B560789D """ # Make a backup of the state file if os.path.exists(state_file): assert state_file.endswith(".json") backup_name = state_file.replace( ".json", "." + datetime.datetime.utcnow().isoformat() + ".bak.json") print("Backing up state file to", backup_name) shutil.copy(state_file, backup_name) project = Project() with project.get_chain(chain) as c: web3 = c.web3 print("Web3 provider is", web3.currentProvider) print("Hot wallet address is", hot_wallet_address) print("Hot wallet balance is", from_wei(web3.eth.getBalance(hot_wallet_address), "ether"), "ETH") # Goes through geth account unlock process if needed if is_account_locked(web3, hot_wallet_address): request_account_unlock(c, hot_wallet_address, timeout=3600 * 6) assert not is_account_locked(web3, hot_wallet_address) print("Reading data", csv_file) with open(csv_file, "rt", encoding='utf-8-sig') as inp: reader = csv.DictReader(inp) rows = [row for row in reader] # Check that we have unique addresses uniq_ids = set() for row in rows: print(row) id = row[id_column].strip() if id in uniq_ids: raise RuntimeError("Id appears twice in input data", id) uniq_ids.add(id) addr = row[address_column] if not is_checksum_address(addr): print("Not a checksummed address", addr) # Start distribution start_time = time.time() start_balance = from_wei(web3.eth.getBalance(hot_wallet_address), "ether") print("Total rows", len(rows)) if os.path.exists(state_file): with open(state_file, "rt") as inp: state = json.load(inp) else: state = {} for i in range(start_from, min(start_from + limit, len(rows))): data = rows[i] addr = data[address_column].strip() id = data[id_column].strip() amount = Decimal(data[amount_column].strip()) amount_wei = to_wei(amount, "ether") if id in state: print("Already refunded", id, addr, amount) continue # Use non-default gas price for speedier processing gas_price = int(web3.eth.gasPrice * 3) txid = web3.eth.sendTransaction({ "from": hot_wallet_address, "to": addr, "value": amount_wei, "gasPrice": gas_price }) duration = time.time() - start_time print("Transferring", id, amount_wei, "to", addr, "txid", txid, "duration", duration) state[id] = txid with open(state_file, "wt") as out: json.dump(state, out) check_succesful_tx(web3, txid, timeout=300) end_balance = from_wei(web3.eth.getBalance(hot_wallet_address), "ether") print("Refund cost is", start_balance - end_balance, "ETH") print("All done! Enjoy your decentralized future.")
def main(chain, address, token, csv_file, limit, start_from, issuer_address, address_column, amount_column, allow_zero, master_address): """Distribute tokens to centrally issued crowdsale participant or bounty program participants. Reads in distribution data as CSV. Then uses Issuer contract to distribute tokens. All token counts are multiplied by token contract decimal specifier. E.g. if CSV has amount 15.5, token has 2 decimal places, we will issue out 1550 raw token amount. To speed up the issuance, transactions are verified in batches. Each batch is 16 transactions at a time. Example (first run): distribute-tokens --chain=kovan --address=0x001FC7d7E506866aEAB82C11dA515E9DD6D02c25 --token=0x1644a421ae0a0869bac127fa4cce8513bd666705 --master-address=0x9a60ad6de185c4ea95058601beaf16f63742782a --csv-file=input.csv --allow-zero --address-column="Ethereum address" --amount-column="Token amount" Example (second run, continue after first run was interrupted): distribute-tokens --chain=kovan --address=0x001FC7d7E506866aEAB82C11dA515E9DD6D02c25 --token=0x1644a421ae0a0869bac127fa4cce8513bd666705 --csv-file=input.csv --allow-zero --address-column="Ethereum address" --amount-column="Token amount" --issuer-address=0x2c9877534f62c8b40aebcd08ec9f54d20cb0a945 """ project = Project() with project.get_chain(chain) as c: web3 = c.web3 print("Web3 provider is", web3.currentProvider) print("Owner address is", address) print("Owner balance is", from_wei(web3.eth.getBalance(address), "ether"), "ETH") # Goes through geth account unlock process if needed if is_account_locked(web3, address): request_account_unlock(c, address, timeout=3600*6) assert not is_account_locked(web3, address) Token = c.provider.get_base_contract_factory('CentrallyIssuedToken') token = Token(address=token) print("Token is", token.address) print("Total supply is", token.call().totalSupply()) print("Upgrade master is", token.call().upgradeMaster()) print("Owner token balance master is", token.call().balanceOf(address)) decimals = token.call().decimals() print("Token decimal places is", decimals) assert decimals >= 0 decimal_multiplier = 10**decimals transaction = {"from": address} Issuer = c.provider.get_base_contract_factory('Issuer') if not issuer_address: # TODO: Fix Populus support this via an deploy argument if "JSONFile" in c.registrar.registrar_backends: del c.registrar.registrar_backends["JSONFile"] # Create issuer contract assert master_address, "You need to give master-address" args = [address, master_address, token.address] print("Deploying new issuer contract", args) issuer, txhash = c.provider.deploy_contract("Issuer", deploy_transaction=transaction, deploy_args=args) check_succesful_tx(web3, txhash) const_args = get_constructor_arguments(issuer, args) chain_name = chain fname = "Issuer.sol" browser_driver = "chrome" verify_contract( project=project, libraries={}, # TODO: Figure out how to pass around chain_name=chain_name, address=issuer.address, contract_name="Issuer", contract_filename=fname, constructor_args=const_args, # libraries=runtime_data["contracts"][name]["libraries"], browser_driver=browser_driver) link = get_etherscan_link(chain_name, issuer.address) print("Issuer verified contract is", link) else: print("Reusing existing issuer contract") issuer = Issuer(address=issuer_address) print("Issuer contract is", issuer.address) print("Currently issued", issuer.call().issuedCount()) if not master_address: sys.exit("Please use Token.approve() to give some allowance for the issuer contract by master address") allowance = token.call().allowance(master_address, issuer.address) print("Issuer allowance", allowance) if allowance == 0 or not master_address: sys.exit("Please use Token.approve() to give some allowance for the issuer contract by master address") print("Reading data", csv_file) with open(csv_file, "rt") as inp: reader = csv.DictReader(inp) rows = [row for row in reader] # Check that we have unique addresses uniq_addresses = set() for row in rows: addr = row[address_column].strip() if addr in uniq_addresses: raise RuntimeError("Address appears twice in input data", addr) uniq_addresses.add(addr) # Start distribution start_time = time.time() start_balance = from_wei(web3.eth.getBalance(address), "ether") tx_to_confirm = [] # List of txids to confirm tx_batch_size = 16 # How many transactions confirm once print("Total rows", len(rows)) for i in range(start_from, min(start_from+limit, len(rows))): data = rows[i] addr = data[address_column].strip() tokens = Decimal(data[amount_column].strip()) tokens *= decimal_multiplier end_balance = from_wei(web3.eth.getBalance(address), "ether") spent = start_balance - end_balance if tokens == 0: if not allow_zero: raise RuntimeError("Encountered zero amount") else: continue # http://stackoverflow.com/a/19965088/315168 if not tokens % 1 == 0: raise RuntimeError("Could not issue tokens because after multiplication was not integer") transaction = { "from": address, "gasPrice": int(web3.eth.gasPrice * 1.5) } tokens = int(tokens) print("Row", i, "giving", tokens, "to", addr, "issuer", issuer.address, "time passed", time.time() - start_time, "ETH passed", spent) if issuer.call().issued(addr): print("Already issued, skipping") continue txid = issuer.transact(transaction).issue(addr, tokens) tx_to_confirm.append(txid) # Confirm N transactions when batch max size is reached if len(tx_to_confirm) >= tx_batch_size: check_multiple_succesful_txs(web3, tx_to_confirm) tx_to_confirm = [] # Confirm dangling transactions check_multiple_succesful_txs(web3, tx_to_confirm) end_balance = from_wei(web3.eth.getBalance(address), "ether") print("Deployment cost is", start_balance - end_balance, "ETH") print("All done! Enjoy your decentralized future.")
def main(chain, address, contract_address, csv_file, limit, start_from, multiplier): """Rebuild data on relaunched CrowdsaleToken contract. This allows you rerun investment data to fix potential errors in the contract. Example: rebuild-crowdsale --address=0x001FC7d7E506866aEAB82C11dA515E9DD6D02c25 --chain=kovan --contract-address=0xf09e4a27a02afd29590a989cb2dda9af8eebc77f --start-from=0 --limit=600 --multiplier=12 --csv-file=inputdata.csv """ project = Project() with project.get_chain(chain) as c: web3 = c.web3 print("Web3 provider is", web3.currentProvider) print("Owner address is", address) print("Owner balance is", from_wei(web3.eth.getBalance(address), "ether"), "ETH") # Goes through geth account unlock process if needed if is_account_locked(web3, address): request_account_unlock(c, address, timeout=3600 * 6) transaction = {"from": address} print("Reading data", csv_file) with open(csv_file, "rt") as inp: reader = csv.DictReader(inp) rows = [row for row in reader] print("Source data has", len(rows), "rows") print("Importing rows", start_from, "-", start_from + limit) RelaunchedCrowdsale = c.provider.get_contract_factory( 'RelaunchedCrowdsale') relaunched_crowdsale = RelaunchedCrowdsale(address=contract_address) print("Crowdsale contract is", contract_address) print("Currently issued", relaunched_crowdsale.call().tokensSold()) assert relaunched_crowdsale.call().owner().lower() == address.lower( ), "We are not the crowdsale owner. Real owner is {}, we are {}".format( relaunched_crowdsale.call().owner(), address) multiplier = 10**multiplier start_time = time.time() start_balance = from_wei(web3.eth.getBalance(address), "ether") for i in range(start_from, min(start_from + limit, len(rows))): data = rows[i] addr = data["Address"] wei = to_wei(data["Invested ETH"], "ether") tokens = int(data["Received tokens"]) orig_txid = int(data["Tx hash"], 16) # orig_tx_index = int(data["Tx index"]) tokens *= multiplier end_balance = from_wei(web3.eth.getBalance(address), "ether") spent = start_balance - end_balance print("Row", i, "giving", tokens, "to", addr, "from tx", orig_txid, "ETH spent", spent, "time passed", time.time() - start_time) if relaunched_crowdsale.call().getRestoredTransactionStatus( orig_txid): print("Already restored, skipping") continue txid = relaunched_crowdsale.transact( transaction).setInvestorDataAndIssueNewToken( addr, wei, tokens, orig_txid) check_succesful_tx(web3, txid) end_balance = from_wei(web3.eth.getBalance(address), "ether") print("Deployment cost is", start_balance - end_balance, "ETH") print("All done! Enjoy your decentralized future.")
def main(chain, hot_wallet_address, csv_file, limit, start_from, address_column, amount_column, id_column, state_file): """Distribute ETh refunds. Reads in funds distribution data as CSV. Then sends funds from a local address. The refund status is stored as a JSON file. Example: refund --chain=kovan --hot-wallet-address=0x001fc7d7e506866aeab82c11da515e9dd6d02c25 --csv-file=refunds.csv --address-column="Refund address" --amount-column="ETH" --id-column="Email" --start-from=0 --limit=2 --state-file=refund-state.json Example CSV data: .. code-block:: csv Email,ETH,Refund address [email protected],61.52,0x0078EF811B6564c996fD10012579633B1a518b9D [email protected],111.21,0xf0b91641CCe2ADB4c0D7B90c54E7eE96CCCBc3d1 [email protected],61.52,0x0dAbC71Faa8982bF23eE2c4979d22536F5101065 [email protected],61.52,0x0B8EceBc18153166Beec1b568D510B55B560789D """ # Make a backup of the state file if os.path.exists(state_file): assert state_file.endswith(".json") backup_name = state_file.replace(".json", "." + datetime.datetime.utcnow().isoformat() + ".bak.json") print("Backing up state file to", backup_name) shutil.copy(state_file, backup_name) project = Project() with project.get_chain(chain) as c: web3 = c.web3 print("Web3 provider is", web3.currentProvider) print("Hot wallet address is", hot_wallet_address) print("Hot wallet balance is", from_wei(web3.eth.getBalance(hot_wallet_address), "ether"), "ETH") # Goes through geth account unlock process if needed if is_account_locked(web3, hot_wallet_address): request_account_unlock(c, hot_wallet_address, timeout=3600*6) assert not is_account_locked(web3, hot_wallet_address) print("Reading data", csv_file) with open(csv_file, "rt", encoding='utf-8-sig') as inp: reader = csv.DictReader(inp) rows = [row for row in reader] # Check that we have unique addresses uniq_ids = set() for row in rows: print(row) id = row[id_column].strip() if id in uniq_ids: raise RuntimeError("Id appears twice in input data", id) uniq_ids.add(id) addr = row[address_column] if not is_checksum_address(addr): print("Not a checksummed address", addr) # Start distribution start_time = time.time() start_balance = from_wei(web3.eth.getBalance(hot_wallet_address), "ether") print("Total rows", len(rows)) if os.path.exists(state_file): with open(state_file, "rt") as inp: state = json.load(inp) else: state = {} for i in range(start_from, min(start_from+limit, len(rows))): data = rows[i] addr = data[address_column].strip() id = data[id_column].strip() amount = Decimal(data[amount_column].strip()) amount_wei = to_wei(amount, "ether") if id in state: print("Already refunded", id, addr, amount) continue # Use non-default gas price for speedier processing gas_price = int(web3.eth.gasPrice * 3) txid = web3.eth.sendTransaction({"from": hot_wallet_address, "to": addr, "value": amount_wei, "gasPrice": gas_price}) duration = time.time() - start_time print("Transferring", id, amount_wei, "to", addr, "txid", txid, "duration", duration) state[id] = txid with open(state_file, "wt") as out: json.dump(state, out) check_succesful_tx(web3, txid, timeout=300) end_balance = from_wei(web3.eth.getBalance(hot_wallet_address), "ether") print("Refund cost is", start_balance - end_balance, "ETH") print("All done! Enjoy your decentralized future.")