def create_address(web3: Web3, dbsession: Session, opid: UUID): """User requests new hosted address. We create a hosted wallet contract. The contract id is associated with the user in the database. We hold the the only owner address of the wallet. The wallet code is based on https://github.com/ethereum/meteor-dapp-wallet/blob/master/Wallet.sol """ assert isinstance(opid, UUID) @retryable(tm=dbsession.transaction_manager) def finish_op(): op = dbsession.query(CryptoOperation).get(opid) txid = wallet.initial_txid receipt = web3.eth.getTransactionReceipt(txid) op.txid = txid_to_bin(txid) op.block = receipt["blockNumber"] op.address.address = eth_address_to_bin(wallet.address) op.external_address = op.address.address # There is no broadcast wait, so we can close this right away op.mark_performed() op.mark_broadcasted() op.mark_complete() logger.info("Starting wallet creation for %s", opid) wallet = HostedWallet.create(web3) logger.info("Updating db for wallet creation for %s", opid) finish_op()
def setup_listeners(self): """Setup subsystems that scan for incoming events from geth.""" wallet_contract = HostedWallet.contract_class(self.web3) token_contract = Token.contract_class(self.web3) self.eth_wallet_listener = EthWalletListener(self.web3, wallet_contract, self.dbsession, self.asset_network_id, registry=self.registry) self.eth_token_listener = EthTokenListener(self.web3, token_contract, self.dbsession, self.asset_network_id, registry=self.registry) self.confirmation_updater = DatabaseConfirmationUpdater(self.web3, self.dbsession, self.asset_network_id, self.registry) self.op_queue_manager = OperationQueueManager(self.web3, self.dbsession, self.asset_network_id, self.registry)
def hosted_wallet(web3: Web3, coinbase: str) -> HostedWallet: """Deploy a smart contract to local private blockchain so test functions can stress it out. :param client: py.test fixture to create RPC client to call geth node :param geth_node: py.test fixture to spin up geth node with test network parameters :param coinbase: Ethereum account number for coinbase account where our mined ETHs appear :return: 0x prefixed hexadecimal address of the deployed contract """ return HostedWallet.create(web3)
def hosted_wallet(web3: Web3, coinbase: str) -> HostedWallet: """Deploy a smart contract to local private blockchain so test functions can stress it out. :param client: py.test fixture to create RPC client to call geth node :param geth_node: py.test fixture to spin up geth node with test network parameters :param coinbase: Ethereum account number for coinbase account where our mined ETHs appear :return: 0x prefixed hexadecimal address of the deployed contract """ return HostedWallet.create(web3, contract_name="Wallet2")
def withdraw_token(web3: Web3, dbsession: Session, opid: UUID): """Perform token withdraw operation from the wallet.""" @retryable(tm=dbsession.transaction_manager) def prepare_withdraw(): # Check everyting looks sane op = dbsession.query(CryptoOperation).get(opid) assert op.crypto_account.id assert op.crypto_account.account.id assert op.holding_account.id assert op.holding_account.get_balance() > 0 assert op.external_address assert op.required_confirmation_count # Should be set by the creator asset = op.holding_account.asset assert asset.asset_class == AssetClass.token from_address = bin_to_eth_address(op.crypto_account.address.address) to_address = bin_to_eth_address(op.external_address) asset_address = bin_to_eth_address(asset.external_id) # How much we are withdrawing amount = op.holding_account.transactions.one().amount op.mark_performed() # Don't try to pick this op automatically again return from_address, to_address, asset_address, amount @retryable(tm=dbsession.transaction_manager) def close_withdraw(): # Fill in details. # Block number will be filled in later, when confirmation updater picks a transaction receipt for this operation. op = dbsession.query(CryptoOperation).get(opid) op.txid = txid_to_bin(txid) op.block = None op.mark_broadcasted() from_address, to_address, asset_address, amount = prepare_withdraw() wallet = HostedWallet.get(web3, from_address) token = Token.get(web3, asset_address) amount = token.validate_transfer_amount(amount) # Perform actual transfer outside retryable transaction # boundaries to avoid double withdrwa txid = wallet.execute(token.contract, "transfer", [to_address, amount]) close_withdraw()
def withdraw_eth(web3: Web3, dbsession: Session, opid: UUID): """Perform ETH withdraw operation from the wallet.""" @retryable(tm=dbsession.transaction_manager) def prepare_withdraw(): # Check everyting looks sane op = dbsession.query(CryptoOperation).get(opid) assert op.crypto_account.id assert op.crypto_account.account.id assert op.holding_account.id assert op.holding_account.get_balance() > 0 assert op.external_address assert op.required_confirmation_count # Should be set by the creator address = bin_to_eth_address(op.crypto_account.address.address) # How much we are withdrawing amount = op.holding_account.transactions.one().amount op.mark_performed() # Don't pick this to action list anymore gas = op.other_data.get("gas") data = op.other_data.get("data") return address, amount, op.external_address, gas, data @retryable(tm=dbsession.transaction_manager) def close_withdraw(): # Fill in details. # Block number will be filled in later, when confirmation updater picks a transaction receipt for this operation. op = dbsession.query(CryptoOperation).get(opid) op.txid = txid_to_bin(txid) op.block = None op.mark_broadcasted() address, amount, external_address, gas, data = prepare_withdraw() # Do actual network communications outside the transaction, # so we avoid double withdraws in the case transaction is retried wallet = HostedWallet.get(web3, address) txid = wallet.withdraw(bin_to_eth_address(external_address), amount, max_gas=gas, data=data) close_withdraw()
def withdraw_eth(web3: Web3, dbsession: Session, opid: UUID): """Perform ETH withdraw operation from the wallet.""" @retryable(tm=dbsession.transaction_manager) def prepare_withdraw(): # Check everyting looks sane op = dbsession.query(CryptoOperation).get(opid) assert op.crypto_account.id assert op.crypto_account.account.id assert op.holding_account.id assert op.holding_account.get_balance() > 0 assert op.external_address assert op.required_confirmation_count # Should be set by the creator address = bin_to_eth_address(op.crypto_account.address.address) # How much we are withdrawing amount = op.holding_account.transactions.one().amount op.mark_performed() # Don't pick this to action list anymore return address, amount, op.external_address @retryable(tm=dbsession.transaction_manager) def close_withdraw(): # Fill in details. # Block number will be filled in later, when confirmation updater picks a transaction receipt for this operation. op = dbsession.query(CryptoOperation).get(opid) op.txid = txid_to_bin(txid) op.block = None op.mark_broadcasted() address, amount, external_address = prepare_withdraw() # Do actual network communications outside the transaction, # so we avoid double withdraws in the case transaction is retried wallet = HostedWallet.get(web3, address) txid = wallet.withdraw(bin_to_eth_address(external_address), amount) close_withdraw()
def get_wallet(web3, address): return HostedWallet.get(web3, address)