def _withdraw_preconditions( self, amount_to_withdraw: TokenAmount, given_block_identifier: BlockIdentifier ) -> None: try: withdraw_plan = self.get_withdraw_plan( withdrawer_address=self.node_address, block_identifier=given_block_identifier ) whole_balance = self.whole_balance(block_identifier=given_block_identifier) except ValueError: # If 'given_block_identifier' has been pruned, we can't perform the check return except BadFunctionCallOutput: raise_on_call_returned_empty(given_block_identifier) if amount_to_withdraw > withdraw_plan.withdraw_amount: raise BrokenPreconditionError( f"Can't withdraw {amount_to_withdraw}, " f"current withdraw plan only allows for {withdraw_plan.withdraw_amount}." ) given_block_number = self.client.get_block(given_block_identifier)["number"] if withdraw_plan.withdraw_block > given_block_number: raise BrokenPreconditionError( f"Can't withdraw at block {given_block_number}. " f"The current withdraw plan requires block number {withdraw_plan.withdraw_block}." ) if whole_balance - amount_to_withdraw < 0: raise BrokenPreconditionError( f"The current whole balance is {whole_balance}. " f"The withdraw of {amount_to_withdraw} would lead to an underflow." )
def _deposit_preconditions( self, beneficiary: Address, total_deposit: TokenAmount, given_block_identifier: BlockIdentifier, token: Token, ) -> Tuple[TokenAmount, TokenAmount]: try: previous_total_deposit = self.get_total_deposit( address=beneficiary, block_identifier=given_block_identifier ) current_balance = token.balance_of( address=self.node_address, block_identifier=given_block_identifier ) whole_balance = self.whole_balance(block_identifier=given_block_identifier) whole_balance_limit = self.whole_balance_limit(block_identifier=given_block_identifier) except ValueError: # If 'given_block_identifier' has been pruned, we cannot perform the # precondition checks but must still set the amount_to_deposit to a # reasonable value. previous_total_deposit = self.get_total_deposit( address=beneficiary, block_identifier=self.client.get_checking_block() ) amount_to_deposit = TokenAmount(total_deposit - previous_total_deposit) except BadFunctionCallOutput: raise_on_call_returned_empty(given_block_identifier) else: amount_to_deposit = TokenAmount(total_deposit - previous_total_deposit) if whole_balance + amount_to_deposit > UINT256_MAX: msg = ( f"Current whole balance is {whole_balance}. " f"The new deposit of {amount_to_deposit} would lead to an overflow." ) raise BrokenPreconditionError(msg) if whole_balance + amount_to_deposit > whole_balance_limit: msg = ( f"Current whole balance is {whole_balance}. " f"With the new deposit of {amount_to_deposit}, the deposit " f"limit of {whole_balance_limit} would be exceeded." ) raise BrokenPreconditionError(msg) if total_deposit <= previous_total_deposit: msg = ( f"Current total deposit {previous_total_deposit} is already larger " f"than the requested total deposit amount {total_deposit}" ) raise BrokenPreconditionError(msg) if current_balance < amount_to_deposit: msg = ( f"new_total_deposit - previous_total_deposit = {amount_to_deposit} " f"can not be larger than the available balance {current_balance}, " f"for token at address {to_checksum_address(token.address)}" ) raise BrokenPreconditionError(msg) return previous_total_deposit, amount_to_deposit
def init( self, monitoring_service_address: MonitoringServiceAddress, one_to_n_address: OneToNAddress, given_block_identifier: BlockSpecification, ) -> None: """ Initialize the UserDeposit contract with MS and OneToN addresses """ log_details = { "monitoring_service_address": to_checksum_address(monitoring_service_address), "one_to_n_address": to_checksum_address(one_to_n_address), } check_address_has_code( client=self.client, address=Address(monitoring_service_address), contract_name=CONTRACT_MONITORING_SERVICE, expected_code=decode_hex( self.contract_manager.get_runtime_hexcode( CONTRACT_MONITORING_SERVICE)), ) check_address_has_code( client=self.client, address=Address(one_to_n_address), contract_name=CONTRACT_ONE_TO_N, expected_code=decode_hex( self.contract_manager.get_runtime_hexcode(CONTRACT_ONE_TO_N)), ) try: existing_monitoring_service_address = self.monitoring_service_address( block_identifier=given_block_identifier) existing_one_to_n_address = self.one_to_n_address( block_identifier=given_block_identifier) except ValueError: pass except BadFunctionCallOutput: raise_on_call_returned_empty(given_block_identifier) else: if existing_monitoring_service_address != EMPTY_ADDRESS: msg = ( f"MonitoringService contract address is already set to " f"{to_checksum_address(existing_monitoring_service_address)}" ) raise BrokenPreconditionError(msg) if existing_one_to_n_address != EMPTY_ADDRESS: msg = (f"OneToN contract address is already set to " f"{to_checksum_address(existing_one_to_n_address)}") raise BrokenPreconditionError(msg) with log_transaction(log, "init", log_details): self._init( monitoring_service_address=monitoring_service_address, one_to_n_address=one_to_n_address, log_details=log_details, )
def _plan_withdraw_preconditions( self, amount_to_plan_withdraw: TokenAmount, given_block_identifier: BlockIdentifier ) -> None: """ Check if a WithdrawPlan for the given amount can be created. """ if amount_to_plan_withdraw <= 0: raise BrokenPreconditionError("Planned withdraw amount must be greater than zero.") try: current_balance = self.get_total_deposit( address=self.node_address, block_identifier=given_block_identifier ) except ValueError: # If `given_block_identifier` has been pruned we can't perform the check return except BadFunctionCallOutput: raise_on_call_returned_empty(given_block_identifier) if current_balance < amount_to_plan_withdraw: raise BrokenPreconditionError( f"Can't create WithdrawPlan for amount {amount_to_plan_withdraw}, " f"it exceeds the current balance of {current_balance}." )
def deposit( self, beneficiary: Address, total_deposit: TokenAmount, given_block_identifier: BlockSpecification, ) -> None: """ Deposit provided amount into the user-deposit contract to the beneficiary's account. """ token_address = self.token_address(given_block_identifier) token = self.proxy_manager.token(token_address=token_address) log_details = { "beneficiary": to_checksum_address(beneficiary), "contract": to_checksum_address(self.address), "node": to_checksum_address(self.node_address), "total_deposit": total_deposit, } # To prevent concurrent transactions for token transfers where it is unknown if # we have enough capacity for both, we acquire the lock # for the token proxy. Example: A user deposit and a channel deposit # for the same token. with self.deposit_lock, token.token_lock: # check preconditions try: previous_total_deposit = self.get_total_deposit( address=beneficiary, block_identifier=given_block_identifier) current_balance = token.balance_of( address=self.node_address, block_identifier=given_block_identifier) whole_balance = self.whole_balance( block_identifier=given_block_identifier) whole_balance_limit = self.whole_balance_limit( block_identifier=given_block_identifier) except ValueError: # If 'given_block_identifier' has been pruned, we cannot perform the # precondition checks but must still set the amount_to_deposit to a # reasonable value. previous_total_deposit = self.get_total_deposit( address=beneficiary, block_identifier=self.client.get_checking_block()) amount_to_deposit = TokenAmount(total_deposit - previous_total_deposit) except BadFunctionCallOutput: raise_on_call_returned_empty(given_block_identifier) else: log_details["previous_total_deposit"] = previous_total_deposit amount_to_deposit = TokenAmount(total_deposit - previous_total_deposit) if whole_balance + amount_to_deposit > UINT256_MAX: msg = ( f"Current whole balance is {whole_balance}. " f"The new deposit of {amount_to_deposit} would lead to an overflow." ) raise BrokenPreconditionError(msg) if whole_balance + amount_to_deposit > whole_balance_limit: msg = ( f"Current whole balance is {whole_balance}. " f"With the new deposit of {amount_to_deposit}, the deposit " f"limit of {whole_balance_limit} would be exceeded.") raise BrokenPreconditionError(msg) if total_deposit <= previous_total_deposit: msg = ( f"Current total deposit {previous_total_deposit} is already larger " f"than the requested total deposit amount {total_deposit}" ) raise BrokenPreconditionError(msg) if current_balance < amount_to_deposit: msg = ( f"new_total_deposit - previous_total_deposit = {amount_to_deposit} " f"can not be larger than the available balance {current_balance}, " f"for token at address {to_checksum_address(token.address)}" ) raise BrokenPreconditionError(msg) with log_transaction(log, "deposit", log_details): self._deposit( beneficiary=beneficiary, token=token, total_deposit=total_deposit, amount_to_deposit=amount_to_deposit, log_details=log_details, )
def add_token( self, token_address: TokenAddress, channel_participant_deposit_limit: TokenAmount, token_network_deposit_limit: TokenAmount, given_block_identifier: BlockIdentifier, ) -> Tuple[TransactionHash, TokenNetworkAddress]: """ Register token of `token_address` with the token network. The limits apply for version 0.13.0 and above of raiden-contracts, since instantiation also takes the limits as constructor arguments. """ if given_block_identifier == BLOCK_ID_LATEST: raise ValueError( 'Calling a proxy with "latest" is usually wrong because ' "the result of the precondition check is not precisely predictable." ) if token_address == NULL_ADDRESS_BYTES: raise InvalidTokenAddress( "The call to register a token at 0x00..00 will fail.") if token_network_deposit_limit <= 0: raise InvalidTokenNetworkDepositLimit( f"Token network deposit limit must be larger than zero, " f"{token_network_deposit_limit} given.") if channel_participant_deposit_limit <= 0: raise InvalidTokenNetworkDepositLimit( f"Participant deposit limit must be larger than zero, " f"{channel_participant_deposit_limit} given") if channel_participant_deposit_limit > token_network_deposit_limit: raise InvalidChannelParticipantDepositLimit( f"Participant deposit limit must be smaller than the network " f"deposit limit, {channel_participant_deposit_limit} is larger " f"than {token_network_deposit_limit}.") token_proxy = self.proxy_manager.token(token_address, given_block_identifier) try: token_supply = token_proxy.total_supply( block_identifier=given_block_identifier) already_registered = self.get_token_network( token_address=token_address, block_identifier=given_block_identifier) deprecation_executor = self.get_deprecation_executor( block_identifier=given_block_identifier) settlement_timeout_min = self.settlement_timeout_min( block_identifier=given_block_identifier) settlement_timeout_max = self.settlement_timeout_max( block_identifier=given_block_identifier) chain_id = self.get_chain_id( block_identifier=given_block_identifier) secret_registry_address = self.get_secret_registry_address( block_identifier=given_block_identifier) max_token_networks = self.get_max_token_networks( block_identifier=given_block_identifier) token_networks_created = self.get_token_network_created( block_identifier=given_block_identifier) except ValueError: # If `given_block_identifier` has been pruned the checks cannot be performed pass except BadFunctionCallOutput: raise_on_call_returned_empty(given_block_identifier) else: if token_networks_created >= max_token_networks: raise MaxTokenNetworkNumberReached( f"Number of token networks will exceed the maximum of {max_token_networks}" ) if token_supply is None: raise InvalidToken("Given token address does not follow the " "ERC20 standard (missing `totalSupply()`)") if already_registered: raise BrokenPreconditionError( "The token is already registered in the TokenNetworkRegistry." ) if deprecation_executor == NULL_ADDRESS_BYTES: raise BrokenPreconditionError( "The deprecation executor property for the TokenNetworkRegistry is invalid." ) if chain_id == 0: raise BrokenPreconditionError( "The chain ID property for the TokenNetworkRegistry is invalid." ) if chain_id != self.rpc_client.chain_id: raise BrokenPreconditionError( f"The provided chain ID {chain_id} does not match the " f"network Raiden is running on: {self.rpc_client.chain_id}." ) if secret_registry_address == NULL_ADDRESS_BYTES: raise BrokenPreconditionError( "The secret registry address for the token network is invalid." ) if settlement_timeout_min == 0: raise BrokenPreconditionError( "The minimum settlement timeout for the token network " "should be larger than zero.") if settlement_timeout_max <= settlement_timeout_min: raise BrokenPreconditionError( "The maximum settlement timeout for the token network " "should be larger than the minimum settlement timeout.") log_details = { "given_block_identifier": format_block_id(given_block_identifier) } return self._add_token( token_address=token_address, channel_participant_deposit_limit=channel_participant_deposit_limit, token_network_deposit_limit=token_network_deposit_limit, log_details=log_details, )
def add_token( self, token_address: TokenAddress, channel_participant_deposit_limit: TokenAmount, token_network_deposit_limit: TokenAmount, block_identifier: BlockSpecification, ) -> TokenNetworkAddress: """ Register token of `token_address` with the token network. The limits apply for version 0.13.0 and above of raiden-contracts, since instantiation also takes the limits as constructor arguments. """ if block_identifier == "latest": raise ValueError( 'Calling a proxy with "latest" is usually wrong because ' "the result of the precondition check is not precisely predictable." ) if token_address == NULL_ADDRESS_BYTES: raise InvalidTokenAddress( "The call to register a token at 0x00..00 will fail.") if token_network_deposit_limit <= 0: raise InvalidTokenNetworkDepositLimit( f"Token network deposit limit of {token_network_deposit_limit} is invalid" ) if channel_participant_deposit_limit > token_network_deposit_limit: raise InvalidChannelParticipantDepositLimit( f"Channel participant deposit limit of " f"{channel_participant_deposit_limit} is invalid") token_proxy = self.proxy_manager.token(token_address) try: token_supply = token_proxy.total_supply( block_identifier=block_identifier) already_registered = self.get_token_network( token_address=token_address, block_identifier=block_identifier) deprecation_executor = self.get_deprecation_executor( block_identifier=block_identifier) settlement_timeout_min = self.settlement_timeout_min( block_identifier=block_identifier) settlement_timeout_max = self.settlement_timeout_max( block_identifier=block_identifier) chain_id = self.get_chain_id(block_identifier=block_identifier) secret_registry_address = self.get_secret_registry_address( block_identifier=block_identifier) max_token_networks = self.get_max_token_networks( block_identifier=block_identifier) token_networks_created = self.get_token_network_created( block_identifier=block_identifier) except ValueError: # If `block_identifier` has been pruned the checks cannot be performed pass except BadFunctionCallOutput: raise_on_call_returned_empty(block_identifier) else: if token_networks_created >= max_token_networks: raise BrokenPreconditionError( f"Number of token networks will exceed the max of {max_token_networks}" ) if token_supply == "": raise InvalidToken("Given token address does not follow the " "ERC20 standard (missing `totalSupply()`)") if already_registered: raise BrokenPreconditionError( "The token is already registered in the TokenNetworkRegistry." ) if deprecation_executor == NULL_ADDRESS_BYTES: raise BrokenPreconditionError( "The deprecation executor property for the TokenNetworkRegistry is invalid." ) if chain_id == 0: raise BrokenPreconditionError( "The chain ID property for the TokenNetworkRegistry is invalid." ) if secret_registry_address == NULL_ADDRESS_BYTES: raise BrokenPreconditionError( "The secret registry address for the token network is invalid." ) if settlement_timeout_min == 0: raise BrokenPreconditionError( "The minimum settlement timeout for the token network " "should be larger than zero.") if settlement_timeout_min == 0: raise BrokenPreconditionError( "The minimum settlement timeout for the token network " "should be larger than zero.") if settlement_timeout_max <= settlement_timeout_min: raise BrokenPreconditionError( "The maximum settlement timeout for the token network " "should be larger than the minimum settlement timeout.") log_details = { "node": to_checksum_address(self.node_address), "contract": to_checksum_address(self.address), "token_address": to_checksum_address(token_address), } with log_transaction(log, "add_token", log_details): return self._add_token( token_address=token_address, channel_participant_deposit_limit= channel_participant_deposit_limit, token_network_deposit_limit=token_network_deposit_limit, log_details=log_details, )