Beispiel #1
0
    def __init__(self, web3: Web3, address: Address):
        assert isinstance(web3, Web3)
        assert isinstance(address, Address)

        self.web3 = web3
        self.address = address
        self._contract = self._get_contract(web3, self.abi, address)
        self.vat = Vat(self.web3,
                       Address(self._contract.functions.vat().call()))
Beispiel #2
0
 def from_json(web3: Web3, conf: str):
     conf = json.loads(conf)
     mom = DSGuard(web3, Address(conf['MCD_MOM']))
     vat = Vat(web3, Address(conf['MCD_VAT']))
     vow = Vow(web3, Address(conf['MCD_VOW']))
     drip = Drip(web3, Address(conf['MCD_DRIP']))
     pit = Pit(web3, Address(conf['MCD_PIT']))
     cat = Cat(web3, Address(conf['MCD_CAT']))
     flap = Flapper(web3, Address(conf['MCD_FLAP']))
     flop = Flopper(web3, Address(conf['MCD_FLOP']))
     dai = DSToken(web3, Address(conf['MCD_DAI']))
     dai_adapter = DaiAdapter(web3, Address(conf['MCD_JOIN_DAI']))
     dai_move = DaiVat(web3, Address(conf['MCD_MOVE_DAI']))
     mkr = DSToken(web3, Address(conf['MCD_GOV']))
     collaterals = []
     for name in conf['COLLATERALS']:
         collateral = Collateral(Ilk(name))
         collateral.gem = DSToken(web3, Address(conf[name]))
         collateral.adapter = GemAdapter(web3, Address(conf[f'MCD_JOIN_{name}']))
         collateral.mover = GemVat(web3, Address(conf[f'MCD_MOVE_{name}']))
         collateral.flipper = Flipper(web3, Address(conf[f'MCD_FLIP_{name}']))
         collateral.pip = DSValue(web3, Address(conf[f'PIP_{name}']))
         collateral.spotter = Spotter(web3, Address(conf[f'MCD_SPOT_{name}']))
         collaterals.append(collateral)
     return DssDeployment.Config(mom, vat, vow, drip, pit, cat, flap, flop, dai, dai_adapter, dai_move, mkr,
                                 collaterals)
Beispiel #3
0
        def from_json(web3: Web3, conf: str):
            conf = json.loads(conf)
            pause = DSPause(web3, Address(conf['MCD_PAUSE']))
            vat = Vat(web3, Address(conf['MCD_VAT']))
            vow = Vow(web3, Address(conf['MCD_VOW']))
            jug = Jug(web3, Address(conf['MCD_JUG']))
            cat = Cat(web3, Address(conf['MCD_CAT']))
            dai = DSToken(web3, Address(conf['MCD_DAI']))
            dai_adapter = DaiJoin(web3, Address(conf['MCD_JOIN_DAI']))
            flapper = Flapper(web3, Address(conf['MCD_FLAP']))
            flopper = Flopper(web3, Address(conf['MCD_FLOP']))
            mkr = DSToken(web3, Address(conf['MCD_GOV']))
            spotter = Spotter(web3, Address(conf['MCD_SPOT']))

            collaterals = {}
            for name in DssDeployment.Config._infer_collaterals_from_addresses(conf.keys()):
                ilk = Ilk(name[0].replace('_', '-'))
                if name[1] == "ETH":
                    gem = DSEthToken(web3, Address(conf[name[1]]))
                else:
                    gem = DSToken(web3, Address(conf[name[1]]))

                # TODO: If problematic, skip pip for deployments which use a medianizer.
                collateral = Collateral(ilk=ilk, gem=gem,
                                        adapter=GemJoin(web3, Address(conf[f'MCD_JOIN_{name[0]}'])),
                                        flipper=Flipper(web3, Address(conf[f'MCD_FLIP_{name[0]}'])),
                                        pip=DSValue(web3, Address(conf[f'PIP_{name[1]}'])))
                collaterals[ilk.name] = collateral

            return DssDeployment.Config(pause, vat, vow, jug, cat, flapper, flopper,
                                        dai, dai_adapter, mkr, spotter, collaterals)
Beispiel #4
0
        def from_json(web3: Web3, conf: str):
            conf = json.loads(conf)
            pause = DSPause(web3, Address(conf['MCD_PAUSE']))
            vat = Vat(web3, Address(conf['MCD_VAT']))
            vow = Vow(web3, Address(conf['MCD_VOW']))
            jug = Jug(web3, Address(conf['MCD_JUG']))
            cat = Cat(web3, Address(conf['MCD_CAT']))
            dai = DSToken(web3, Address(conf['MCD_DAI']))
            dai_adapter = DaiJoin(web3, Address(conf['MCD_JOIN_DAI']))
            flapper = Flapper(web3, Address(conf['MCD_FLAP']))
            flopper = Flopper(web3, Address(conf['MCD_FLOP']))
            pot = Pot(web3, Address(conf['MCD_POT']))
            mkr = DSToken(web3, Address(conf['MCD_GOV']))
            spotter = Spotter(web3, Address(conf['MCD_SPOT']))
            ds_chief = DSChief(web3, Address(conf['MCD_ADM']))
            esm = ShutdownModule(web3, Address(conf['MCD_ESM']))
            end = End(web3, Address(conf['MCD_END']))
            proxy_registry = ProxyRegistry(web3,
                                           Address(conf['PROXY_REGISTRY']))
            dss_proxy_actions = DssProxyActionsDsr(
                web3, Address(conf['PROXY_ACTIONS_DSR']))

            collaterals = {}
            for name in DssDeployment.Config._infer_collaterals_from_addresses(
                    conf.keys()):
                ilk = Ilk(name[0].replace('_', '-'))
                if name[1] == "ETH":
                    gem = DSEthToken(web3, Address(conf[name[1]]))
                else:
                    gem = DSToken(web3, Address(conf[name[1]]))

                # PIP contract may be a DSValue, OSM, or bogus address.
                pip_address = Address(conf[f'PIP_{name[1]}'])
                network = DssDeployment.NETWORKS.get(web3.net.version,
                                                     "testnet")
                if network == "testnet":
                    pip = DSValue(web3, pip_address)
                else:
                    pip = OSM(web3, pip_address)

                collateral = Collateral(
                    ilk=ilk,
                    gem=gem,
                    adapter=GemJoin(web3,
                                    Address(conf[f'MCD_JOIN_{name[0]}'])),
                    flipper=Flipper(web3,
                                    Address(conf[f'MCD_FLIP_{name[0]}'])),
                    pip=pip)
                collaterals[ilk.name] = collateral

            return DssDeployment.Config(pause, vat, vow, jug, cat, flapper,
                                        flopper, pot, dai, dai_adapter, mkr,
                                        spotter, ds_chief, esm, end,
                                        proxy_registry, dss_proxy_actions,
                                        collaterals)
Beispiel #5
0
    def __init__(self, web3: Web3, address: Address):
        super(Clipper, self).__init__(web3, address, Clipper.abi)
        assert isinstance(web3, Web3)
        assert isinstance(address, Address)

        self.web3 = web3
        self.address = address
        self._contract = self._get_contract(web3, self.abi, address)
        # Albeit more elegant, this is inconsistent with AuctionContract.vat(), a method call
        self.calc = Address(self._contract.functions.calc().call())
        self.dog = Dog(web3, Address(self._contract.functions.dog().call()))
        self.vat = Vat(web3, Address(self._contract.functions.vat().call()))

        self.take_abi = None
        self.redo_abi = None
        for member in self.abi:
            if not self.take_abi and member.get('name') == 'Take':
                self.take_abi = member
            if not self.redo_abi and member.get('name') == 'Redo':
                self.redo_abi = member
Beispiel #6
0
        def from_json(web3: Web3, conf: str):
            def address_in_configs(key: str, conf: str) -> bool:
                if key not in conf:
                    return False
                elif not conf[key]:
                    return False
                elif conf[key] == "0x0000000000000000000000000000000000000000":
                    return False
                else:
                    return True

            conf = json.loads(conf)
            pause = DSPause(web3, Address(conf['MCD_PAUSE']))
            vat = Vat(web3, Address(conf['MCD_VAT']))
            vow = Vow(web3, Address(conf['MCD_VOW']))
            jug = Jug(web3, Address(conf['MCD_JUG']))
            cat = Cat(web3, Address(conf['MCD_CAT'])) if address_in_configs(
                'MCD_CAT', conf) else None
            dog = Dog(web3, Address(conf['MCD_DOG'])) if address_in_configs(
                'MCD_DOG', conf) else None
            dai = DSToken(web3, Address(conf['MCD_DAI']))
            dai_adapter = DaiJoin(web3, Address(conf['MCD_JOIN_DAI']))
            flapper = Flapper(web3, Address(conf['MCD_FLAP']))
            flopper = Flopper(web3, Address(conf['MCD_FLOP']))
            pot = Pot(web3, Address(conf['MCD_POT']))
            mkr = DSToken(web3, Address(conf['MCD_GOV']))
            spotter = Spotter(web3, Address(conf['MCD_SPOT']))
            ds_chief = DSChief(web3, Address(conf['MCD_ADM']))
            esm = ShutdownModule(web3, Address(conf['MCD_ESM']))
            end = End(web3, Address(conf['MCD_END']))
            proxy_registry = ProxyRegistry(web3,
                                           Address(conf['PROXY_REGISTRY']))
            dss_proxy_actions = DssProxyActionsDsr(
                web3, Address(conf['PROXY_ACTIONS_DSR']))
            cdp_manager = CdpManager(web3, Address(conf['CDP_MANAGER']))
            dsr_manager = DsrManager(web3, Address(conf['DSR_MANAGER']))
            faucet = TokenFaucet(
                web3, Address(conf['FAUCET'])) if address_in_configs(
                    'FAUCET', conf) else None

            collaterals = {}
            for name in DssDeployment.Config._infer_collaterals_from_addresses(
                    conf.keys()):
                ilk = vat.ilk(name[0].replace('_', '-'))
                if name[1] == "ETH":
                    gem = DSEthToken(web3, Address(conf[name[1]]))
                else:
                    gem = DSToken(web3, Address(conf[name[1]]))

                if name[1] in [
                        'USDC', 'WBTC', 'TUSD', 'USDT', 'GUSD', 'RENBTC'
                ]:
                    adapter = GemJoin5(web3,
                                       Address(conf[f'MCD_JOIN_{name[0]}']))
                else:
                    adapter = GemJoin(web3,
                                      Address(conf[f'MCD_JOIN_{name[0]}']))

                # PIP contract may be a DSValue, OSM, or bogus address.
                pip_name = f'PIP_{name[1]}'
                pip_address = Address(
                    conf[pip_name]
                ) if pip_name in conf and conf[pip_name] else None
                val_name = f'VAL_{name[1]}'
                val_address = Address(
                    conf[val_name]
                ) if val_name in conf and conf[val_name] else None
                if pip_address:  # Configure OSM as price source
                    if name[1].startswith('UNIV2'):
                        pip = Univ2LpOSM(web3, pip_address)
                    else:
                        pip = OSM(web3, pip_address)
                elif val_address:  # Configure price using DSValue
                    pip = DSValue(web3, val_address)
                else:
                    pip = None

                auction = None
                if f'MCD_FLIP_{name[0]}' in conf:
                    auction = Flipper(web3,
                                      Address(conf[f'MCD_FLIP_{name[0]}']))
                elif f'MCD_CLIP_{name[0]}' in conf:
                    auction = Clipper(web3,
                                      Address(conf[f'MCD_CLIP_{name[0]}']))

                collateral = Collateral(ilk=ilk,
                                        gem=gem,
                                        adapter=adapter,
                                        auction=auction,
                                        pip=pip,
                                        vat=vat)
                collaterals[ilk.name] = collateral

            return DssDeployment.Config(pause, vat, vow, jug, cat, dog,
                                        flapper, flopper, pot, dai,
                                        dai_adapter, mkr, spotter, ds_chief,
                                        esm, end, proxy_registry,
                                        dss_proxy_actions, cdp_manager,
                                        dsr_manager, faucet, collaterals)
Beispiel #7
0
class Clipper(AuctionContract):
    """A client for the `Clipper` contract, used to interact with collateral auctions.

    You can find the source code of the `Clipper` contract here:
    <https://github.com/makerdao/dss/blob/master/src/clip.sol>.

    Attributes:
        web3: An instance of `Web` from `web3.py`.
        address: Ethereum address of the `Clipper` contract.
    """

    abi = Contract._load_abi(__name__, 'abi/Clipper.abi')
    bin = Contract._load_bin(__name__, 'abi/Clipper.bin')

    class KickLog:
        def __init__(self, log):
            args = log['args']
            self.id = args['id']
            self.top = Ray(args['top'])         # starting price
            self.tab = Rad(args['tab'])         # debt
            self.lot = Wad(args['lot'])         # collateral
            self.usr = Address(args['usr'])     # liquidated vault
            self.kpr = Address(args['kpr'])     # keeper who barked
            self.coin = Rad(args['coin'])       # total kick incentive (tip + tab*chip)
            self.block = log['blockNumber']
            self.tx_hash = log['transactionHash'].hex()

        def __repr__(self):
            return f"Clipper.KickLog({pformat(vars(self))})"

    class TakeLog:
        def __init__(self, log, sender):
            args = log['args']
            self.id = args['id']
            self.max = Ray(args['max'])         # Max bid price specified
            self.price = Ray(args['price'])     # Calculated bid price
            self.owe = Rad(args['owe'])         # Dai needed to satisfy the calculated bid price
            self.tab = Rad(args['tab'])         # Remaining debt
            self.lot = Wad(args['lot'])         # Remaining lot
            self.usr = Address(args['usr'])     # Liquidated vault
            self.block = log['blockNumber']
            self.tx_hash = log['transactionHash'].hex()
            self.sender = sender

        def __repr__(self):
            return f"Clipper.TakeLog({pformat(vars(self))})"

    class RedoLog(KickLog):
        # Same fields as KickLog
        def __repr__(self):
            return f"Clipper.RedoLog({pformat(vars(self))})"

    class Sale:
        def __init__(self, id: int, pos: int, tab: Rad, lot: Wad, usr: Address, tic: int, top: Ray):
            assert(isinstance(id, int))
            assert(isinstance(pos, int))
            assert(isinstance(tab, Rad))
            assert(isinstance(lot, Wad))
            assert(isinstance(usr, Address))
            assert(isinstance(tic, int))
            assert(isinstance(top, Ray))

            self.id = id    # auction identifier
            self.pos = pos  # active index
            self.tab = tab  # dai to raise
            self.lot = lot  # collateral to sell
            self.usr = usr  # liquidated urn address
            self.tic = tic  # auction start time
            self.top = top  # starting price

        def __repr__(self):
            return f"Clipper.Sale({pformat(vars(self))})"

    def __init__(self, web3: Web3, address: Address):
        super(Clipper, self).__init__(web3, address, Clipper.abi)
        assert isinstance(web3, Web3)
        assert isinstance(address, Address)

        self.web3 = web3
        self.address = address
        self._contract = self._get_contract(web3, self.abi, address)
        # Albeit more elegant, this is inconsistent with AuctionContract.vat(), a method call
        self.calc = Address(self._contract.functions.calc().call())
        self.dog = Dog(web3, Address(self._contract.functions.dog().call()))
        self.vat = Vat(web3, Address(self._contract.functions.vat().call()))

        self.take_abi = None
        self.redo_abi = None
        for member in self.abi:
            if not self.take_abi and member.get('name') == 'Take':
                self.take_abi = member
            if not self.redo_abi and member.get('name') == 'Redo':
                self.redo_abi = member

    def active_auctions(self) -> list:
        active_auctions = []
        for index in range(1, self.kicks()+1):
            sale = self.sales(index)
            if sale.usr != Address.zero():
                active_auctions.append(sale)
            index += 1
        return active_auctions

    def ilk_name(self) -> str:
        ilk = self._contract.functions.ilk().call()
        return Web3.toText(ilk.strip(bytes(1)))

    def buf(self) -> Ray:
        """Multiplicative factor to increase starting price"""
        return Ray(self._contract.functions.buf().call())

    def tail(self) -> int:
        """Time elapsed before auction reset"""
        return int(self._contract.functions.tail().call())

    def cusp(self) -> Ray:
        """Percentage drop before auction reset"""
        return Ray(self._contract.functions.cusp().call())

    def chip(self) -> Wad:
        """Percentage of tab to suck from vow to incentivize keepers"""
        return Wad(self._contract.functions.chip().call())

    def tip(self) -> Rad:
        """Flat fee to suck from vow to incentivize keepers"""
        return Rad(self._contract.functions.tip().call())

    def chost(self) -> Rad:
        """Ilk dust times the ilk chop"""
        return Rad(self._contract.functions.chost().call())

    def kicks(self) -> int:
        """Number of auctions started so far."""
        return int(self._contract.functions.kicks().call())

    def active_count(self) -> int:
        """Number of active and redoable auctions."""
        return int(self._contract.functions.count().call())

    def status(self, id: int) -> (bool, Ray, Wad, Rad):
        """Indicates current state of the auction
        Args:
            id: Auction identifier.
        """
        assert isinstance(id, int)
        (needs_redo, price, lot, tab) = self._contract.functions.getStatus(id).call()
        logging.debug(f"Auction {id} {'needs redo ' if needs_redo else ''}with price={float(Ray(price))} " 
                      f"lot={float(Wad(lot))} tab={float(Rad(tab))}")
        return needs_redo, Ray(price), Wad(lot), Rad(tab)

    def sales(self, id: int) -> Sale:
        """Returns the auction details.
        Args:
            id: Auction identifier.
        Returns:
            The auction details.
        """
        assert(isinstance(id, int))

        array = self._contract.functions.sales(id).call()

        return Clipper.Sale(id=id,
                            pos=int(array[0]),
                            tab=Rad(array[1]),
                            lot=Wad(array[2]),
                            usr=Address(array[3]),
                            tic=int(array[4]),
                            top=Ray(array[5]))

    def validate_take(self, id: int, amt: Wad, max: Ray, our_address: Address = None):
        """Raise assertion if collateral cannot be purchased from an auction as desired"""
        assert isinstance(id, int)
        assert isinstance(amt, Wad)
        assert isinstance(max, Ray)

        if our_address:
            assert isinstance(our_address, Address)
        else:
            our_address = Address(self.web3.eth.defaultAccount)

        (done, price, lot, tab) = self.status(id)
        assert not done
        assert max >= price

        slice: Wad = min(lot, amt)          # Purchase as much as possible, up to amt
        owe: Rad = Rad(slice) * Rad(price)  # DAI needed to buy a slice of this sale
        chost = self.chost()

        if Rad(owe) > tab:
            owe = Rad(tab)
            slice = Wad(owe / Rad(price))
        elif owe < tab and slice < lot:
            if (tab - owe) < chost:
                assert tab > chost
                owe = tab - chost
                slice = Wad(owe / Rad(price))

        tab: Rad = tab - owe
        lot: Wad = lot - slice
        assert self.vat.dai(our_address) >= owe
        logger.debug(f"Validated clip.take which will leave tab={float(tab)} and lot={float(lot)}")

    def take(self, id: int, amt: Wad, max: Ray, who: Address = None, data=b'') -> Transact:
        """Buy amount of collateral from auction indexed by id.
        Args:
            id:     Auction id
            amt:    Upper limit on amount of collateral to buy
            max:    Maximum acceptable price (DAI / collateral)
            who:    Receiver of collateral and external call address
            data:   Data to pass in external call; if length 0, no call is done
        """
        assert isinstance(id, int)
        assert isinstance(amt, Wad)
        assert isinstance(max, Ray)

        if who:
            assert isinstance(who, Address)
        else:
            who = Address(self.web3.eth.defaultAccount)

        return Transact(self, self.web3, self.abi, self.address, self._contract, 'take',
                        [id, amt.value, max.value, who.address, data])

    def redo(self, id: int, kpr: Address = None) -> Transact:
        """Restart an auction which ended without liquidating all collateral.
            id:     Auction id
            kpr:    Keeper that called dog.bark()
        """
        assert isinstance(id, int)
        assert isinstance(kpr, Address) or kpr is None

        if kpr:
            assert isinstance(kpr, Address)
        else:
            kpr = Address(self.web3.eth.defaultAccount)

        return Transact(self, self.web3, self.abi, self.address, self._contract, 'redo', [id, kpr.address])

    def upchost(self):
        """Update the the cached dust*chop value following a governance change"""
        return Transact(self, self.web3, self.abi, self.address, self._contract, 'upchost', [])

    def past_logs(self, from_block: int, to_block: int = None, chunk_size=20000):
        logs = super().get_past_lognotes(Clipper.abi, from_block, to_block, chunk_size)

        history = []
        for log in logs:
            if log is None:
                continue
            elif isinstance(log, Clipper.KickLog) \
                    or isinstance(log, Clipper.TakeLog) \
                    or isinstance(log, Clipper.RedoLog):
                history.append(log)
            else:
                logger.debug(f"Found log with signature {log.sig}")
        return history

    def parse_event(self, event):
        signature = Web3.toHex(event['topics'][0])
        codec = ABICodec(default_registry)
        if signature == "0x7c5bfdc0a5e8192f6cd4972f382cec69116862fb62e6abff8003874c58e064b8":
            event_data = get_event_data(codec, self.kick_abi, event)
            return Clipper.KickLog(event_data)
        elif signature == "0x05e309fd6ce72f2ab888a20056bb4210df08daed86f21f95053deb19964d86b1":
            event_data = get_event_data(codec, self.take_abi, event)
            self._get_sender_for_eventlog(event_data)
            return Clipper.TakeLog(event_data, self._get_sender_for_eventlog(event_data))
        elif signature == "0x275de7ecdd375b5e8049319f8b350686131c219dd4dc450a08e9cf83b03c865f":
            event_data = get_event_data(codec, self.redo_abi, event)
            return Clipper.RedoLog(event_data)
        else:
            logger.debug(f"Found event signature {signature}")

    def _get_sender_for_eventlog(self, event_data) -> Address:
        tx_hash = event_data['transactionHash'].hex()
        receipt = self.web3.eth.getTransactionReceipt(tx_hash)
        return Address(receipt['from'])

    def __repr__(self):
        return f"Clipper('{self.address}')"
Beispiel #8
0
class CdpManager(Contract):
    """A client for the `DSCdpManger` contract, which is a wrapper around the cdp system, for easier use.

    Ref. <https://github.com/makerdao/dss-cdp-manager/blob/master/src/DssCdpManager.sol>
    """

    abi = Contract._load_abi(__name__, 'abi/DssCdpManager.abi')
    bin = Contract._load_bin(__name__, 'abi/DssCdpManager.bin')

    def __init__(self, web3: Web3, address: Address):
        assert isinstance(web3, Web3)
        assert isinstance(address, Address)

        self.web3 = web3
        self.address = address
        self._contract = self._get_contract(web3, self.abi, address)
        self.vat = Vat(self.web3,
                       Address(self._contract.functions.vat().call()))

    def open(self, ilk: Ilk, address: Address) -> Transact:
        assert isinstance(ilk, Ilk)
        assert isinstance(address, Address)

        return Transact(self, self.web3, self.abi, self.address,
                        self._contract, 'open',
                        [ilk.toBytes(), address.address])

    def urn(self, cdpid: int) -> Urn:
        '''Returns Urn for respective CDP ID'''
        assert isinstance(cdpid, int)

        urn_address = Address(self._contract.functions.urns(cdpid).call())
        ilk = self.ilk(cdpid)
        urn = self.vat.urn(ilk, Address(urn_address))

        return urn

    def owns(self, cdpid: int) -> Address:
        '''Returns owner Address of respective CDP ID'''
        assert isinstance(cdpid, int)

        owner = Address(self._contract.functions.owns(cdpid).call())
        return owner

    def ilk(self, cdpid: int) -> Ilk:
        '''Returns Ilk for respective CDP ID'''
        assert isinstance(cdpid, int)

        ilk = Ilk.fromBytes(self._contract.functions.ilks(cdpid).call())
        return ilk

    def first(self, address: Address) -> int:
        '''Returns first CDP Id created by owner address'''
        assert isinstance(address, Address)

        cdpid = int(self._contract.functions.first(address.address).call())
        return cdpid

    def last(self, address: Address) -> int:
        '''Returns last CDP Id created by owner address'''
        assert isinstance(address, Address)

        cdpid = self._contract.functions.last(address.address).call()
        return int(cdpid)

    def count(self, address: Address) -> int:
        '''Returns number of CDP's created using the DS-Cdp-Manager contract specifically'''
        assert isinstance(address, Address)

        count = int(self._contract.functions.count(address.address).call())
        return count

    def __repr__(self):
        return f"CdpManager('{self.address}')"
Beispiel #9
0
    def deploy(web3: Web3, debt_ceiling: Wad):
        assert isinstance(web3, Web3)

        vat = Vat.deploy(web3=web3)

        pit = Pit.deploy(web3=web3, vat=vat.address)
        assert pit.file_global_line(debt_ceiling).transact()  # Global debt Ceiling
        assert vat.rely(pit.address).transact()

        dai = DSToken.deploy(web3=web3, symbol='DAI')
        dai_adapter = DaiAdapter.deploy(web3=web3, vat=vat.address, dai=dai.address)
        dai_move = DaiVat.deploy(web3=web3, vat=vat.address)
        assert vat.rely(dai_adapter.address).transact()
        assert vat.rely(dai_move.address).transact()

        mkr = DSToken.deploy(web3=web3, symbol='MKR')

        # TODO: use a DSProxy
        mom = DSGuard.deploy(web3)
        assert mom.permit(DSGuard.ANY, DSGuard.ANY, DSGuard.ANY).transact()
        assert dai.set_authority(mom.address).transact()
        assert mkr.set_authority(mom.address).transact()

        vow = Vow.deploy(web3=web3)
        drip = Drip.deploy(web3=web3, vat=vat.address)
        flap = Flapper.deploy(web3=web3, dai=dai_move.address, gem=mkr.address)

        assert vow.file_vat(vat).transact()
        assert vow.file_flap(flap).transact()
        assert vow.file_bump(Wad.from_number(1000)).transact()
        assert vow.file_sump(Wad.from_number(10)).transact()
        assert drip.file_vow(vow).transact()

        assert vat.rely(vow.address).transact()
        assert vat.rely(drip.address).transact()
        assert vat.rely(flap.address).transact()

        cat = Cat.deploy(web3=web3, vat=vat.address)
        assert cat.file_vow(vow).transact()
        assert cat.file_pit(pit).transact()

        flop = Flopper.deploy(web3=web3, dai=dai_move.address, gem=mkr.address)

        assert vow.file_flop(flop).transact()

        assert vat.rely(cat.address).transact()
        assert vat.rely(flop.address).transact()
        assert vow.rely(cat.address).transact()
        assert flop.rely(vow.address).transact()

        config = DssDeployment.Config(mom, vat, vow, drip, pit, cat, flap, flop, dai, dai_adapter, dai_move, mkr)
        deployment = DssDeployment(web3, config)

        collateral = Collateral.deploy(web3=web3, name='WETH', vat=vat)
        deployment.deploy_collateral(collateral,
                                     debt_ceiling=Wad.from_number(100000),
                                     penalty=Ray.from_number(1),
                                     flop_lot=Wad.from_number(10000),
                                     ratio=Ray.from_number(1.5),
                                     initial_price=Wad.from_number(219))

        return deployment