diff --git a/nucypher/blockchain/eth/actors.py b/nucypher/blockchain/eth/actors.py index 83cc071bd..6474a5db0 100644 --- a/nucypher/blockchain/eth/actors.py +++ b/nucypher/blockchain/eth/actors.py @@ -32,7 +32,8 @@ from constant_sorrow.constants import ( NO_FUNDING_ACCOUNT ) from eth_tester.exceptions import TransactionFailed -from eth_utils import keccak, is_checksum_address +from eth_utils import is_checksum_address +from eth_utils import keccak from twisted.logger import Logger from web3 import Web3 @@ -724,9 +725,21 @@ class StakeHolder(BaseConfiguration): self.__get_accounts() - if sync_now: + if sync_now and not offline_mode: self.read_onchain_stakes() # Stakes + def __str__(self) -> str: + r = f'{self.__class__.__name__}(funding_account={self.funding_account})' + return r + + def __repr__(self) -> str: + r = f'{self.__class__.__name__}(funding_account={self.funding_account})' + return r + + # + # Configuration + # + def static_payload(self) -> dict: """Values to read/write from stakeholder JSON configuration files""" payload = dict(provider_uri=self.blockchain.provider_uri, @@ -757,6 +770,18 @@ class StakeHolder(BaseConfiguration): self.__transacting_powers[checksum_address] = transacting_power transacting_power.activate(password=password) + def to_configuration_file(self, *args, **kwargs) -> str: + filepath = super().to_configuration_file(modifier=self.funding_account, *args, **kwargs) + return filepath + + def connect(self, blockchain: BlockchainInterface = None) -> None: + """Go Online""" + if not self.staking_agent: + self.staking_agent = StakingEscrowAgent(blockchain=blockchain) + if not self.token_agent: + self.token_agent = NucypherTokenAgent(blockchain=blockchain) + self.blockchain = self.token_agent.blockchain + # # Account Utilities # @@ -916,7 +941,9 @@ class StakeHolder(BaseConfiguration): # Send new staker account NU _result = self.token_agent.transfer(amount=amount.to_nunits(), sender_address=self.funding_account, - target_address=staker.checksum_address) + target_address=staker.checksum_address, + auto_approve=True) + return staker def initialize_stake(self, amount: NU, @@ -967,6 +994,13 @@ class StakeHolder(BaseConfiguration): self.to_configuration_file(override=True) return receipts + def calculate_rewards(self) -> dict: + rewards = dict() + for staker in self.stakers: + reward = staker.calculate_reward() + rewards[staker.checksum_address] = reward + return rewards + def collect_rewards(self, staker_address: str, password: str, diff --git a/nucypher/blockchain/eth/agents.py b/nucypher/blockchain/eth/agents.py index 797d80656..5de84bc58 100644 --- a/nucypher/blockchain/eth/agents.py +++ b/nucypher/blockchain/eth/agents.py @@ -124,6 +124,12 @@ class NucypherTokenAgent(EthereumContractAgent, metaclass=Agency): address = address if address is not None else self.contract_address return self.contract.functions.balanceOf(address).call() + def increase_allowance(self, sender_address: str, target_address: str, increase: int): + contract_function = self.contract.functions.increaseAllowance(target_address, increase) + receipt = self.blockchain.send_transaction(transaction_function=contract_function, + sender_address=sender_address) + return receipt + def approve_transfer(self, amount: int, target_address: str, sender_address: str): """Approve the transfer of token from the sender address to the target address.""" payload = {'gas': 500_000} # TODO #413: gas needed for use with geth. @@ -133,8 +139,16 @@ class NucypherTokenAgent(EthereumContractAgent, metaclass=Agency): sender_address=sender_address) return receipt - def transfer(self, amount: int, target_address: str, sender_address: str): - self.approve_transfer(amount=amount, target_address=target_address, sender_address=sender_address) + def transfer(self, amount: int, target_address: str, sender_address: str, auto_approve: bool = True): + if auto_approve: + allowance = self.contract.functions.allowance(sender_address, target_address).call() + if allowance != 0: + delta = int(amount) - int(allowance) + self.increase_allowance(sender_address=sender_address, + target_address=target_address, + increase=delta) + else: + self.approve_transfer(amount=amount, target_address=target_address, sender_address=sender_address) contract_function = self.contract.functions.transfer(target_address, amount) receipt = self.blockchain.send_transaction(contract_function=contract_function, sender_address=sender_address) return receipt