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())
Exemple #3
0
    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()'
            )
Exemple #4
0
    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
    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())
            spell = DSSSpell(self.web3, Address(contender))
        elif hat != "0x0000000000000000000000000000000000000000":
            self.logger.info(
                f'Current hat ({hat}) with Approvals {hatApprovals}')
            spell = DSSSpell(self.web3, Address(hat))
        else:
            return True
        self.check_schedule(spell, yay)

        return True
    def test_check_eta(self, mcd: DssDeployment, keeper: ChiefKeeper):
        print_out("test_check_eta")

        keeper.initial_query()

        etas = keeper.database.db.get(doc_id=3)['upcoming_etas']
        hat = mcd.ds_chief.get_hat()
        verify([hat.address], etas, 1)

        keeper.check_eta()

        # Confirm that the spell was casted and that the database was updated
        assert DSSSpell(mcd.web3, Address(hat)).done() == True
        etas = keeper.database.db.get(doc_id=3)['upcoming_etas']
        verify([], etas, 0)
Exemple #7
0
    def test_setup(self, mcd: DssDeployment, our_address: Address,
                   guy_address: Address):
        print_out("test_setup")

        # Give 1000 MKR to our_address
        amount = Wad.from_number(1000)
        mint_mkr(mcd.mkr, our_address, amount)
        assert mcd.mkr.balance_of(our_address) == amount

        #Give 2000 MKR to guy_address
        guyAmount = Wad.from_number(2000)
        mint_mkr(mcd.mkr, guy_address, guyAmount)
        assert mcd.mkr.balance_of(guy_address) == guyAmount

        # Lock MKR in DS-Chief
        assert mcd.mkr.approve(
            mcd.ds_chief.address).transact(from_address=our_address)
        assert mcd.mkr.approve(
            mcd.ds_chief.address).transact(from_address=guy_address)
        assert mcd.ds_chief.lock(amount).transact(from_address=our_address)
        assert mcd.ds_chief.lock(guyAmount).transact(from_address=guy_address)

        # Deploy spell
        self.spell = DSSSpell.deploy(mcd.web3, mcd.pause.address,
                                     mcd.vat.address)

        # Vote 1000 mkr on our address and guy_address
        # Vote 2000 mkr on global spell address
        assert mcd.ds_chief.vote_yays(
            [our_address.address,
             guy_address.address]).transact(from_address=our_address)
        assert mcd.ds_chief.vote_yays([self.spell.address.address
                                       ]).transact(from_address=guy_address)

        # At this point there are two yays in the chief, one to our_address and the other to the spell address

        pytest.global_spell = self.spell
    def test_check_hat(self, mcd: DssDeployment, keeper: ChiefKeeper,
                       guy_address: Address):
        print_out("test_check_hat")

        # Confirm the hat with the most approval is unchanged
        oldHat = mcd.ds_chief.get_hat()
        keeper.check_hat()
        newHat = mcd.ds_chief.get_hat()
        assert oldHat.address == newHat.address

        # Move the 2000 MKR vote from the last spell in test_database.py to new spell
        self.spell = DSSSpell.deploy(mcd.web3, mcd.pause.address,
                                     mcd.vat.address)
        assert mcd.ds_chief.vote_yays([self.spell.address.address
                                       ]).transact(from_address=guy_address)

        keeper.check_hat()

        # Confirm that the hat has been lifted
        newerHat = mcd.ds_chief.get_hat()
        assert newerHat.address == self.spell.address.address

        # Confirm that the spell was scheduled
        assert self.spell.eta() != 0
Exemple #9
0
    def get_eta_inUnix(self, spell: DSSSpell) -> int:
        eta = spell.eta()
        etaInUnix = eta.replace(tzinfo=timezone.utc).timestamp()

        return etaInUnix