def _first_allowed_block_to_monitor(token_network_address: TokenNetworkAddress, channel: Channel, context: Context) -> BlockNumber: # Get token_network_contract abi = CONTRACT_MANAGER.get_contract_abi(CONTRACT_TOKEN_NETWORK) token_network_contract = context.web3.eth.contract( abi=abi, address=Address(token_network_address)) # Use the same assumptions as the MS contract, which can't get the real # channel timeout and close block. settle_block_number, _ = token_network_contract.functions.getChannelInfo( channel.identifier, channel.participant1, channel.participant2).call() assumed_settle_timeout = token_network_contract.functions.settlement_timeout_min( ).call() assumed_close_block = settle_block_number - assumed_settle_timeout # Call smart contract to use its firstBlockAllowedToMonitor calculation return BlockNumber( context.monitoring_service_contract.functions. firstBlockAllowedToMonitor( closed_at_block=assumed_close_block, settle_timeout=assumed_settle_timeout, participant1=channel.participant1, participant2=channel.participant2, monitoring_service_address=context.ms_state.address, ).call())
def info( private_key: str, web3: Web3, contracts: Dict[str, Contract], start_block: BlockNumber, ) -> None: log.info("Using RPC endpoint", rpc_url=get_web3_provider_info(web3)) service_registry_contract = contracts[CONTRACT_SERVICE_REGISTRY] deposit_token_address = service_registry_contract.functions.token().call() deposit_token_contract = web3.eth.contract( address=deposit_token_address, abi=CONTRACT_MANAGER.get_contract_abi(CONTRACT_CUSTOM_TOKEN), ) caller_address = private_key_to_address(private_key) fmt_amount = get_token_formatter(deposit_token_contract) deposits = find_deposits( web3=web3, service_address=caller_address, service_registry_contract=service_registry_contract, start_block=start_block, ) if not deposits: print("No deposits were made from this account.") return print("Deposits:") for dep in deposits: print(f" * block {dep['block_number']}", end=", ") print(f"amount: {fmt_amount(dep['amount'])}", end=", ") if dep["withdrawn"]: print("WITHDRAWN") else: print("increased validity till " + dep["valid_till"])
def find_withdrawable_deposit( web3: Web3, service_address: Address, service_registry_contract: Contract, start_block: BlockNumber, ) -> Address: # Get formatter for token amounts deposit_token_address = service_registry_contract.functions.token().call() deposit_token_contract = web3.eth.contract( address=deposit_token_address, abi=CONTRACT_MANAGER.get_contract_abi(CONTRACT_CUSTOM_TOKEN), ) fmt_amount = get_token_formatter(deposit_token_contract) # Find deposits deposits = find_deposits(web3, service_address, service_registry_contract, start_block) deposits = [d for d in deposits if not d["withdrawn"]] if not deposits: click.echo("No deposits found!", err=True) sys.exit(1) # Inform user print("Deposit found:") for deposit_event in deposits: valid_till = deposit_event["valid_till"] amount = deposit_event["amount"] print(f" * valid till {valid_till}, amount: {fmt_amount(amount)}") if len(deposits) > 1: print("I will withdraw the first (oldest) one. Run this script again for the next deposit") return to_canonical_address(deposits[0]["deposit_contract"])
def connect_to_blockchain( eth_rpc: str, used_contracts: List[str], address_overwrites: Dict[str, Address] ) -> Tuple[Web3, Dict[str, Contract], BlockNumber]: try: provider = HTTPProvider(eth_rpc) web3 = Web3(provider) # Will throw ConnectionError on bad Ethereum client chain_id = ChainID(int(web3.net.version)) except requests.exceptions.ConnectionError: log.error( "Can not connect to the Ethereum client. Please check that it is running and that " "your settings are correct.", eth_rpc=eth_rpc, ) sys.exit(1) # Add POA middleware for geth POA chains, no/op for other chains web3.middleware_stack.inject(geth_poa_middleware, layer=0) # give web3 some time between retries before failing provider.middlewares.replace("http_retry_request", http_retry_with_backoff_middleware) addresses, start_block = get_contract_addresses_and_start_block( chain_id=chain_id, contracts=used_contracts, address_overwrites=address_overwrites) contracts = { c: web3.eth.contract(abi=CONTRACT_MANAGER.get_contract_abi(c), address=address) for c, address in addresses.items() } return web3, contracts, start_block
def connect_to_blockchain( eth_rpc: URI, gas_price_strategy: Optional[Callable[[Web3, Any], Wei]], used_contracts: List[str], address_overwrites: Dict[str, Address], development_environment: ContractDevEnvironment, ) -> Tuple[Web3, Dict[str, Contract], BlockNumber]: try: provider = HTTPProvider(eth_rpc) web3 = Web3(provider) # Will throw ConnectionError on bad Ethereum client chain_id = ChainID(web3.eth.chain_id) except requests.exceptions.ConnectionError: log.error( "Can not connect to the Ethereum client. Please check that it is running and that " "your settings are correct.", eth_rpc=eth_rpc, ) sys.exit(1) # Add POA middleware for geth POA chains, no/op for other chains web3.middleware_onion.inject(geth_poa_middleware, layer=0) # Set gas price strategy # for that we also need a cache middleware, otherwise sampling is expensive web3.middleware_onion.add(simple_cache_middleware) if not gas_price_strategy: chain_id = ChainID(web3.eth.chain_id) gas_price_strategy = (rpc_gas_price_strategy if "arbitrum" in ID_TO_CHAINNAME.get( chain_id, "") else fast_gas_price_strategy) web3.eth.setGasPriceStrategy(gas_price_strategy) # give web3 some time between retries before failing # TODO: find a way to to this type safe provider.middlewares.replace( # type: ignore "http_retry_request", http_retry_with_backoff_middleware) addresses, start_block = get_contract_addresses_and_start_block( chain_id=chain_id, contracts=used_contracts, address_overwrites=address_overwrites, development_environment=development_environment, ) contracts = { c: web3.eth.contract(abi=CONTRACT_MANAGER.get_contract_abi(c), address=address) for c, address in addresses.items() } return web3, contracts, start_block
def withdraw( private_key: str, web3: Web3, contracts: Dict[str, Contract], start_block: BlockNumber, to: Optional[Address], ) -> None: log.info("Using RPC endpoint", rpc_url=get_web3_provider_info(web3)) service_registry_contract = contracts[CONTRACT_SERVICE_REGISTRY] caller_address = private_key_to_address(private_key) # Find deposit contract address caller_address = private_key_to_address(private_key) deposit_contract_address = find_withdrawable_deposit( web3=web3, service_address=caller_address, service_registry_contract=service_registry_contract, start_block=start_block, ) deposit_contract = web3.eth.contract( abi=CONTRACT_MANAGER.get_contract_abi(CONTRACT_DEPOSIT), address=deposit_contract_address) # Check usage of correct key withdrawer = deposit_contract.functions.withdrawer().call() if to_canonical_address(withdrawer) != caller_address: log.error( "You must use the key used to deposit when withdrawing", expected=withdrawer, actual=to_checksum_address(caller_address), ) sys.exit(1) # Can we withdraw already? release_at = deposit_contract.functions.release_at().call() deprecated = service_registry_contract.functions.deprecated().call() if web3.eth.getBlock( "latest")["timestamp"] < release_at and not deprecated: log.error( "Too early to withdraw", released_at_utc=datetime.utcfromtimestamp(release_at).isoformat(), ) sys.exit(1) receiver = to or private_key_to_address(private_key) checked_transact( web3=web3, sender_address=caller_address, function_call=deposit_contract.functions.withdraw(receiver), task_name="withdraw", wait_confirmation_interval=False, )
def deposit_to_registry( web3: Web3, service_registry_contract: Contract, user_deposit_contract: Contract, service_address: Address, ) -> None: log.info("Address not registered in ServiceRegistry") deposit_token_address = user_deposit_contract.functions.token().call() deposit_token_contract = web3.eth.contract( address=deposit_token_address, abi=CONTRACT_MANAGER.get_contract_abi(CONTRACT_CUSTOM_TOKEN) ) # Get required deposit required_deposit = service_registry_contract.functions.currentPrice().call() # Check current token balance account_balance = deposit_token_contract.functions.balanceOf(service_address).call() log.info("Current account balance", balance=account_balance, required_deposit=required_deposit) # mint tokens if necessary if account_balance < required_deposit: checked_transact( web3=web3, service_address=service_address, function_call=deposit_token_contract.functions.mint(required_deposit), task_name="Minting new Test RDN tokens", ) account_balance = deposit_token_contract.functions.balanceOf(service_address).call() log.info( "Updated account balance", balance=account_balance, desired_deposit=required_deposit ) # Approve token transfer checked_transact( web3=web3, service_address=service_address, function_call=deposit_token_contract.functions.approve( service_registry_contract.address, required_deposit ), task_name="Allowing token transfor for deposit", ) # Deposit tokens checked_transact( web3=web3, service_address=service_address, function_call=service_registry_contract.functions.deposit(required_deposit), task_name="Depositing to service registry", )
def create_event_topic_to_abi_dict() -> Dict[bytes, ABIEvent]: contract_names = [ CONTRACT_TOKEN_NETWORK_REGISTRY, CONTRACT_TOKEN_NETWORK, CONTRACT_MONITORING_SERVICE, ] event_abis = {} for contract_name in contract_names: events = filter_by_type( "event", CONTRACT_MANAGER.get_contract_abi(contract_name)) for event_abi in events: event_topic = event_abi_to_log_topic(event_abi) # type: ignore event_abis[event_topic] = event_abi return event_abis # type: ignore
def register_account( private_key: str, web3: Web3, contracts: Dict[str, Contract], start_block: BlockNumber, service_url: Optional[str], accept_disclaimer: bool, accept_all: bool, extend: bool = False, ) -> None: click.secho(DISCLAIMER, fg="yellow") if not accept_disclaimer and not accept_all: click.confirm(CONFIRMATION_OF_UNDERSTANDING, abort=True) def maybe_prompt(query: str) -> None: if not accept_all: click.confirm(query, abort=True) chain_id = web3.eth.chainId log.info("Using RPC endpoint", rpc_url=get_web3_provider_info(web3)) hex_addresses = { name: to_checksum_address(contract.address) for name, contract in contracts.items() } log.info("Contract information", addresses=hex_addresses, start_block=start_block) # Add middleware to sign transactions by default web3.middleware_onion.add(construct_sign_and_send_raw_middleware(private_key)) service_address = private_key_to_address(private_key) log.info("Running service registration script", account_address=service_address) click.secho( f"\nThis will run the registration with the address {to_checksum_address(service_address)}" f"\n\tSee {etherscan_url_for_address(chain_id, service_address)}" ) maybe_prompt("I have checked that the address is correct and want to continue") # Create contract proxies service_registry_contract = contracts[CONTRACT_SERVICE_REGISTRY] service_registry_address = to_canonical_address(service_registry_contract.address) deposit_token_address = service_registry_contract.functions.token().call() deposit_token_contract = web3.eth.contract( address=deposit_token_address, abi=CONTRACT_MANAGER.get_contract_abi(CONTRACT_CUSTOM_TOKEN), ) click.secho( "\nThe address of the service registry contract used is " f"{to_checksum_address(service_registry_contract.address)}" f"\n\tSee {etherscan_url_for_address(chain_id, service_registry_address)}" ) maybe_prompt("I have checked that the address is correct and want to continue") # Check current token balance fmt_amount = get_token_formatter(deposit_token_contract) account_balance = deposit_token_contract.functions.balanceOf(service_address).call() log.info("Current account balance", balance=account_balance) click.secho( "\nThe address of the token used is " f"{to_checksum_address(deposit_token_address)}" f"\n\tSee {etherscan_url_for_address(chain_id, deposit_token_address)}" f"\nThe account balance of that token is {fmt_amount(account_balance)}." ) maybe_prompt("I have checked that the address and my balance are correct and want to continue") # check if already registered currently_registered = service_registry_contract.functions.hasValidRegistration( service_address ).call() current_url = service_registry_contract.functions.urls(service_address).call() log.info( "Current ServiceRegistry information for service address", service_address=service_address, currently_registered=currently_registered, current_url=current_url, ) # Register if not yet done or extension is requested if extend and currently_registered: log.info("Registration found. Preparing to extend registration.") send_registration_transaction( web3=web3, service_registry_contract=service_registry_contract, deposit_token_contract=deposit_token_contract, maybe_prompt=maybe_prompt, account_balance=account_balance, service_address=service_address, fmt_amount=fmt_amount, ) elif not currently_registered: log.info("Address not registered in ServiceRegistry") send_registration_transaction( web3=web3, service_registry_contract=service_registry_contract, deposit_token_contract=deposit_token_contract, maybe_prompt=maybe_prompt, account_balance=account_balance, service_address=service_address, fmt_amount=fmt_amount, ) else: log.info( "Already registered. If you want to extend your registration, " "use the 'extend' command." ) if service_url and service_url != current_url: click.secho(f'\nNew Url to be registered "{service_url}"') hostname = service_url.split("//")[1] reachable = not subprocess.run( ["ping", "-c", "1", hostname], capture_output=True, check=False ).returncode if not reachable: click.secho(f"`ping {hostname}` fails. Are you sure the URL is correct?", fg="yellow") maybe_prompt("I have checked the URL and it is correct") checked_transact( web3=web3, sender_address=service_address, function_call=service_registry_contract.functions.setURL(service_url), task_name="Registering new URL", ) current_url = service_registry_contract.functions.urls(service_address).call() click.secho("\nThank you for registering your services!", fg="green") log.info("Updated infos", current_url=current_url)