ETH Price: $2,439.36 (-4.47%)
Gas: 0.3 Gwei

Transaction Decoder

Block:
11512328 at Dec-23-2020 09:45:20 PM +UTC
Transaction Fee:
0.0075236 ETH $18.35
Gas Used:
94,045 Gas / 80 Gwei

Account State Difference:

  Address   Before After State Difference Code
0x7AAF40b3...7e40103Bf
1.168066460948300804 Eth
Nonce: 71
1.160542860948300804 Eth
Nonce: 72
0.0075236
(Ethermine)
896.866027602933756585 Eth896.873551202933756585 Eth0.0075236

Execution Trace

Vyper_contract.exchange( i=0, j=2, dx=15000000000000000000000, min_dy=14957476290 )
  • Dai.transferFrom( src=0x7AAF40b3A7bc11711Bba004Bd3CF8547e40103Bf, dst=0xA5407eAE9Ba41422680e2e00537571bcC53efBfD, wad=15000000000000000000000 )
    File 1 of 2: Vyper_contract
    # (c) Curve.Fi, 2020
    
    
    # External Contracts
    contract ERC20m:
        def totalSupply() -> uint256: constant
        def allowance(_owner: address, _spender: address) -> uint256: constant
        def transfer(_to: address, _value: uint256) -> bool: modifying
        def transferFrom(_from: address, _to: address, _value: uint256) -> bool: modifying
        def approve(_spender: address, _value: uint256) -> bool: modifying
        def mint(_to: address, _value: uint256): modifying
        def burn(_value: uint256): modifying
        def burnFrom(_to: address, _value: uint256): modifying
        def name() -> string[64]: constant
        def symbol() -> string[32]: constant
        def decimals() -> uint256: constant
        def balanceOf(arg0: address) -> uint256: constant
        def set_minter(_minter: address): modifying
    
    
    
    # External Contracts
    contract cERC20:
        def totalSupply() -> uint256: constant
        def allowance(_owner: address, _spender: address) -> uint256: constant
        def transfer(_to: address, _value: uint256) -> bool: modifying
        def transferFrom(_from: address, _to: address, _value: uint256) -> bool: modifying
        def approve(_spender: address, _value: uint256) -> bool: modifying
        def burn(_value: uint256): modifying
        def burnFrom(_to: address, _value: uint256): modifying
        def name() -> string[64]: constant
        def symbol() -> string[32]: constant
        def decimals() -> uint256: constant
        def balanceOf(arg0: address) -> uint256: constant
        def mint(mintAmount: uint256) -> uint256: modifying
        def redeem(redeemTokens: uint256) -> uint256: modifying
        def redeemUnderlying(redeemAmount: uint256) -> uint256: modifying
        def exchangeRateStored() -> uint256: constant
        def exchangeRateCurrent() -> uint256: modifying
        def supplyRatePerBlock() -> uint256: constant
        def accrualBlockNumber() -> uint256: constant
    
    
    from vyper.interfaces import ERC20
    
    
    # Tether transfer-only ABI
    contract USDT:
        def transfer(_to: address, _value: uint256): modifying
        def transferFrom(_from: address, _to: address, _value: uint256): modifying
    
    
    # This can (and needs to) be changed at compile time
    N_COINS: constant(int128) = 4  # <- change
    
    ZERO256: constant(uint256) = 0  # This hack is really bad XXX
    ZEROS: constant(uint256[N_COINS]) = [ZERO256, ZERO256, ZERO256, ZERO256]  # <- change
    
    USE_LENDING: constant(bool[N_COINS]) = [False, False, False, False]
    
    # Flag "ERC20s" which don't return from transfer() and transferFrom()
    TETHERED: constant(bool[N_COINS]) = [False, False, True, False]
    
    FEE_DENOMINATOR: constant(uint256) = 10 ** 10
    LENDING_PRECISION: constant(uint256) = 10 ** 18
    PRECISION: constant(uint256) = 10 ** 18  # The precision to convert to
    PRECISION_MUL: constant(uint256[N_COINS]) = [convert(1, uint256), convert(1000000000000, uint256), convert(1000000000000, uint256), convert(1, uint256)]
    # PRECISION_MUL: constant(uint256[N_COINS]) = [
    #     PRECISION / convert(PRECISION, uint256),  # DAI
    #     PRECISION / convert(10 ** 6, uint256),   # USDC
    #     PRECISION / convert(10 ** 6, uint256)]   # USDT
    
    
    admin_actions_delay: constant(uint256) = 3 * 86400
    
    # Events
    TokenExchange: event({buyer: indexed(address), sold_id: int128, tokens_sold: uint256, bought_id: int128, tokens_bought: uint256})
    TokenExchangeUnderlying: event({buyer: indexed(address), sold_id: int128, tokens_sold: uint256, bought_id: int128, tokens_bought: uint256})
    AddLiquidity: event({provider: indexed(address), token_amounts: uint256[N_COINS], fees: uint256[N_COINS], invariant: uint256, token_supply: uint256})
    RemoveLiquidity: event({provider: indexed(address), token_amounts: uint256[N_COINS], fees: uint256[N_COINS], token_supply: uint256})
    RemoveLiquidityImbalance: event({provider: indexed(address), token_amounts: uint256[N_COINS], fees: uint256[N_COINS], invariant: uint256, token_supply: uint256})
    CommitNewAdmin: event({deadline: indexed(timestamp), admin: indexed(address)})
    NewAdmin: event({admin: indexed(address)})
    CommitNewParameters: event({deadline: indexed(timestamp), A: uint256, fee: uint256, admin_fee: uint256})
    NewParameters: event({A: uint256, fee: uint256, admin_fee: uint256})
    
    coins: public(address[N_COINS])
    underlying_coins: public(address[N_COINS])
    balances: public(uint256[N_COINS])
    A: public(uint256)  # 2 x amplification coefficient
    fee: public(uint256)  # fee * 1e10
    admin_fee: public(uint256)  # admin_fee * 1e10
    
    max_admin_fee: constant(uint256) = 5 * 10 ** 9
    max_fee: constant(uint256) = 5 * 10 ** 9
    max_A: constant(uint256) = 10 ** 6
    
    owner: public(address)
    token: ERC20m
    
    admin_actions_deadline: public(timestamp)
    transfer_ownership_deadline: public(timestamp)
    future_A: public(uint256)
    future_fee: public(uint256)
    future_admin_fee: public(uint256)
    future_owner: public(address)
    
    kill_deadline: timestamp
    kill_deadline_dt: constant(uint256) = 2 * 30 * 86400
    is_killed: bool
    
    
    @public
    def __init__(_coins: address[N_COINS], _underlying_coins: address[N_COINS],
                 _pool_token: address,
                 _A: uint256, _fee: uint256):
        """
        _coins: Addresses of ERC20 conracts of coins (c-tokens) involved
        _underlying_coins: Addresses of plain coins (ERC20)
        _pool_token: Address of the token representing LP share
        _A: Amplification coefficient multiplied by n * (n - 1)
        _fee: Fee to charge for exchanges
        """
        for i in range(N_COINS):
            assert _coins[i] != ZERO_ADDRESS
            assert _underlying_coins[i] != ZERO_ADDRESS
            self.balances[i] = 0
        self.coins = _coins
        self.underlying_coins = _underlying_coins
        self.A = _A
        self.fee = _fee
        self.admin_fee = 0
        self.owner = msg.sender
        self.kill_deadline = block.timestamp + kill_deadline_dt
        self.is_killed = False
        self.token = ERC20m(_pool_token)
    
    
    @private
    @constant
    def _stored_rates() -> uint256[N_COINS]:
        # exchangeRateStored * (1 + supplyRatePerBlock * (getBlockNumber - accrualBlockNumber) / 1e18)
        result: uint256[N_COINS] = PRECISION_MUL
        use_lending: bool[N_COINS] = USE_LENDING
        for i in range(N_COINS):
            rate: uint256 = LENDING_PRECISION  # Used with no lending
            if use_lending[i]:
                rate = cERC20(self.coins[i]).exchangeRateStored()
                supply_rate: uint256 = cERC20(self.coins[i]).supplyRatePerBlock()
                old_block: uint256 = cERC20(self.coins[i]).accrualBlockNumber()
                rate += rate * supply_rate * (block.number - old_block) / LENDING_PRECISION
            result[i] *= rate
        return result
    
    
    @private
    def _current_rates() -> uint256[N_COINS]:
        result: uint256[N_COINS] = PRECISION_MUL
        use_lending: bool[N_COINS] = USE_LENDING
        for i in range(N_COINS):
            rate: uint256 = LENDING_PRECISION  # Used with no lending
            if use_lending[i]:
                rate = cERC20(self.coins[i]).exchangeRateCurrent()
            result[i] *= rate
        return result
    
    
    @private
    @constant
    def _xp(rates: uint256[N_COINS]) -> uint256[N_COINS]:
        result: uint256[N_COINS] = rates
        for i in range(N_COINS):
            result[i] = result[i] * self.balances[i] / PRECISION
        return result
    
    
    @private
    @constant
    def _xp_mem(rates: uint256[N_COINS], _balances: uint256[N_COINS]) -> uint256[N_COINS]:
        result: uint256[N_COINS] = rates
        for i in range(N_COINS):
            result[i] = result[i] * _balances[i] / PRECISION
        return result
    
    
    @private
    @constant
    def get_D(xp: uint256[N_COINS]) -> uint256:
        S: uint256 = 0
        for _x in xp:
            S += _x
        if S == 0:
            return 0
    
        Dprev: uint256 = 0
        D: uint256 = S
        Ann: uint256 = self.A * N_COINS
        for _i in range(255):
            D_P: uint256 = D
            for _x in xp:
                D_P = D_P * D / (_x * N_COINS + 1)  # +1 is to prevent /0
            Dprev = D
            D = (Ann * S + D_P * N_COINS) * D / ((Ann - 1) * D + (N_COINS + 1) * D_P)
            # Equality with the precision of 1
            if D > Dprev:
                if D - Dprev <= 1:
                    break
            else:
                if Dprev - D <= 1:
                    break
        return D
    
    
    @private
    @constant
    def get_D_mem(rates: uint256[N_COINS], _balances: uint256[N_COINS]) -> uint256:
        return self.get_D(self._xp_mem(rates, _balances))
    
    
    @public
    @constant
    def get_virtual_price() -> uint256:
        """
        Returns portfolio virtual price (for calculating profit)
        scaled up by 1e18
        """
        D: uint256 = self.get_D(self._xp(self._stored_rates()))
        # D is in the units similar to DAI (e.g. converted to precision 1e18)
        # When balanced, D = n * x_u - total virtual value of the portfolio
        token_supply: uint256 = self.token.totalSupply()
        return D * PRECISION / token_supply
    
    
    @public
    @constant
    def calc_token_amount(amounts: uint256[N_COINS], deposit: bool) -> uint256:
        """
        Simplified method to calculate addition or reduction in token supply at
        deposit or withdrawal without taking fees into account (but looking at
        slippage).
        Needed to prevent front-running, not for precise calculations!
        """
        _balances: uint256[N_COINS] = self.balances
        rates: uint256[N_COINS] = self._stored_rates()
        D0: uint256 = self.get_D_mem(rates, _balances)
        for i in range(N_COINS):
            if deposit:
                _balances[i] += amounts[i]
            else:
                _balances[i] -= amounts[i]
        D1: uint256 = self.get_D_mem(rates, _balances)
        token_amount: uint256 = self.token.totalSupply()
        diff: uint256 = 0
        if deposit:
            diff = D1 - D0
        else:
            diff = D0 - D1
        return diff * token_amount / D0
    
    
    @public
    @nonreentrant('lock')
    def add_liquidity(amounts: uint256[N_COINS], min_mint_amount: uint256):
        # Amounts is amounts of c-tokens
        assert not self.is_killed
    
        tethered: bool[N_COINS] = TETHERED
        use_lending: bool[N_COINS] = USE_LENDING
        fees: uint256[N_COINS] = ZEROS
        _fee: uint256 = self.fee * N_COINS / (4 * (N_COINS - 1))
        _admin_fee: uint256 = self.admin_fee
    
        token_supply: uint256 = self.token.totalSupply()
        rates: uint256[N_COINS] = self._current_rates()
        # Initial invariant
        D0: uint256 = 0
        old_balances: uint256[N_COINS] = self.balances
        if token_supply > 0:
            D0 = self.get_D_mem(rates, old_balances)
        new_balances: uint256[N_COINS] = old_balances
    
        for i in range(N_COINS):
            if token_supply == 0:
                assert amounts[i] > 0
            # balances store amounts of c-tokens
            new_balances[i] = old_balances[i] + amounts[i]
    
        # Invariant after change
        D1: uint256 = self.get_D_mem(rates, new_balances)
        assert D1 > D0
    
        # We need to recalculate the invariant accounting for fees
        # to calculate fair user's share
        D2: uint256 = D1
        if token_supply > 0:
            # Only account for fees if we are not the first to deposit
            for i in range(N_COINS):
                ideal_balance: uint256 = D1 * old_balances[i] / D0
                difference: uint256 = 0
                if ideal_balance > new_balances[i]:
                    difference = ideal_balance - new_balances[i]
                else:
                    difference = new_balances[i] - ideal_balance
                fees[i] = _fee * difference / FEE_DENOMINATOR
                self.balances[i] = new_balances[i] - (fees[i] * _admin_fee / FEE_DENOMINATOR)
                new_balances[i] -= fees[i]
            D2 = self.get_D_mem(rates, new_balances)
        else:
            self.balances = new_balances
    
        # Calculate, how much pool tokens to mint
        mint_amount: uint256 = 0
        if token_supply == 0:
            mint_amount = D1  # Take the dust if there was any
        else:
            mint_amount = token_supply * (D2 - D0) / D0
    
        assert mint_amount >= min_mint_amount, "Slippage screwed you"
    
        # Take coins from the sender
        for i in range(N_COINS):
            if tethered[i] and not use_lending[i]:
                USDT(self.coins[i]).transferFrom(msg.sender, self, amounts[i])
            else:
                assert_modifiable(
                    cERC20(self.coins[i]).transferFrom(msg.sender, self, amounts[i]))
    
        # Mint pool tokens
        self.token.mint(msg.sender, mint_amount)
    
        log.AddLiquidity(msg.sender, amounts, fees, D1, token_supply + mint_amount)
    
    
    @private
    @constant
    def get_y(i: int128, j: int128, x: uint256, _xp: uint256[N_COINS]) -> uint256:
        # x in the input is converted to the same price/precision
    
        assert (i != j) and (i >= 0) and (j >= 0) and (i < N_COINS) and (j < N_COINS)
    
        D: uint256 = self.get_D(_xp)
        c: uint256 = D
        S_: uint256 = 0
        Ann: uint256 = self.A * N_COINS
    
        _x: uint256 = 0
        for _i in range(N_COINS):
            if _i == i:
                _x = x
            elif _i != j:
                _x = _xp[_i]
            else:
                continue
            S_ += _x
            c = c * D / (_x * N_COINS)
        c = c * D / (Ann * N_COINS)
        b: uint256 = S_ + D / Ann  # - D
        y_prev: uint256 = 0
        y: uint256 = D
        for _i in range(255):
            y_prev = y
            y = (y*y + c) / (2 * y + b - D)
            # Equality with the precision of 1
            if y > y_prev:
                if y - y_prev <= 1:
                    break
            else:
                if y_prev - y <= 1:
                    break
        return y
    
    
    @public
    @constant
    def get_dy(i: int128, j: int128, dx: uint256) -> uint256:
        # dx and dy in c-units
        rates: uint256[N_COINS] = self._stored_rates()
        xp: uint256[N_COINS] = self._xp(rates)
    
        x: uint256 = xp[i] + (dx * rates[i] / PRECISION)
        y: uint256 = self.get_y(i, j, x, xp)
        dy: uint256 = (xp[j] - y) * PRECISION / rates[j]
        _fee: uint256 = self.fee * dy / FEE_DENOMINATOR
        return dy - _fee
    
    
    @public
    @constant
    def get_dy_underlying(i: int128, j: int128, dx: uint256) -> uint256:
        # dx and dy in underlying units
        rates: uint256[N_COINS] = self._stored_rates()
        xp: uint256[N_COINS] = self._xp(rates)
        precisions: uint256[N_COINS] = PRECISION_MUL
    
        x: uint256 = xp[i] + dx * precisions[i]
        y: uint256 = self.get_y(i, j, x, xp)
        dy: uint256 = (xp[j] - y) / precisions[j]
        _fee: uint256 = self.fee * dy / FEE_DENOMINATOR
        return dy - _fee
    
    
    @private
    def _exchange(i: int128, j: int128, dx: uint256, rates: uint256[N_COINS]) -> uint256:
        assert not self.is_killed
        # dx and dy are in c-tokens
    
        xp: uint256[N_COINS] = self._xp(rates)
    
        x: uint256 = xp[i] + dx * rates[i] / PRECISION
        y: uint256 = self.get_y(i, j, x, xp)
        dy: uint256 = xp[j] - y
        dy_fee: uint256 = dy * self.fee / FEE_DENOMINATOR
        dy_admin_fee: uint256 = dy_fee * self.admin_fee / FEE_DENOMINATOR
        self.balances[i] = x * PRECISION / rates[i]
        self.balances[j] = (y + (dy_fee - dy_admin_fee)) * PRECISION / rates[j]
    
        _dy: uint256 = (dy - dy_fee) * PRECISION / rates[j]
    
        return _dy
    
    
    @public
    @nonreentrant('lock')
    def exchange(i: int128, j: int128, dx: uint256, min_dy: uint256):
        rates: uint256[N_COINS] = self._current_rates()
        dy: uint256 = self._exchange(i, j, dx, rates)
        assert dy >= min_dy, "Exchange resulted in fewer coins than expected"
        tethered: bool[N_COINS] = TETHERED
        use_lending: bool[N_COINS] = USE_LENDING
    
        if tethered[i] and not use_lending[i]:
            USDT(self.coins[i]).transferFrom(msg.sender, self, dx)
        else:
            assert_modifiable(cERC20(self.coins[i]).transferFrom(msg.sender, self, dx))
    
        if tethered[j] and not use_lending[j]:
            USDT(self.coins[j]).transfer(msg.sender, dy)
        else:
            assert_modifiable(cERC20(self.coins[j]).transfer(msg.sender, dy))
    
        log.TokenExchange(msg.sender, i, dx, j, dy)
    
    
    @public
    @nonreentrant('lock')
    def exchange_underlying(i: int128, j: int128, dx: uint256, min_dy: uint256):
        rates: uint256[N_COINS] = self._current_rates()
        precisions: uint256[N_COINS] = PRECISION_MUL
        rate_i: uint256 = rates[i] / precisions[i]
        rate_j: uint256 = rates[j] / precisions[j]
        dx_: uint256 = dx * PRECISION / rate_i
    
        dy_: uint256 = self._exchange(i, j, dx_, rates)
        dy: uint256 = dy_ * rate_j / PRECISION
        assert dy >= min_dy, "Exchange resulted in fewer coins than expected"
        use_lending: bool[N_COINS] = USE_LENDING
        tethered: bool[N_COINS] = TETHERED
    
        ok: uint256 = 0
        if tethered[i]:
            USDT(self.underlying_coins[i]).transferFrom(msg.sender, self, dx)
        else:
            assert_modifiable(ERC20(self.underlying_coins[i])\
                .transferFrom(msg.sender, self, dx))
        if use_lending[i]:
            ERC20(self.underlying_coins[i]).approve(self.coins[i], dx)
            ok = cERC20(self.coins[i]).mint(dx)
            if ok > 0:
                raise "Could not mint coin"
        if use_lending[j]:
            ok = cERC20(self.coins[j]).redeem(dy_)
            if ok > 0:
                raise "Could not redeem coin"
        if tethered[j]:
            USDT(self.underlying_coins[j]).transfer(msg.sender, dy)
        else:
            assert_modifiable(ERC20(self.underlying_coins[j])\
                .transfer(msg.sender, dy))
    
        log.TokenExchangeUnderlying(msg.sender, i, dx, j, dy)
    
    
    @public
    @nonreentrant('lock')
    def remove_liquidity(_amount: uint256, min_amounts: uint256[N_COINS]):
        total_supply: uint256 = self.token.totalSupply()
        amounts: uint256[N_COINS] = ZEROS
        fees: uint256[N_COINS] = ZEROS
        tethered: bool[N_COINS] = TETHERED
        use_lending: bool[N_COINS] = USE_LENDING
    
        for i in range(N_COINS):
            value: uint256 = self.balances[i] * _amount / total_supply
            assert value >= min_amounts[i], "Withdrawal resulted in fewer coins than expected"
            self.balances[i] -= value
            amounts[i] = value
            if tethered[i] and not use_lending[i]:
                USDT(self.coins[i]).transfer(msg.sender, value)
            else:
                assert_modifiable(cERC20(self.coins[i]).transfer(
                    msg.sender, value))
    
        self.token.burnFrom(msg.sender, _amount)  # Will raise if not enough
    
        log.RemoveLiquidity(msg.sender, amounts, fees, total_supply - _amount)
    
    
    @public
    @nonreentrant('lock')
    def remove_liquidity_imbalance(amounts: uint256[N_COINS], max_burn_amount: uint256):
        assert not self.is_killed
        tethered: bool[N_COINS] = TETHERED
        use_lending: bool[N_COINS] = USE_LENDING
    
        token_supply: uint256 = self.token.totalSupply()
        assert token_supply > 0
        _fee: uint256 = self.fee * N_COINS / (4 * (N_COINS - 1))
        _admin_fee: uint256 = self.admin_fee
        rates: uint256[N_COINS] = self._current_rates()
    
        old_balances: uint256[N_COINS] = self.balances
        new_balances: uint256[N_COINS] = old_balances
        D0: uint256 = self.get_D_mem(rates, old_balances)
        for i in range(N_COINS):
            new_balances[i] -= amounts[i]
        D1: uint256 = self.get_D_mem(rates, new_balances)
        fees: uint256[N_COINS] = ZEROS
        for i in range(N_COINS):
            ideal_balance: uint256 = D1 * old_balances[i] / D0
            difference: uint256 = 0
            if ideal_balance > new_balances[i]:
                difference = ideal_balance - new_balances[i]
            else:
                difference = new_balances[i] - ideal_balance
            fees[i] = _fee * difference / FEE_DENOMINATOR
            self.balances[i] = new_balances[i] - (fees[i] * _admin_fee / FEE_DENOMINATOR)
            new_balances[i] -= fees[i]
        D2: uint256 = self.get_D_mem(rates, new_balances)
    
        token_amount: uint256 = (D0 - D2) * token_supply / D0
        assert token_amount > 0
        assert token_amount <= max_burn_amount, "Slippage screwed you"
    
        for i in range(N_COINS):
            if tethered[i] and not use_lending[i]:
                USDT(self.coins[i]).transfer(msg.sender, amounts[i])
            else:
                assert_modifiable(cERC20(self.coins[i]).transfer(msg.sender, amounts[i]))
        self.token.burnFrom(msg.sender, token_amount)  # Will raise if not enough
    
        log.RemoveLiquidityImbalance(msg.sender, amounts, fees, D1, token_supply - token_amount)
    
    
    ### Admin functions ###
    @public
    def commit_new_parameters(amplification: uint256,
                              new_fee: uint256,
                              new_admin_fee: uint256):
        assert msg.sender == self.owner
        assert self.admin_actions_deadline == 0
        assert new_admin_fee <= max_admin_fee
        assert new_fee <= max_fee
        assert amplification <= max_A
    
        _deadline: timestamp = block.timestamp + admin_actions_delay
        self.admin_actions_deadline = _deadline
        self.future_A = amplification
        self.future_fee = new_fee
        self.future_admin_fee = new_admin_fee
    
        log.CommitNewParameters(_deadline, amplification, new_fee, new_admin_fee)
    
    
    @public
    def apply_new_parameters():
        assert msg.sender == self.owner
        assert self.admin_actions_deadline <= block.timestamp\
            and self.admin_actions_deadline > 0
    
        self.admin_actions_deadline = 0
        _A: uint256 = self.future_A
        _fee: uint256 = self.future_fee
        _admin_fee: uint256 = self.future_admin_fee
        self.A = _A
        self.fee = _fee
        self.admin_fee = _admin_fee
    
        log.NewParameters(_A, _fee, _admin_fee)
    
    
    @public
    def revert_new_parameters():
        assert msg.sender == self.owner
    
        self.admin_actions_deadline = 0
    
    
    @public
    def commit_transfer_ownership(_owner: address):
        assert msg.sender == self.owner
        assert self.transfer_ownership_deadline == 0
    
        _deadline: timestamp = block.timestamp + admin_actions_delay
        self.transfer_ownership_deadline = _deadline
        self.future_owner = _owner
    
        log.CommitNewAdmin(_deadline, _owner)
    
    
    @public
    def apply_transfer_ownership():
        assert msg.sender == self.owner
        assert block.timestamp >= self.transfer_ownership_deadline\
            and self.transfer_ownership_deadline > 0
    
        self.transfer_ownership_deadline = 0
        _owner: address = self.future_owner
        self.owner = _owner
    
        log.NewAdmin(_owner)
    
    
    @public
    def revert_transfer_ownership():
        assert msg.sender == self.owner
    
        self.transfer_ownership_deadline = 0
    
    
    @public
    def withdraw_admin_fees():
        assert msg.sender == self.owner
        _precisions: uint256[N_COINS] = PRECISION_MUL
        tethered: bool[N_COINS] = TETHERED
        use_lending: bool[N_COINS] = USE_LENDING
    
        for i in range(N_COINS):
            c: address = self.coins[i]
            value: uint256 = cERC20(c).balanceOf(self) - self.balances[i]
            if value > 0:
                if tethered[i] and not use_lending[i]:
                    USDT(c).transfer(msg.sender, value)
                else:
                    assert_modifiable(cERC20(c).transfer(msg.sender, value))
    
    
    @public
    def kill_me():
        assert msg.sender == self.owner
        assert self.kill_deadline > block.timestamp
        self.is_killed = True
    
    
    @public
    def unkill_me():
        assert msg.sender == self.owner
        self.is_killed = False

    File 2 of 2: Dai
    // hevm: flattened sources of /nix/store/8xb41r4qd0cjb63wcrxf1qmfg88p0961-dss-6fd7de0/src/dai.sol
    pragma solidity =0.5.12;
    
    ////// /nix/store/8xb41r4qd0cjb63wcrxf1qmfg88p0961-dss-6fd7de0/src/lib.sol
    // This program is free software: you can redistribute it and/or modify
    // it under the terms of the GNU General Public License as published by
    // the Free Software Foundation, either version 3 of the License, or
    // (at your option) any later version.
    
    // This program is distributed in the hope that it will be useful,
    // but WITHOUT ANY WARRANTY; without even the implied warranty of
    // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    // GNU General Public License for more details.
    
    // You should have received a copy of the GNU General Public License
    // along with this program.  If not, see <http://www.gnu.org/licenses/>.
    
    /* pragma solidity 0.5.12; */
    
    contract LibNote {
        event LogNote(
            bytes4   indexed  sig,
            address  indexed  usr,
            bytes32  indexed  arg1,
            bytes32  indexed  arg2,
            bytes             data
        ) anonymous;
    
        modifier note {
            _;
            assembly {
                // log an 'anonymous' event with a constant 6 words of calldata
                // and four indexed topics: selector, caller, arg1 and arg2
                let mark := msize                         // end of memory ensures zero
                mstore(0x40, add(mark, 288))              // update free memory pointer
                mstore(mark, 0x20)                        // bytes type data offset
                mstore(add(mark, 0x20), 224)              // bytes size (padded)
                calldatacopy(add(mark, 0x40), 0, 224)     // bytes payload
                log4(mark, 288,                           // calldata
                     shl(224, shr(224, calldataload(0))), // msg.sig
                     caller,                              // msg.sender
                     calldataload(4),                     // arg1
                     calldataload(36)                     // arg2
                    )
            }
        }
    }
    
    ////// /nix/store/8xb41r4qd0cjb63wcrxf1qmfg88p0961-dss-6fd7de0/src/dai.sol
    // Copyright (C) 2017, 2018, 2019 dbrock, rain, mrchico
    
    // This program is free software: you can redistribute it and/or modify
    // it under the terms of the GNU Affero General Public License as published by
    // the Free Software Foundation, either version 3 of the License, or
    // (at your option) any later version.
    //
    // This program is distributed in the hope that it will be useful,
    // but WITHOUT ANY WARRANTY; without even the implied warranty of
    // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    // GNU Affero General Public License for more details.
    //
    // You should have received a copy of the GNU Affero General Public License
    // along with this program.  If not, see <https://www.gnu.org/licenses/>.
    
    /* pragma solidity 0.5.12; */
    
    /* import "./lib.sol"; */
    
    contract Dai is LibNote {
        // --- Auth ---
        mapping (address => uint) public wards;
        function rely(address guy) external note auth { wards[guy] = 1; }
        function deny(address guy) external note auth { wards[guy] = 0; }
        modifier auth {
            require(wards[msg.sender] == 1, "Dai/not-authorized");
            _;
        }
    
        // --- ERC20 Data ---
        string  public constant name     = "Dai Stablecoin";
        string  public constant symbol   = "DAI";
        string  public constant version  = "1";
        uint8   public constant decimals = 18;
        uint256 public totalSupply;
    
        mapping (address => uint)                      public balanceOf;
        mapping (address => mapping (address => uint)) public allowance;
        mapping (address => uint)                      public nonces;
    
        event Approval(address indexed src, address indexed guy, uint wad);
        event Transfer(address indexed src, address indexed dst, uint wad);
    
        // --- Math ---
        function add(uint x, uint y) internal pure returns (uint z) {
            require((z = x + y) >= x);
        }
        function sub(uint x, uint y) internal pure returns (uint z) {
            require((z = x - y) <= x);
        }
    
        // --- EIP712 niceties ---
        bytes32 public DOMAIN_SEPARATOR;
        // bytes32 public constant PERMIT_TYPEHASH = keccak256("Permit(address holder,address spender,uint256 nonce,uint256 expiry,bool allowed)");
        bytes32 public constant PERMIT_TYPEHASH = 0xea2aa0a1be11a07ed86d755c93467f4f82362b452371d1ba94d1715123511acb;
    
        constructor(uint256 chainId_) public {
            wards[msg.sender] = 1;
            DOMAIN_SEPARATOR = keccak256(abi.encode(
                keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"),
                keccak256(bytes(name)),
                keccak256(bytes(version)),
                chainId_,
                address(this)
            ));
        }
    
        // --- Token ---
        function transfer(address dst, uint wad) external returns (bool) {
            return transferFrom(msg.sender, dst, wad);
        }
        function transferFrom(address src, address dst, uint wad)
            public returns (bool)
        {
            require(balanceOf[src] >= wad, "Dai/insufficient-balance");
            if (src != msg.sender && allowance[src][msg.sender] != uint(-1)) {
                require(allowance[src][msg.sender] >= wad, "Dai/insufficient-allowance");
                allowance[src][msg.sender] = sub(allowance[src][msg.sender], wad);
            }
            balanceOf[src] = sub(balanceOf[src], wad);
            balanceOf[dst] = add(balanceOf[dst], wad);
            emit Transfer(src, dst, wad);
            return true;
        }
        function mint(address usr, uint wad) external auth {
            balanceOf[usr] = add(balanceOf[usr], wad);
            totalSupply    = add(totalSupply, wad);
            emit Transfer(address(0), usr, wad);
        }
        function burn(address usr, uint wad) external {
            require(balanceOf[usr] >= wad, "Dai/insufficient-balance");
            if (usr != msg.sender && allowance[usr][msg.sender] != uint(-1)) {
                require(allowance[usr][msg.sender] >= wad, "Dai/insufficient-allowance");
                allowance[usr][msg.sender] = sub(allowance[usr][msg.sender], wad);
            }
            balanceOf[usr] = sub(balanceOf[usr], wad);
            totalSupply    = sub(totalSupply, wad);
            emit Transfer(usr, address(0), wad);
        }
        function approve(address usr, uint wad) external returns (bool) {
            allowance[msg.sender][usr] = wad;
            emit Approval(msg.sender, usr, wad);
            return true;
        }
    
        // --- Alias ---
        function push(address usr, uint wad) external {
            transferFrom(msg.sender, usr, wad);
        }
        function pull(address usr, uint wad) external {
            transferFrom(usr, msg.sender, wad);
        }
        function move(address src, address dst, uint wad) external {
            transferFrom(src, dst, wad);
        }
    
        // --- Approve by signature ---
        function permit(address holder, address spender, uint256 nonce, uint256 expiry,
                        bool allowed, uint8 v, bytes32 r, bytes32 s) external
        {
            bytes32 digest =
                keccak256(abi.encodePacked(
                    "\x19\x01",
                    DOMAIN_SEPARATOR,
                    keccak256(abi.encode(PERMIT_TYPEHASH,
                                         holder,
                                         spender,
                                         nonce,
                                         expiry,
                                         allowed))
            ));
    
            require(holder != address(0), "Dai/invalid-address-0");
            require(holder == ecrecover(digest, v, r, s), "Dai/invalid-permit");
            require(expiry == 0 || now <= expiry, "Dai/permit-expired");
            require(nonce == nonces[holder]++, "Dai/invalid-nonce");
            uint wad = allowed ? uint(-1) : 0;
            allowance[holder][spender] = wad;
            emit Approval(holder, spender, wad);
        }
    }