def update_denormalised_balance(self): """Denormalise the balance for this account.""" raw_balance, last_block_num, last_block_at = self.calculate_sum_from_deltas() self.set_balance_uint(raw_balance) self.last_block_updated_at = last_block_at self.last_block_num = last_block_num self.balance_calculated_at = now()
def broadcast(self, tx: _PreparedTransaction): """Push transactions to Ethereum network.""" if tx.broadcast_account.address != self.address: raise AddressConfigurationMismatch("Could not broadcast due to address mismatch. A pendign transaction was created for account {}, but we are using configured account {}".format(tx.broadcast_account.addres, self.address)) tx_data = tx.unsigned_payload signed = self.web3.eth.account.signTransaction(tx_data, self.private_key_hex) tx.txid = signed.hash.hex() self.web3.eth.sendRawTransaction(signed.rawTransaction) tx.broadcasted_at = now() return tx
def update_status(self, tx: _PreparedTransaction): """Update tx status from Etheruem network.""" assert tx.txid # https://web3py.readthedocs.io/en/stable/web3.eth.html#web3.eth.Eth.getTransactionReceipt receipt = self.web3.eth.getTransactionReceipt(tx.txid) if receipt: tx.result_block_num = receipt["blockNumber"] # https://ethereum.stackexchange.com/a/6003/620 if receipt["status"] == 0: tx.result_transaction_success = False tx.result_transaction_reason = "Transaction failed" # TODO: Need some logic to separate failure modes else: tx.result_transaction_success = True tx.result_fetched_at = now() return tx
def verify_on_etherscan(logger: Logger, network: str, tx: _PreparedTransaction, api_key: str, session, timeout=120): """Verify a contrcact deployment on Etherscan. Uses https://etherscan.io/apis#contracts """ assert network in ("ethereum", "kovan", "ropsten", "rinkerby") if network != "ethereum": url = "https://api-{}.etherscan.io/api".format(network) else: url = "https://api.etherscan.io/api" assert tx.result_transaction_success assert tx.contract_deployment source = tx.flattened_source_code assert source.strip(), "Source code missing" compiler = tx.compiler_version address = tx.contract_address constructor_arguments = tx.constructor_arguments contract_name = tx.contract_name data = { "apikey": api_key, "module": "contract", "contractaddress": address, "action": "verifysourcecode", "sourceCode": source, "contractname": contract_name, "compilerversion": "v" + compiler, # https://etherscan.io/solcversions "constructorArguements": constructor_arguments[2:], # Remove leading 0x "optimizationUsed": 1, # TODO: Hardcoded "runs": 500, # TODO: Hardcoded } info_data = data.copy() del info_data["sourceCode"] # Too verbose del info_data["apikey"] # Security logger.info("Calling EtherScan API as: %s", info_data) # # Step 1: EtherScan validates input and gives us a ticket id to track the submission status # resp = session.post(url, data) # {'status': '0', 'message': 'NOTOK', 'result': 'Error!'} data = resp.json() logger.info("Etherscan replied %s", data) if data["status"] == "0": if "already verified" in data["result"]: return raise CouldNotVerifyOnEtherScan("Could not verify contract: " + address + " " + str(data)) ticket = data["result"] # # Step 2: Poll for results # ready = False started = time.time() while not ready and time.time() < started + timeout: data = { "apikey": api_key, "module": "contract", "action": "checkverifystatus", "guid": ticket, } logger.info("Checking verification status on EtherScan API as: %s", info_data) resp = session.post(url, data) # {'status': '0', 'message': 'NOTOK', 'result': 'Error!'} data = resp.json() logger.info("Got reply %s", data) if data["result"] == "Pending in queue": # Keep polling # {'status': '0', 'message': 'NOTOK', 'result': 'Pending in queue'} time.sleep(5) continue elif data["status"] == "0": # Produced binary did not match raise CouldNotVerifyOnEtherScan("Could not verify contract: " + address + " " + str(data)) else: # All good assert data["status"] == "1" # {'status': '1', 'message': 'OK', 'result': 'Pass - Verified'} break # Write to the database that we managed verify the contract tx.verified_at = now() tx.verification_info = data