def restart_nonce( logger: Logger, dbsession: Session, network: str, ethereum_node_url: str, ethereum_private_key: str, ethereum_gas_limit: str, ethereum_gas_price: str, ): check_good_private_key(ethereum_private_key) web3 = create_web3(ethereum_node_url) service = EthereumStoredTXService(network, dbsession, web3, ethereum_private_key, ethereum_gas_price, ethereum_gas_limit, BroadcastAccount, PreparedTransaction) service.ensure_accounts_in_sync() account = service.get_or_create_broadcast_account() txs = service.get_last_transactions(limit=1) if txs.count() > 0: raise HistoryDeleteNeeded( "Cannot reset nonce as the database contains txs for {}. Delete database to restart." .format(service.address)) # read nonce from the network and record to the database tx_count = web3.eth.getTransactionCount(service.address) account.current_nonce = tx_count logger.info("Address %s, nonce is now set to %d", service.address, account.current_nonce) dbsession.flush()
def broadcast( logger: Logger, dbsession: Session, network: str, ethereum_node_url: Union[str, Web3], ethereum_private_key: str, ethereum_gas_limit: Optional[str], ethereum_gas_price: Optional[str], commit=True, ): """Issue out a new Ethereum token.""" check_good_private_key(ethereum_private_key) web3 = create_web3(ethereum_node_url) service = EthereumStoredTXService(network, dbsession, web3, ethereum_private_key, ethereum_gas_price, ethereum_gas_limit, BroadcastAccount, PreparedTransaction) service.ensure_accounts_in_sync() pending_broadcasts = service.get_pending_broadcasts() logger.info("Pending %d transactions for broadcasting in network %s", pending_broadcasts.count(), network) if pending_broadcasts.count() == 0: logger.info( "No new transactions to broadcast. Use sto tx-update command to see tx status." ) return [] account = Account.privateKeyToAccount(ethereum_private_key) balance = web3.eth.getBalance(account.address) logger.info("Our address %s has ETH balance of %f for operations", account.address, from_wei(balance, "ether")) txs = list(pending_broadcasts) # https://stackoverflow.com/questions/41985993/tqdm-show-progress-for-a-generator-i-know-the-length-of for tx in tqdm(txs, total=pending_broadcasts.count()): try: service.broadcast(tx) # logger.info("Broadcasted %s", tx.txid) except Exception as e: logger.exception(e) logger.error("Failed to broadcast transaction %s: %s", tx.txid, tx.human_readable_description) raise e if commit: dbsession.commit() # Try to minimise file system sync issues return txs
def distribute_single(logger: Logger, dbsession: Session, network: str, ethereum_node_url: Union[str, Web3], ethereum_abi_file: Optional[str], ethereum_private_key: Optional[str], ethereum_gas_limit: Optional[int], ethereum_gas_price: Optional[int], token_address: str, ext_id: str, email: str, name: str, to_address: str, amount: Decimal) -> bool: """Send out a single transfer. :return: True if a new tx for broadcasting was created """ d = DistributionEntry(ext_id, email, name, to_address, amount) check_good_private_key(ethereum_private_key) abi = get_abi(ethereum_abi_file) web3 = create_web3(ethereum_node_url) service = EthereumStoredTXService(network, dbsession, web3, ethereum_private_key, ethereum_gas_price, ethereum_gas_limit, BroadcastAccount, PreparedTransaction) logger.info( "Starting creating distribution transactions for %s token from nonce %s", token_address, service.get_next_nonce()) total = d.amount * 10**18 available = service.get_raw_token_balance(token_address, abi) if total > available: raise NotEnoughTokens( "Not enough tokens for distribution. Account {} has {} raw token balance, needed {}" .format(service.get_or_create_broadcast_account().address, available, total)) if not service.is_distributed(d.external_id, token_address): # Going to tx queue raw_amount = int(d.amount * 10**18) note = "Distributing tokens, raw amount: {}".format(raw_amount) service.distribute_tokens(d.external_id, d.address, raw_amount, token_address, abi, note) logger.info("New broadcast has been created") return True else: logger.error("Already distributed") return False
def distribute_tokens(logger: Logger, dbsession: Session, network: str, ethereum_node_url: Union[str, Web3], ethereum_abi_file: Optional[str], ethereum_private_key: Optional[str], ethereum_gas_limit: Optional[int], ethereum_gas_price: Optional[int], token_address: str, dists: List[DistributionEntry]) -> Tuple[int, int]: """Sends tokens to their first owners in primary markets.""" check_good_private_key(ethereum_private_key) abi = get_abi(ethereum_abi_file) web3 = create_web3(ethereum_node_url) service = EthereumStoredTXService(network, dbsession, web3, ethereum_private_key, ethereum_gas_price, ethereum_gas_limit, BroadcastAccount, PreparedTransaction) logger.info( "Starting creating distribution transactions for %s token from nonce %s", token_address, service.get_next_nonce()) total = sum([dist.amount * 10**18 for dist in dists]) available = service.get_raw_token_balance(token_address, abi) if total > available: raise NotEnoughTokens( "Not enough tokens for distribution. Account {} has {} raw token balance, needed {}" .format(service.get_or_create_broadcast_account().address, available, total)) new_distributes = old_distributes = 0 for d in tqdm(dists): if not service.is_distributed(d.external_id, token_address): # Going to tx queue raw_amount = int(d.amount * 10**18) note = "Distributing tokens, raw amount: {}".format(raw_amount) service.distribute_tokens(d.external_id, d.address, raw_amount, token_address, abi, note) new_distributes += 1 else: # CSV reimports old_distributes += 1 logger.info("Prepared transactions for broadcasting for network %s", network) return new_distributes, old_distributes
def update_status( logger: Logger, dbsession: Session, network: str, ethereum_node_url: Union[str, Web3], ethereum_private_key: str, ethereum_gas_limit: str, ethereum_gas_price: str, commit=True, ): """Issue out a new Ethereum token.""" check_good_private_key(ethereum_private_key) web3 = create_web3(ethereum_node_url) service = EthereumStoredTXService(network, dbsession, web3, ethereum_private_key, ethereum_gas_price, ethereum_gas_limit, BroadcastAccount, PreparedTransaction) unfinished_txs = service.get_unmined_txs() logger.info( "Updating status for %d unfinished transactions for broadcasting in network %s", unfinished_txs.count(), network) if unfinished_txs.count() == 0: logger.info( "No transactions to update. Use sto tx-last command to show the status of the last transactions." ) return [] unfinished_txs = list(unfinished_txs) # https://stackoverflow.com/questions/41985993/tqdm-show-progress-for-a-generator-i-know-the-length-of for tx in tqdm(unfinished_txs): service.update_status(tx) if commit: dbsession.commit() # Try to minimise file system sync issues return unfinished_txs
def next_nonce( logger: Logger, dbsession: Session, network: str, ethereum_node_url: str, ethereum_private_key: str, ethereum_gas_limit: str, ethereum_gas_price: str, ): check_good_private_key(ethereum_private_key) web3 = create_web3(ethereum_node_url) service = EthereumStoredTXService(network, dbsession, web3, ethereum_private_key, ethereum_gas_price, ethereum_gas_limit, BroadcastAccount, PreparedTransaction) account = service.get_or_create_broadcast_account() ft = pretty_date(account.created_at) logger.info("Address %s, created at %s, nonce is now set to %d", service.address, ft, account.current_nonce)
def get_last_transactions(logger: Logger, dbsession: Session, network: str, limit: int, ethereum_node_url: str, ethereum_private_key: str, ethereum_gas_limit: str, ethereum_gas_price: str, ): """Issue out a new Ethereum token.""" check_good_private_key(ethereum_private_key) web3 = create_web3(ethereum_node_url) service = EthereumStoredTXService(network, dbsession, web3, ethereum_private_key, ethereum_gas_price, ethereum_gas_limit, BroadcastAccount, PreparedTransaction) last_txs = service.get_last_transactions(limit) if last_txs.count() == 0: logger.info("No transactions yet") return [] return list(last_txs)
def deploy_token_contracts( logger: Logger, dbsession: Session, network: str, ethereum_node_url: Union[str, Web3], ethereum_abi_file: Optional[str], ethereum_private_key: Optional[str], ethereum_gas_limit: Optional[int], ethereum_gas_price: Optional[int], name: str, symbol: str, url: str, amount: int, transfer_restriction: str): """Issue out a new Ethereum token.""" assert type(amount) == int decimals = 18 # Everything else is bad idea check_good_private_key(ethereum_private_key) abi = get_abi(ethereum_abi_file) web3 = create_web3(ethereum_node_url) # We do not have anything else implemented yet assert transfer_restriction == "unrestricted" service = EthereumStoredTXService(network, dbsession, web3, ethereum_private_key, ethereum_gas_price, ethereum_gas_limit, BroadcastAccount, PreparedTransaction) # Deploy security token note = "Deploying token contract for {}".format(name) deploy_tx1 = service.deploy_contract("SecurityToken", abi, note, constructor_args={ "_name": name, "_symbol": symbol, "_url": url }) # See SecurityToken.sol # Deploy transfer agent note = "Deploying unrestricted transfer policy for {}".format(name) deploy_tx2 = service.deploy_contract("UnrestrictedTransferAgent", abi, note) # Set transfer agent note = "Making transfer restriction policy for {} effective".format(name) contract_address = deploy_tx1.contract_address update_tx1 = service.interact_with_contract( "SecurityToken", abi, contract_address, note, "setTransactionVerifier", {"newVerifier": deploy_tx2.contract_address}) # Issue out initial shares note = "Creating {} initial shares for {}".format(amount, name) contract_address = deploy_tx1.contract_address amount_18 = int(amount * 10**decimals) update_tx2 = service.interact_with_contract("SecurityToken", abi, contract_address, note, "issueTokens", {"value": amount_18}) logger.info("Prepared transactions for broadcasting for network %s", network) logger.info("STO token contract address will be %s%s%s", colorama.Fore.LIGHTGREEN_EX, deploy_tx1.contract_address, colorama.Fore.RESET) return [deploy_tx1, deploy_tx2, update_tx1, update_tx2]
def diagnose(logger: Logger, node_url: str, private_key_hex: str, check_timestamps=True) -> Optional[Exception]: """Run Ethereum connection and account diagnostics. Check that the user has properly configured Ethereum node and private key. Never fails. Exceptions are written to the logger output and returned. """ try: check_good_node_url(node_url) logger.info("Attempting to connect to Ethereum node %s", node_url) web3 = create_web3(node_url) logger.info("Connected to Ethereum node software %s", web3.version.node) block_num = web3.eth.blockNumber d = datetime.datetime.utcnow() unix_time = calendar.timegm(d.utctimetuple()) block_info = web3.eth.getBlock(block_num) last_time = block_info["timestamp"] if check_timestamps: if last_time == 0: raise NodeNotSynced( "Looks like your node has not yet been synced.") ago = unix_time - last_time logger.info( "Current Ethereum node block number: %d, last block %d seconds ago - compare this to data on https://etherscan.io", block_num, ago) if ago < 0: raise NodeNotSynced( "Last block in the future? Do we have a clock with a wrong timezone somewhere?" ) if ago > 1800: raise NodeNotSynced( "Looks like your node has not received a block for half an hour. It is most likely unsynced at the moment." ) check_good_private_key(private_key_hex) logger.info("Using private key %s...", private_key_hex[0:3]) account = Account.privateKeyToAccount(to_bytes(hexstr=private_key_hex)) balance = web3.eth.getBalance(account.address) logger.info("Address %s has ETH balance of %f", account.address, from_wei(balance, "ether")) if balance == 0: raise NeedMoney( "Your Ethereum account {} needs to have ETH in order to use this tool" .format(account.address)) except Exception as e: logger.error("Diagnostics failure") logger.exception(e) return e