def check_eta(self): """ Cast spells that meet their schedule. First, the local database is updated with spells that have been scheduled between the last block reviewed and the most recent block receieved. Next, it simply traverses through each spell address, checking if its schedule has been reached/passed. If it has, it attempts to `cast` the spell. """ blockNumber = self.web3.eth.blockNumber now = self.web3.eth.getBlock(blockNumber).timestamp self.logger.info(f'Checking scheduled spells on block {blockNumber}') self.database.update_db_etas(blockNumber) etas = self.database.db.get(doc_id=3)["upcoming_etas"] yays = list(etas.keys()) for yay in yays: if etas[yay] <= now: spell = DSSSpell(self.web3, Address(yay)) if spell.done() == False: receipt = spell.cast().transact(gas_price=self.gas_price()) if receipt.successful == True: del etas[yay] else: del etas[yay] self.database.db.update({'upcoming_etas': etas}, doc_ids=[3])
def check_schedule(self, spell: DSSSpell, yay: str): """ Schedules spells that haven't been scheduled nor casted """ if is_contract_at(self.web3, Address(yay)): # Functional with DSSSpells but not DSSpells (not compatiable with DSPause) if spell.done() == False and self.database.get_eta_inUnix( spell) == 0: self.logger.info(f'Scheduling spell ({yay})') spell.schedule().transact(gas_price=self.gas_price())
def check_hat(self): """ Ensures the Hat is on the proposal (spell, EOA, multisig, etc) with the most approval. First, the local database is updated with proposal addresses (yays) that have been `etched` in DSChief between the last block reviewed and the most recent block receieved. Next, it simply traverses through each address, checking if its approval has surpased the current Hat. If it has, it will `lift` the hat. If the current or new hat hasn't been casted nor plotted in the pause, it will `schedule` the spell """ blockNumber = self.web3.eth.blockNumber self.logger.info(f'Checking Hat on block {blockNumber}') self.database.update_db_yays(blockNumber) yays = self.database.db.get(doc_id=2)["yays"] hat = self.dss.ds_chief.get_hat().address hatApprovals = self.dss.ds_chief.get_approvals(hat) contender, highestApprovals = hat, hatApprovals for yay in yays: contenderApprovals = self.dss.ds_chief.get_approvals(yay) if contenderApprovals > highestApprovals: contender = yay highestApprovals = contenderApprovals if contender != hat: self.logger.info(f'Lifting hat') self.logger.info(f'Old hat ({hat}) with Approvals {hatApprovals}') self.logger.info( f'New hat ({contender}) with Approvals {highestApprovals}') self.dss.ds_chief.lift( Address(contender)).transact(gas_price=self.gas_price) else: self.logger.info( f'Current hat ({hat}) with Approvals {hatApprovals}') # Read the hat; either is equivalent to the contender or old hat hatNew = self.dss.ds_chief.get_hat().address if hatNew != hat: self.logger.info(f'Confirmed ({contender}) now has the hat') spell = DSSSpell(self.web3, Address(hatNew)) if is_contract_at( self.web3, Address(hatNew)) else None # Schedules spells that haven't been scheduled nor casted if spell is not None: # Functional with DSSSpells but not DSSpells (not compatiable with DSPause) if spell.done() == False and self.database.get_eta_inUnix( spell) == 0: self.logger.info(f'Scheduling spell ({yay})') spell.schedule().transact(gas_price=self.gas_price) else: self.logger.warning( f'Spell is an EOA or 0x0, so keeper will not attempt to call schedule()' )
def get_etas(self, yays, blockNumber: int): """ Get all upcoming etas """ etas = {} for yay in yays: #Check if yay is an address to an EOA or a contract if is_contract_at(self.web3, Address(yay)): spell = DSSSpell(self.web3, Address(yay)) eta = self.get_eta_inUnix(spell) if (eta > 0) and (spell.done() == False): etas[spell.address.address] = eta return etas