ETH Price: $2,452.70 (+2.16%)

Transaction Decoder

Block:
12493707 at May-24-2021 12:01:28 AM +UTC
Transaction Fee:
0.018261111 ETH $44.79
Gas Used:
424,677 Gas / 43 Gwei

Emitted Events:

97 0xa78c17921e7e060c246ac9575b01ff0fb29bceae.0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef( 0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef, 0x000000000000000000000000e7eb8022cb1040a3fad73886048d8af48be1d1b5, 0x000000000000000000000000c42cfb07bc1140f9a615bd63c4ffae5f8260ab22, 0xf228a8a8c846d97aa0c7c2726ace3761e09aa1857eb9a31d92aeb80272b81445 )
98 Shelf.0xdd46706400000000000000000000000000000000000000000000000000000000( 0xdd46706400000000000000000000000000000000000000000000000000000000, 0x000000000000000000000000e7eb8022cb1040a3fad73886048d8af48be1d1b5, 0x000000000000000000000000000000000000000000000000000000000000000d, 0x0000000000000000000000000000000000000000000000000000000000000000, 0000000000000000000000000000000000000000000000000000000000000000, 0000000000000000000000000000000000000000000000000000000000000040, 0000000000000000000000000000000000000000000000000000000000000024, dd46706400000000000000000000000000000000000000000000000000000000, 0000000d00000000000000000000000000000000000000000000000000000000 )
99 Pile.0x46df2ccb00000000000000000000000000000000000000000000000000000000( 0x46df2ccb00000000000000000000000000000000000000000000000000000000, 0x00000000000000000000000000cd3ae59fdbd375a187bf8074db59edaf766c19, 0x000000000000000000000000000000000000000000000000000000000000000d, 0x000000000000000000000000000000000000000000000000000000000000000d, 0000000000000000000000000000000000000000000000000000000000000000, 0000000000000000000000000000000000000000000000000000000000000040, 0000000000000000000000000000000000000000000000000000000000000044, 46df2ccb00000000000000000000000000000000000000000000000000000000, 0000000d00000000000000000000000000000000000000000000000000000000, 0000000d00000000000000000000000000000000000000000000000000000000 )
100 Pile.0x071ffb3c00000000000000000000000000000000000000000000000000000000( 0x071ffb3c00000000000000000000000000000000000000000000000000000000, 0x000000000000000000000000c42cfb07bc1140f9a615bd63c4ffae5f8260ab22, 0x000000000000000000000000000000000000000000000000000000000000000d, 0x00000000000000000000000000000000000000000000055e1e2a548a49900000, 0000000000000000000000000000000000000000000000000000000000000000, 0000000000000000000000000000000000000000000000000000000000000040, 0000000000000000000000000000000000000000000000000000000000000044, 071ffb3c00000000000000000000000000000000000000000000000000000000, 0000000d00000000000000000000000000000000000000000000055e1e2a548a, 4990000000000000000000000000000000000000000000000000000000000000 )
101 Shelf.0x0ecbcdab00000000000000000000000000000000000000000000000000000000( 0x0ecbcdab00000000000000000000000000000000000000000000000000000000, 0x000000000000000000000000e7eb8022cb1040a3fad73886048d8af48be1d1b5, 0x000000000000000000000000000000000000000000000000000000000000000d, 0x00000000000000000000000000000000000000000000055e1e2a548a49900000, 0000000000000000000000000000000000000000000000000000000000000000, 0000000000000000000000000000000000000000000000000000000000000040, 0000000000000000000000000000000000000000000000000000000000000044, 0ecbcdab00000000000000000000000000000000000000000000000000000000, 0000000d00000000000000000000000000000000000000000000055e1e2a548a, 4990000000000000000000000000000000000000000000000000000000000000 )
102 Dai.Transfer( src=Reserve, dst=Shelf, wad=25348000000000000000000 )
103 Dai.Transfer( src=Shelf, dst=[Sender] 0xe23d39c932d1cfb8956d0291f4f06e61bb31729e, wad=25348000000000000000000 )
104 Shelf.0x0ad58d2f00000000000000000000000000000000000000000000000000000000( 0x0ad58d2f00000000000000000000000000000000000000000000000000000000, 0x000000000000000000000000e7eb8022cb1040a3fad73886048d8af48be1d1b5, 0x000000000000000000000000000000000000000000000000000000000000000d, 0x00000000000000000000000000000000000000000000055e1e2a548a49900000, 0000000000000000000000000000000000000000000000000000000000000000, 0000000000000000000000000000000000000000000000000000000000000040, 0000000000000000000000000000000000000000000000000000000000000064, 0ad58d2f00000000000000000000000000000000000000000000000000000000, 0000000d00000000000000000000000000000000000000000000055e1e2a548a, 49900000000000000000000000000000e23d39c932d1cfb8956d0291f4f06e61, bb31729e00000000000000000000000000000000000000000000000000000000 )

Account State Difference:

  Address   Before After State Difference Code
0x00cD3AE5...DAF766C19
0x6B175474...495271d0F
0x729e12cD...32d75AdC5
0x95bC79d2...B7A781a96
0xa78C1792...Fb29BCeaE
0xdB07B211...87c9c2aFf
0xe23d39C9...1Bb31729E
0.850074354002474621 Eth
Nonce: 57
0.831813243002474621 Eth
Nonce: 58
0.018261111
(Ethermine)
1,602.243380951929161693 Eth1,602.261642062929161693 Eth0.018261111

Execution Trace

0xe7eb8022cb1040a3fad73886048d8af48be1d1b5.1cff79cd( )
  • 0xc9045c815bf123ad12ea75b9a7c579c1e05051f9.6352211e( )
  • 0x39e9b206dd1e8f9849f11e8ba6bb045e8321a239.1614fa07( )
    • Shelf.lock( loan=13 )
      • Title.ownerOf( tokenId=13 ) => ( 0xE7eB8022CB1040a3FAd73886048d8AF48Be1D1b5 )
      • NAVFeed.unlockEvent( loan=13 )
      • 0xa78c17921e7e060c246ac9575b01ff0fb29bceae.23b872dd( )
      • Shelf.borrow( loan=13, currencyAmount=25348000000000000000000 )
        • Title.ownerOf( tokenId=13 ) => ( 0xE7eB8022CB1040a3FAd73886048d8AF48Be1D1b5 )
        • 0xa78c17921e7e060c246ac9575b01ff0fb29bceae.6352211e( )
        • NAVFeed.borrowEvent( loan=13 )
          • Shelf.shelf( 13 ) => ( registry=0xa78C17921E7E060c246AC9575B01fF0Fb29BCeaE, tokenId=109531547283490506394320449321629014911379087224272814518978146155272055821381 )
          • Pile.loanRates( 13 ) => ( 0 )
          • Pile.setRate( loan=13, rate=13 )
          • Pile.accrue( loan=13 )
          • NAVFeed.borrow( loan=13, amount=25348000000000000000000 ) => ( navIncrease=25294073566662402156298 )
            • Shelf.shelf( 13 ) => ( registry=0xa78C17921E7E060c246AC9575B01fF0Fb29BCeaE, tokenId=109531547283490506394320449321629014911379087224272814518978146155272055821381 )
            • Shelf.shelf( 13 ) => ( registry=0xa78C17921E7E060c246AC9575B01fF0Fb29BCeaE, tokenId=109531547283490506394320449321629014911379087224272814518978146155272055821381 )
            • Shelf.shelf( 13 ) => ( registry=0xa78C17921E7E060c246AC9575B01fF0Fb29BCeaE, tokenId=109531547283490506394320449321629014911379087224272814518978146155272055821381 )
            • Pile.loanRates( 13 ) => ( 13 )
            • Pile.rates( 13 ) => ( pie=37518520242501071069943, chi=1021450326718934255168206307, ratePerSecond=1000000001867706747843734145, lastUpdated=1621814488, fixedRate=0 )
            • Pile.loanRates( 13 ) => ( 13 )
            • Pile.rates( 13 ) => ( pie=37518520242501071069943, chi=1021450326718934255168206307, ratePerSecond=1000000001867706747843734145, lastUpdated=1621814488, fixedRate=0 )
            • Pile.incDebt( loan=13, currencyAmount=25348000000000000000000 )
            • Shelf.withdraw( loan=13, currencyAmount=25348000000000000000000, usr=0xe23d39C932D1cfB8956d0291f4F06E61Bb31729E )
              • Title.ownerOf( tokenId=13 ) => ( 0xE7eB8022CB1040a3FAd73886048d8AF48Be1D1b5 )
              • 0xa78c17921e7e060c246ac9575b01ff0fb29bceae.6352211e( )
              • Reserve.CALL( )
                • Shelf.CALL( )
                  • Dai.balanceOf( 0xC42CfB07bC1140f9A615bD63c4fFAE5F8260Ab22 ) => ( 0 )
                  • Dai.transferFrom( src=0x729e12cDc0190A2e4Ab4401bca4C16132d75AdC5, dst=0xC42CfB07bC1140f9A615bD63c4fFAE5F8260Ab22, wad=25348000000000000000000 ) => ( True )
                  • Assessor.borrowUpdate( currencyAmount=25348000000000000000000 )
                  • Dai.transferFrom( src=0xC42CfB07bC1140f9A615bD63c4fFAE5F8260Ab22, dst=0xe23d39C932D1cfB8956d0291f4F06E61Bb31729E, wad=25348000000000000000000 ) => ( True )
                    File 1 of 7: Shelf
                    // Verified using https://dapp.tools
                    
                    // hevm: flattened sources of src/borrower/shelf.sol
                    pragma solidity >=0.4.23 >=0.5.15 >=0.5.0 <0.6.0 >=0.5.15 <0.6.0;
                    
                    ////// lib/ds-test/src/test.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.4.23; */
                    
                    contract DSTest {
                        event eventListener          (address target, bool exact);
                        event logs                   (bytes);
                        event log_bytes32            (bytes32);
                        event log_named_address      (bytes32 key, address val);
                        event log_named_bytes32      (bytes32 key, bytes32 val);
                        event log_named_decimal_int  (bytes32 key, int val, uint decimals);
                        event log_named_decimal_uint (bytes32 key, uint val, uint decimals);
                        event log_named_int          (bytes32 key, int val);
                        event log_named_uint         (bytes32 key, uint val);
                        event log_named_string       (bytes32 key, string val);
                    
                        bool public IS_TEST;
                        bool public failed;
                    
                        constructor() internal {
                            IS_TEST = true;
                        }
                    
                        function fail() internal {
                            failed = true;
                        }
                    
                        function expectEventsExact(address target) internal {
                            emit eventListener(target, true);
                        }
                    
                        modifier logs_gas() {
                            uint startGas = gasleft();
                            _;
                            uint endGas = gasleft();
                            emit log_named_uint("gas", startGas - endGas);
                        }
                    
                        function assertTrue(bool condition) internal {
                            if (!condition) {
                                emit log_bytes32("Assertion failed");
                                fail();
                            }
                        }
                    
                        function assertEq(address a, address b) internal {
                            if (a != b) {
                                emit log_bytes32("Error: Wrong `address' value");
                                emit log_named_address("  Expected", b);
                                emit log_named_address("    Actual", a);
                                fail();
                            }
                        }
                    
                        function assertEq32(bytes32 a, bytes32 b) internal {
                            assertEq(a, b);
                        }
                    
                        function assertEq(bytes32 a, bytes32 b) internal {
                            if (a != b) {
                                emit log_bytes32("Error: Wrong `bytes32' value");
                                emit log_named_bytes32("  Expected", b);
                                emit log_named_bytes32("    Actual", a);
                                fail();
                            }
                        }
                    
                        function assertEqDecimal(int a, int b, uint decimals) internal {
                            if (a != b) {
                                emit log_bytes32("Error: Wrong fixed-point decimal");
                                emit log_named_decimal_int("  Expected", b, decimals);
                                emit log_named_decimal_int("    Actual", a, decimals);
                                fail();
                            }
                        }
                    
                        function assertEqDecimal(uint a, uint b, uint decimals) internal {
                            if (a != b) {
                                emit log_bytes32("Error: Wrong fixed-point decimal");
                                emit log_named_decimal_uint("  Expected", b, decimals);
                                emit log_named_decimal_uint("    Actual", a, decimals);
                                fail();
                            }
                        }
                    
                        function assertEq(int a, int b) internal {
                            if (a != b) {
                                emit log_bytes32("Error: Wrong `int' value");
                                emit log_named_int("  Expected", b);
                                emit log_named_int("    Actual", a);
                                fail();
                            }
                        }
                    
                        function assertEq(uint a, uint b) internal {
                            if (a != b) {
                                emit log_bytes32("Error: Wrong `uint' value");
                                emit log_named_uint("  Expected", b);
                                emit log_named_uint("    Actual", a);
                                fail();
                            }
                        }
                    
                        function assertEq(string memory a, string memory b) internal {
                            if (keccak256(abi.encodePacked(a)) != keccak256(abi.encodePacked(b))) {
                                emit log_bytes32("Error: Wrong `string' value");
                                emit log_named_string("  Expected", b);
                                emit log_named_string("    Actual", a);
                                fail();
                            }
                        }
                    
                        function assertEq0(bytes memory a, bytes memory b) internal {
                            bool ok = true;
                    
                            if (a.length == b.length) {
                                for (uint i = 0; i < a.length; i++) {
                                    if (a[i] != b[i]) {
                                        ok = false;
                                    }
                                }
                            } else {
                                ok = false;
                            }
                    
                            if (!ok) {
                                emit log_bytes32("Error: Wrong `bytes' value");
                                emit log_named_bytes32("  Expected", "[cannot show `bytes' value]");
                                emit log_named_bytes32("  Actual", "[cannot show `bytes' value]");
                                fail();
                            }
                        }
                    }
                    
                    ////// lib/tinlake-auth/lib/ds-note/src/note.sol
                    /// note.sol -- the `note' modifier, for logging calls as events
                    
                    // 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.15; */
                    
                    contract DSNote {
                        event LogNote(
                            bytes4   indexed  sig,
                            address  indexed  guy,
                            bytes32  indexed  foo,
                            bytes32  indexed  bar,
                            uint256           wad,
                            bytes             fax
                        ) anonymous;
                    
                        modifier note {
                            bytes32 foo;
                            bytes32 bar;
                            uint256 wad;
                    
                            assembly {
                                foo := calldataload(4)
                                bar := calldataload(36)
                                wad := callvalue()
                            }
                    
                            _;
                    
                            emit LogNote(msg.sig, msg.sender, foo, bar, wad, msg.data);
                        }
                    }
                    
                    ////// lib/tinlake-auth/src/auth.sol
                    // Copyright (C) Centrifuge 2020, based on MakerDAO dss https://github.com/makerdao/dss
                    //
                    // 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.15 <0.6.0; */
                    
                    /* import "ds-note/note.sol"; */
                    
                    contract Auth is DSNote {
                        mapping (address => uint) public wards;
                        function rely(address usr) public auth note { wards[usr] = 1; }
                        function deny(address usr) public auth note { wards[usr] = 0; }
                        modifier auth { require(wards[msg.sender] == 1); _; }
                    }
                    
                    ////// lib/tinlake-math/src/math.sol
                    // Copyright (C) 2018 Rain <[email protected]>
                    //
                    // 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.15 <0.6.0; */
                    
                    contract Math {
                        uint256 constant ONE = 10 ** 27;
                    
                        function safeAdd(uint x, uint y) public pure returns (uint z) {
                            require((z = x + y) >= x, "safe-add-failed");
                        }
                    
                        function safeSub(uint x, uint y) public pure returns (uint z) {
                            require((z = x - y) <= x, "safe-sub-failed");
                        }
                    
                        function safeMul(uint x, uint y) public pure returns (uint z) {
                            require(y == 0 || (z = x * y) / y == x, "safe-mul-failed");
                        }
                    
                        function safeDiv(uint x, uint y) public pure returns (uint z) {
                            z = x / y;
                        }
                    
                        function rmul(uint x, uint y) public pure returns (uint z) {
                            z = safeMul(x, y) / ONE;
                        }
                    
                        function rdiv(uint x, uint y) public pure returns (uint z) {
                            require(y > 0, "division by zero");
                            z = safeAdd(safeMul(x, ONE), y / 2) / y;
                        }
                    
                        function rdivup(uint x, uint y) internal pure returns (uint z) {
                            require(y > 0, "division by zero");
                            // always rounds up
                            z = safeAdd(safeMul(x, ONE), safeSub(y, 1)) / y;
                        }
                    
                    
                    }
                    
                    ////// lib/tinlake-title/src/openzeppelin-solidity/math/SafeMath.sol
                    /* pragma solidity ^0.5.0; */
                    
                    /**
                     * @title SafeMath
                     * @dev Unsigned math operations with safety checks that revert on error.
                     */
                    library SafeMath {
                        /**
                         * @dev Multiplies two unsigned integers, reverts on overflow.
                         */
                        function mul(uint256 a, uint256 b) internal pure returns (uint256) {
                            // Gas optimization: this is cheaper than requiring 'a' not being zero, but the
                            // benefit is lost if 'b' is also tested.
                            // See: https://github.com/OpenZeppelin/openzeppelin-solidity/pull/522
                            if (a == 0) {
                                return 0;
                            }
                    
                            uint256 c = a * b;
                            require(c / a == b, "SafeMath: multiplication overflow");
                    
                            return c;
                        }
                    
                        /**
                         * @dev Integer division of two unsigned integers truncating the quotient, reverts on division by zero.
                         */
                        function div(uint256 a, uint256 b) internal pure returns (uint256) {
                            // Solidity only automatically asserts when dividing by 0
                            require(b > 0, "SafeMath: division by zero");
                            uint256 c = a / b;
                            // assert(a == b * c + a % b); // There is no case in which this doesn't hold
                    
                            return c;
                        }
                    
                        /**
                         * @dev Subtracts two unsigned integers, reverts on overflow (i.e. if subtrahend is greater than minuend).
                         */
                        function sub(uint256 a, uint256 b) internal pure returns (uint256) {
                            require(b <= a, "SafeMath: subtraction overflow");
                            uint256 c = a - b;
                    
                            return c;
                        }
                    
                        /**
                         * @dev Adds two unsigned integers, reverts on overflow.
                         */
                        function add(uint256 a, uint256 b) internal pure returns (uint256) {
                            uint256 c = a + b;
                            require(c >= a, "SafeMath: addition overflow");
                    
                            return c;
                        }
                    
                        /**
                         * @dev Divides two unsigned integers and returns the remainder (unsigned integer modulo),
                         * reverts when dividing by zero.
                         */
                        function mod(uint256 a, uint256 b) internal pure returns (uint256) {
                            require(b != 0, "SafeMath: modulo by zero");
                            return a % b;
                        }
                    }
                    
                    ////// lib/tinlake-title/src/openzeppelin-solidity/drafts/Counters.sol
                    /* pragma solidity ^0.5.0; */
                    
                    /* import "../math/SafeMath.sol"; */
                    
                    /**
                     * @title Counters
                     * @author Matt Condon (@shrugs)
                     * @dev Provides counters that can only be incremented or decremented by one. This can be used e.g. to track the number
                     * of elements in a mapping, issuing ERC721 ids, or counting request ids.
                     *
                     * Include with `using Counters for Counters.Counter;`
                     * Since it is not possible to overflow a 256 bit integer with increments of one, `increment` can skip the SafeMath
                     * overflow check, thereby saving gas. This does assume however correct usage, in that the underlying `_value` is never
                     * directly accessed.
                     */
                    library Counters {
                        using SafeMath for uint256;
                    
                        struct Counter {
                            // This variable should never be directly accessed by users of the library: interactions must be restricted to
                            // the library's function. As of Solidity v0.5.2, this cannot be enforced, though there is a proposal to add
                            // this feature: see https://github.com/ethereum/solidity/issues/4637
                            uint256 _value; // default: 0
                        }
                    
                        function current(Counter storage counter) internal view returns (uint256) {
                            return counter._value;
                        }
                    
                        function increment(Counter storage counter) internal {
                            counter._value += 1;
                        }
                    
                        function decrement(Counter storage counter) internal {
                            counter._value = counter._value.sub(1);
                        }
                    }
                    
                    ////// lib/tinlake-title/src/openzeppelin-solidity/introspection/IERC165.sol
                    /* pragma solidity ^0.5.0; */
                    
                    /**
                     * @title IERC165
                     * @dev https://eips.ethereum.org/EIPS/eip-165
                     */
                    interface IERC165 {
                        /**
                         * @notice Query if a contract implements an interface
                         * @param interfaceId The interface identifier, as specified in ERC-165
                         * @dev Interface identification is specified in ERC-165. This function
                         * uses less than 30,000 gas.
                         */
                        function supportsInterface(bytes4 interfaceId) external view returns (bool);
                    }
                    
                    ////// lib/tinlake-title/src/openzeppelin-solidity/introspection/ERC165.sol
                    /* pragma solidity ^0.5.0; */
                    
                    /* import "./IERC165.sol"; */
                    
                    /**
                     * @title ERC165
                     * @author Matt Condon (@shrugs)
                     * @dev Implements ERC165 using a lookup table.
                     */
                    contract ERC165 is IERC165 {
                        /*
                         * bytes4(keccak256('supportsInterface(bytes4)')) == 0x01ffc9a7
                         */
                        bytes4 private constant _INTERFACE_ID_ERC165 = 0x01ffc9a7;
                    
                        /**
                         * @dev Mapping of interface ids to whether or not it's supported.
                         */
                        mapping(bytes4 => bool) private _supportedInterfaces;
                    
                        /**
                         * @dev A contract implementing SupportsInterfaceWithLookup
                         * implements ERC165 itself.
                         */
                        constructor () internal {
                            _registerInterface(_INTERFACE_ID_ERC165);
                        }
                    
                        /**
                         * @dev Implement supportsInterface(bytes4) using a lookup table.
                         */
                        function supportsInterface(bytes4 interfaceId) external view returns (bool) {
                            return _supportedInterfaces[interfaceId];
                        }
                    
                        /**
                         * @dev Internal method for registering an interface.
                         */
                        function _registerInterface(bytes4 interfaceId) internal {
                            require(interfaceId != 0xffffffff, "ERC165: invalid interface id");
                            _supportedInterfaces[interfaceId] = true;
                        }
                    }
                    
                    ////// lib/tinlake-title/src/openzeppelin-solidity/token/ERC721/IERC721.sol
                    /* pragma solidity ^0.5.0; */
                    
                    /* import "../../introspection/IERC165.sol"; */
                    
                    /**
                     * @title ERC721 Non-Fungible Token Standard basic interface
                     * @dev see https://eips.ethereum.org/EIPS/eip-721
                     */
                    contract IERC721 is IERC165 {
                        event Transfer(address indexed from, address indexed to, uint256 indexed tokenId);
                        event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId);
                        event ApprovalForAll(address indexed owner, address indexed operator, bool approved);
                    
                        function balanceOf(address owner) public view returns (uint256 balance);
                        function ownerOf(uint256 tokenId) public view returns (address owner);
                    
                        function approve(address to, uint256 tokenId) public;
                        function getApproved(uint256 tokenId) public view returns (address operator);
                    
                        function setApprovalForAll(address operator, bool _approved) public;
                        function isApprovedForAll(address owner, address operator) public view returns (bool);
                    
                        function transferFrom(address from, address to, uint256 tokenId) public;
                        function safeTransferFrom(address from, address to, uint256 tokenId) public;
                    
                        function safeTransferFrom(address from, address to, uint256 tokenId, bytes memory data) public;
                    }
                    
                    ////// lib/tinlake-title/src/openzeppelin-solidity/token/ERC721/IERC721Receiver.sol
                    /* pragma solidity ^0.5.0; */
                    
                    /**
                     * @title ERC721 token receiver interface
                     * @dev Interface for any contract that wants to support safeTransfers
                     * from ERC721 asset contracts.
                     */
                    contract IERC721Receiver {
                        /**
                         * @notice Handle the receipt of an NFT
                         * @dev The ERC721 smart contract calls this function on the recipient
                         * after a `safeTransfer`. This function MUST return the function selector,
                         * otherwise the caller will revert the transaction. The selector to be
                         * returned can be obtained as `this.onERC721Received.selector`. This
                         * function MAY throw to revert and reject the transfer.
                         * Note: the ERC721 contract address is always the message sender.
                         * @param operator The address which called `safeTransferFrom` function
                         * @param from The address which previously owned the token
                         * @param tokenId The NFT identifier which is being transferred
                         * @param data Additional data with no specified format
                         * @return bytes4 `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))`
                         */
                        function onERC721Received(address operator, address from, uint256 tokenId, bytes memory data)
                        public returns (bytes4);
                    }
                    
                    ////// lib/tinlake-title/src/openzeppelin-solidity/utils/Address.sol
                    /* pragma solidity ^0.5.0; */
                    
                    /**
                     * Utility library of inline functions on addresses
                     */
                    library Address {
                        /**
                         * Returns whether the target address is a contract
                         * @dev This function will return false if invoked during the constructor of a contract,
                         * as the code is not actually created until after the constructor finishes.
                         * @param account address of the account to check
                         * @return whether the target address is a contract
                         */
                        function isContract(address account) internal view returns (bool) {
                            uint256 size;
                            // XXX Currently there is no better way to check if there is a contract in an address
                            // than to check the size of the code at that address.
                            // See https://ethereum.stackexchange.com/a/14016/36603
                            // for more details about how this works.
                            // TODO Check this again before the Serenity release, because all addresses will be
                            // contracts then.
                            // solhint-disable-next-line no-inline-assembly
                            assembly { size := extcodesize(account) }
                            return size > 0;
                        }
                    }
                    
                    ////// lib/tinlake-title/src/openzeppelin-solidity/token/ERC721/ERC721.sol
                    /* pragma solidity ^0.5.0; */
                    
                    /* import "./IERC721.sol"; */
                    /* import "./IERC721Receiver.sol"; */
                    /* import "../../math/SafeMath.sol"; */
                    /* import "../../utils/Address.sol"; */
                    /* import "../../drafts/Counters.sol"; */
                    /* import "../../introspection/ERC165.sol"; */
                    
                    /**
                     * @title ERC721 Non-Fungible Token Standard basic implementation
                     * @dev see https://eips.ethereum.org/EIPS/eip-721
                     */
                    contract ERC721 is ERC165, IERC721 {
                        using SafeMath for uint256;
                        using Address for address;
                        using Counters for Counters.Counter;
                    
                        // Equals to `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))`
                        // which can be also obtained as `IERC721Receiver(0).onERC721Received.selector`
                        bytes4 private constant _ERC721_RECEIVED = 0x150b7a02;
                    
                        // Mapping from token ID to owner
                        mapping (uint256 => address) private _tokenOwner;
                    
                        // Mapping from token ID to approved address
                        mapping (uint256 => address) private _tokenApprovals;
                    
                        // Mapping from owner to number of owned token
                        mapping (address => Counters.Counter) private _ownedTokensCount;
                    
                        // Mapping from owner to operator approvals
                        mapping (address => mapping (address => bool)) private _operatorApprovals;
                    
                        /*
                         *     bytes4(keccak256('balanceOf(address)')) == 0x70a08231
                         *     bytes4(keccak256('ownerOf(uint256)')) == 0x6352211e
                         *     bytes4(keccak256('approve(address,uint256)')) == 0x095ea7b3
                         *     bytes4(keccak256('getApproved(uint256)')) == 0x081812fc
                         *     bytes4(keccak256('setApprovalForAll(address,bool)')) == 0xa22cb465
                         *     bytes4(keccak256('isApprovedForAll(address,address)')) == 0xe985e9c
                         *     bytes4(keccak256('transferFrom(address,address,uint256)')) == 0x23b872dd
                         *     bytes4(keccak256('safeTransferFrom(address,address,uint256)')) == 0x42842e0e
                         *     bytes4(keccak256('safeTransferFrom(address,address,uint256,bytes)')) == 0xb88d4fde
                         *
                         *     => 0x70a08231 ^ 0x6352211e ^ 0x095ea7b3 ^ 0x081812fc ^
                         *        0xa22cb465 ^ 0xe985e9c ^ 0x23b872dd ^ 0x42842e0e ^ 0xb88d4fde == 0x80ac58cd
                         */
                        bytes4 private constant _INTERFACE_ID_ERC721 = 0x80ac58cd;
                    
                        constructor () public {
                            // register the supported interfaces to conform to ERC721 via ERC165
                            _registerInterface(_INTERFACE_ID_ERC721);
                        }
                    
                        /**
                         * @dev Gets the balance of the specified address.
                         * @param owner address to query the balance of
                         * @return uint256 representing the amount owned by the passed address
                         */
                        function balanceOf(address owner) public view returns (uint256) {
                            require(owner != address(0), "ERC721: balance query for the zero address");
                    
                            return _ownedTokensCount[owner].current();
                        }
                    
                        /**
                         * @dev Gets the owner of the specified token ID.
                         * @param tokenId uint256 ID of the token to query the owner of
                         * @return address currently marked as the owner of the given token ID
                         */
                        function ownerOf(uint256 tokenId) public view returns (address) {
                            address owner = _tokenOwner[tokenId];
                            require(owner != address(0), "ERC721: owner query for nonexistent token");
                    
                            return owner;
                        }
                    
                        /**
                         * @dev Approves another address to transfer the given token ID
                         * The zero address indicates there is no approved address.
                         * There can only be one approved address per token at a given time.
                         * Can only be called by the token owner or an approved operator.
                         * @param to address to be approved for the given token ID
                         * @param tokenId uint256 ID of the token to be approved
                         */
                        function approve(address to, uint256 tokenId) public {
                            address owner = ownerOf(tokenId);
                            require(to != owner, "ERC721: approval to current owner");
                    
                            require(msg.sender == owner || isApprovedForAll(owner, msg.sender),
                                "ERC721: approve caller is not owner nor approved for all"
                            );
                    
                            _tokenApprovals[tokenId] = to;
                            emit Approval(owner, to, tokenId);
                        }
                    
                        /**
                         * @dev Gets the approved address for a token ID, or zero if no address set
                         * Reverts if the token ID does not exist.
                         * @param tokenId uint256 ID of the token to query the approval of
                         * @return address currently approved for the given token ID
                         */
                        function getApproved(uint256 tokenId) public view returns (address) {
                            require(_exists(tokenId), "ERC721: approved query for nonexistent token");
                    
                            return _tokenApprovals[tokenId];
                        }
                    
                        /**
                         * @dev Sets or unsets the approval of a given operator
                         * An operator is allowed to transfer all tokens of the sender on their behalf.
                         * @param to operator address to set the approval
                         * @param approved representing the status of the approval to be set
                         */
                        function setApprovalForAll(address to, bool approved) public {
                            require(to != msg.sender, "ERC721: approve to caller");
                    
                            _operatorApprovals[msg.sender][to] = approved;
                            emit ApprovalForAll(msg.sender, to, approved);
                        }
                    
                        /**
                         * @dev Tells whether an operator is approved by a given owner.
                         * @param owner owner address which you want to query the approval of
                         * @param operator operator address which you want to query the approval of
                         * @return bool whether the given operator is approved by the given owner
                         */
                        function isApprovedForAll(address owner, address operator) public view returns (bool) {
                            return _operatorApprovals[owner][operator];
                        }
                    
                        /**
                         * @dev Transfers the ownership of a given token ID to another address.
                         * Usage of this method is discouraged, use `safeTransferFrom` whenever possible.
                         * Requires the msg.sender to be the owner, approved, or operator.
                         * @param from current owner of the token
                         * @param to address to receive the ownership of the given token ID
                         * @param tokenId uint256 ID of the token to be transferred
                         */
                        function transferFrom(address from, address to, uint256 tokenId) public {
                            //solhint-disable-next-line max-line-length
                            require(_isApprovedOrOwner(msg.sender, tokenId), "ERC721: transfer caller is not owner nor approved");
                    
                            _transferFrom(from, to, tokenId);
                        }
                    
                        /**
                         * @dev Safely transfers the ownership of a given token ID to another address
                         * If the target address is a contract, it must implement `onERC721Received`,
                         * which is called upon a safe transfer, and return the magic value
                         * `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))`; otherwise,
                         * the transfer is reverted.
                         * Requires the msg.sender to be the owner, approved, or operator
                         * @param from current owner of the token
                         * @param to address to receive the ownership of the given token ID
                         * @param tokenId uint256 ID of the token to be transferred
                         */
                        function safeTransferFrom(address from, address to, uint256 tokenId) public {
                            safeTransferFrom(from, to, tokenId, "");
                        }
                    
                        /**
                         * @dev Safely transfers the ownership of a given token ID to another address
                         * If the target address is a contract, it must implement `onERC721Received`,
                         * which is called upon a safe transfer, and return the magic value
                         * `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))`; otherwise,
                         * the transfer is reverted.
                         * Requires the msg.sender to be the owner, approved, or operator
                         * @param from current owner of the token
                         * @param to address to receive the ownership of the given token ID
                         * @param tokenId uint256 ID of the token to be transferred
                         * @param _data bytes data to send along with a safe transfer check
                         */
                        function safeTransferFrom(address from, address to, uint256 tokenId, bytes memory _data) public {
                            transferFrom(from, to, tokenId);
                            require(_checkOnERC721Received(from, to, tokenId, _data), "ERC721: transfer to non ERC721Receiver implementer");
                        }
                    
                        /**
                         * @dev Returns whether the specified token exists.
                         * @param tokenId uint256 ID of the token to query the existence of
                         * @return bool whether the token exists
                         */
                        function _exists(uint256 tokenId) internal view returns (bool) {
                            address owner = _tokenOwner[tokenId];
                            return owner != address(0);
                        }
                    
                        /**
                         * @dev Returns whether the given spender can transfer a given token ID.
                         * @param spender address of the spender to query
                         * @param tokenId uint256 ID of the token to be transferred
                         * @return bool whether the msg.sender is approved for the given token ID,
                         * is an operator of the owner, or is the owner of the token
                         */
                        function _isApprovedOrOwner(address spender, uint256 tokenId) internal view returns (bool) {
                            require(_exists(tokenId), "ERC721: operator query for nonexistent token");
                            address owner = ownerOf(tokenId);
                            return (spender == owner || getApproved(tokenId) == spender || isApprovedForAll(owner, spender));
                        }
                    
                        /**
                         * @dev Internal function to mint a new token.
                         * Reverts if the given token ID already exists.
                         * @param to The address that will own the minted token
                         * @param tokenId uint256 ID of the token to be minted
                         */
                        function _mint(address to, uint256 tokenId) internal {
                            require(to != address(0), "ERC721: mint to the zero address");
                            require(!_exists(tokenId), "ERC721: token already minted");
                    
                            _tokenOwner[tokenId] = to;
                            _ownedTokensCount[to].increment();
                    
                            emit Transfer(address(0), to, tokenId);
                        }
                    
                        /**
                         * @dev Internal function to burn a specific token.
                         * Reverts if the token does not exist.
                         * Deprecated, use _burn(uint256) instead.
                         * @param owner owner of the token to burn
                         * @param tokenId uint256 ID of the token being burned
                         */
                        function _burn(address owner, uint256 tokenId) internal {
                            require(ownerOf(tokenId) == owner, "ERC721: burn of token that is not own");
                    
                            _clearApproval(tokenId);
                    
                            _ownedTokensCount[owner].decrement();
                            _tokenOwner[tokenId] = address(0);
                    
                            emit Transfer(owner, address(0), tokenId);
                        }
                    
                        /**
                         * @dev Internal function to burn a specific token.
                         * Reverts if the token does not exist.
                         * @param tokenId uint256 ID of the token being burned
                         */
                        function _burn(uint256 tokenId) internal {
                            _burn(ownerOf(tokenId), tokenId);
                        }
                    
                        /**
                         * @dev Internal function to transfer ownership of a given token ID to another address.
                         * As opposed to transferFrom, this imposes no restrictions on msg.sender.
                         * @param from current owner of the token
                         * @param to address to receive the ownership of the given token ID
                         * @param tokenId uint256 ID of the token to be transferred
                         */
                        function _transferFrom(address from, address to, uint256 tokenId) internal {
                            require(ownerOf(tokenId) == from, "ERC721: transfer of token that is not own");
                            require(to != address(0), "ERC721: transfer to the zero address");
                    
                            _clearApproval(tokenId);
                    
                            _ownedTokensCount[from].decrement();
                            _ownedTokensCount[to].increment();
                    
                            _tokenOwner[tokenId] = to;
                    
                            emit Transfer(from, to, tokenId);
                        }
                    
                        /**
                         * @dev Internal function to invoke `onERC721Received` on a target address.
                         * The call is not executed if the target address is not a contract.
                         * @param from address representing the previous owner of the given token ID
                         * @param to target address that will receive the tokens
                         * @param tokenId uint256 ID of the token to be transferred
                         * @param _data bytes optional data to send along with the call
                         * @return bool whether the call correctly returned the expected magic value
                         */
                        function _checkOnERC721Received(address from, address to, uint256 tokenId, bytes memory _data)
                            internal returns (bool)
                        {
                            if (!to.isContract()) {
                                return true;
                            }
                    
                            bytes4 retval = IERC721Receiver(to).onERC721Received(msg.sender, from, tokenId, _data);
                            return (retval == _ERC721_RECEIVED);
                        }
                    
                        /**
                         * @dev Private function to clear current approval of a given token ID.
                         * @param tokenId uint256 ID of the token to be transferred
                         */
                        function _clearApproval(uint256 tokenId) private {
                            if (_tokenApprovals[tokenId] != address(0)) {
                                _tokenApprovals[tokenId] = address(0);
                            }
                        }
                    }
                    
                    ////// lib/tinlake-title/src/openzeppelin-solidity/token/ERC721/IERC721Metadata.sol
                    /* pragma solidity ^0.5.0; */
                    
                    /* import "./IERC721.sol"; */
                    
                    /**
                     * @title ERC-721 Non-Fungible Token Standard, optional metadata extension
                     * @dev See https://eips.ethereum.org/EIPS/eip-721
                     */
                    contract IERC721Metadata is IERC721 {
                        function name() external view returns (string memory);
                        function symbol() external view returns (string memory);
                        function tokenURI(uint256 tokenId) external view returns (string memory);
                    }
                    
                    ////// lib/tinlake-title/src/openzeppelin-solidity/token/ERC721/ERC721Metadata.sol
                    /* pragma solidity ^0.5.0; */
                    
                    /* import "./ERC721.sol"; */
                    /* import "./IERC721Metadata.sol"; */
                    /* import "../../introspection/ERC165.sol"; */
                    
                    contract ERC721Metadata is ERC165, ERC721, IERC721Metadata {
                        // Token name
                        string private _name;
                    
                        // Token symbol
                        string private _symbol;
                    
                        // Optional mapping for token URIs
                        mapping(uint256 => string) private _tokenURIs;
                    
                        /*
                         *     bytes4(keccak256('name()')) == 0x06fdde03
                         *     bytes4(keccak256('symbol()')) == 0x95d89b41
                         *     bytes4(keccak256('tokenURI(uint256)')) == 0xc87b56dd
                         *
                         *     => 0x06fdde03 ^ 0x95d89b41 ^ 0xc87b56dd == 0x5b5e139f
                         */
                        bytes4 private constant _INTERFACE_ID_ERC721_METADATA = 0x5b5e139f;
                    
                        /**
                         * @dev Constructor function
                         */
                        constructor (string memory name, string memory symbol) public {
                            _name = name;
                            _symbol = symbol;
                    
                            // register the supported interfaces to conform to ERC721 via ERC165
                            _registerInterface(_INTERFACE_ID_ERC721_METADATA);
                        }
                    
                        /**
                         * @dev Gets the token name.
                         * @return string representing the token name
                         */
                        function name() external view returns (string memory) {
                            return _name;
                        }
                    
                        /**
                         * @dev Gets the token symbol.
                         * @return string representing the token symbol
                         */
                        function symbol() external view returns (string memory) {
                            return _symbol;
                        }
                    
                        /**
                         * @dev Returns an URI for a given token ID.
                         * Throws if the token ID does not exist. May return an empty string.
                         * @param tokenId uint256 ID of the token to query
                         */
                        function tokenURI(uint256 tokenId) external view returns (string memory) {
                            require(_exists(tokenId), "ERC721Metadata: URI query for nonexistent token");
                            return _tokenURIs[tokenId];
                        }
                    
                        /**
                         * @dev Internal function to set the token URI for a given token.
                         * Reverts if the token ID does not exist.
                         * @param tokenId uint256 ID of the token to set its URI
                         * @param uri string URI to assign
                         */
                        function _setTokenURI(uint256 tokenId, string memory uri) internal {
                            require(_exists(tokenId), "ERC721Metadata: URI set of nonexistent token");
                            _tokenURIs[tokenId] = uri;
                        }
                    
                        /**
                         * @dev Internal function to burn a specific token.
                         * Reverts if the token does not exist.
                         * Deprecated, use _burn(uint256) instead.
                         * @param owner owner of the token to burn
                         * @param tokenId uint256 ID of the token being burned by the msg.sender
                         */
                        function _burn(address owner, uint256 tokenId) internal {
                            super._burn(owner, tokenId);
                    
                            // Clear metadata (if any)
                            if (bytes(_tokenURIs[tokenId]).length != 0) {
                                delete _tokenURIs[tokenId];
                            }
                        }
                    }
                    
                    ////// lib/tinlake-title/src/title.sol
                    // title.sol -- NFT to manage access rights to contracts
                    // Copyright (C) 2019 lucasvo
                    
                    // 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.15 <0.6.0; */
                    
                    /* import { ERC721Metadata } from "./openzeppelin-solidity/token/ERC721/ERC721Metadata.sol"; */
                    /* import { Auth } from "tinlake-auth/auth.sol"; */
                    
                    contract Title is Auth, ERC721Metadata {
                        // --- Data ---
                        uint public count;
                        string public uri;
                    
                        constructor (string memory name, string memory symbol) ERC721Metadata(name, symbol) public {
                            wards[msg.sender] = 1;
                            count = 1;
                        }
                    
                        // --- Title ---
                        function issue (address usr) public auth returns (uint) {
                            return _issue(usr);
                        }
                    
                        function _issue (address usr) internal returns (uint) {
                            _mint(usr, count);
                            count += 1; // can't overflow, not enough gas in the world to pay for 2**256 nfts.
                            return count-1;
                        }
                    
                        function close (uint tkn) public auth {
                            _burn(tkn);
                        }
                    }
                    
                    contract TitleLike_1 {
                        function ownerOf (uint) public returns (address);
                    }
                    
                    contract TitleOwned {
                        TitleLike_1 title;
                        constructor (address title_) public {
                            title = TitleLike_1(title_);
                        }
                    
                        modifier owner (uint loan) { require(title.ownerOf(loan) == msg.sender); _; }
                    }
                    
                    ////// src/borrower/shelf.sol
                    // shelf.sol -- keeps track and owns NFTs
                    // Copyright (C) 2019 lucasvo
                    
                    // 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.15 <0.6.0; */
                    
                    /* import "ds-note/note.sol"; */
                    /* import "tinlake-math/math.sol"; */
                    /* import "tinlake-auth/auth.sol"; */
                    /* import "ds-test/test.sol"; */
                    /* import { TitleOwned } from "tinlake-title/title.sol"; */
                    
                    contract NFTLike_2 {
                        function ownerOf(uint256 tokenId) public view returns (address owner);
                        function transferFrom(address from, address to, uint256 tokenId) public;
                    }
                    
                    contract TitleLike_2 {
                        function issue(address) public returns (uint);
                        function close(uint) public;
                        function ownerOf (uint) public returns (address);
                    }
                    
                    contract TokenLike_1 {
                        uint public totalSupply;
                        function balanceOf(address) public view returns (uint);
                        function transferFrom(address,address,uint) public returns (bool);
                        function approve(address, uint) public;
                    }
                    
                    contract PileLike_3 {
                        uint public total;
                        function debt(uint) public returns (uint);
                        function accrue(uint) public;
                        function incDebt(uint, uint) public;
                        function decDebt(uint, uint) public;
                    }
                    
                    contract CeilingLike {
                        function borrow(uint loan, uint currencyAmount) public;
                        function repay(uint loan, uint currencyAmount) public;
                    }
                    
                    contract DistributorLike_2 {
                        function balance() public;
                    }
                    
                    contract SubscriberLike {
                        function borrowEvent(uint loan) public;
                        function unlockEvent(uint loan) public;
                    }
                    
                    contract Shelf is DSNote, Auth, TitleOwned, Math {
                    
                        // --- Data ---
                        TitleLike_2 public title;
                        CeilingLike public ceiling;
                        PileLike_3 public pile;
                        TokenLike_1 public currency;
                        DistributorLike_2 public distributor;
                        SubscriberLike public subscriber;
                    
                        struct Loan {
                            address registry;
                            uint256 tokenId;
                        }
                    
                        mapping (uint => uint) public balances;
                        mapping (uint => Loan) public shelf;
                        mapping (bytes32 => uint) public nftlookup;
                    
                        uint public balance;
                        address public lender;
                    
                        constructor(address currency_, address title_, address pile_, address ceiling_) TitleOwned(title_) public {
                            wards[msg.sender] = 1;
                            currency = TokenLike_1(currency_);
                            title = TitleLike_2(title_);
                            pile = PileLike_3(pile_);
                            ceiling = CeilingLike(ceiling_);
                        }
                    
                    
                        /// sets the dependency to another contract
                        function depend(bytes32 contractName, address addr) external auth {
                            if (contractName == "lender") {
                                currency.approve(lender, uint(0));
                                currency.approve(addr, uint(-1));
                                lender = addr;
                            }
                            else if (contractName == "token") { currency = TokenLike_1(addr); }
                            else if (contractName == "title") { title = TitleLike_2(addr); }
                            else if (contractName == "pile") { pile = PileLike_3(addr); }
                            else if (contractName == "ceiling") { ceiling = CeilingLike(addr); }
                            else if (contractName == "distributor") { distributor = DistributorLike_2(addr);}
                            else if (contractName == "subscriber") { subscriber = SubscriberLike(addr);}
                            else revert();
                        }
                    
                        function token(uint loan) public view returns (address registry, uint nft) {
                            return (shelf[loan].registry, shelf[loan].tokenId);
                        }
                    
                        /// issues a new loan in Tinlake - it requires the ownership of an nft
                        /// first step in the loan process - everyone could add an nft
                        function issue(address registry_, uint token_) external note returns (uint) {
                            require(NFTLike_2(registry_).ownerOf(token_) == msg.sender, "nft-not-owned");
                            bytes32 nft = keccak256(abi.encodePacked(registry_, token_));
                            require(nftlookup[nft] == 0, "nft-in-use");
                            uint loan = title.issue(msg.sender);
                            nftlookup[nft] = loan;
                            shelf[loan].registry = registry_;
                            shelf[loan].tokenId = token_;
                    
                            return loan;
                        }
                    
                        function close(uint loan) external note{
                            require(pile.debt(loan) == 0, "loan-has-outstanding-debt");
                            require(!nftLocked(loan), "nft-not-locked");
                            (address registry, uint tokenId) = token(loan);
                            require(title.ownerOf(loan) == msg.sender || NFTLike_2(registry).ownerOf(tokenId) == msg.sender, "not-loan-or-nft-owner");
                            title.close(loan);
                            bytes32 nft = keccak256(abi.encodePacked(shelf[loan].registry, shelf[loan].tokenId));
                            nftlookup[nft] = 0;
                            resetLoanBalance(loan);
                        }
                    
                        /// used by the lender contracts to know if currency is needed or currency can be taken
                        function balanceRequest() external view returns (bool, uint) {
                            uint currencyBalance = currency.balanceOf(address(this));
                            if (balance > currencyBalance) {
                                return (true, safeSub(balance, currencyBalance));
                    
                            } else {
                                return (false, safeSub(currencyBalance, balance));
                            }
                        }
                    
                        /// starts the borrow process of a loan
                        /// informs the system of the requested currencyAmount
                        /// interest accumulation starts with this method
                        /// the method can only be called if the nft is locked
                        /// a max ceiling needs to be defined by an oracle
                        function borrow(uint loan, uint currencyAmount) external owner(loan) note {
                            require(nftLocked(loan), "nft-not-locked");
                            if(address(subscriber) != address(0)) {
                                subscriber.borrowEvent(loan);
                            }
                            pile.accrue(loan);
                            ceiling.borrow(loan, currencyAmount);
                            pile.incDebt(loan, currencyAmount);
                            balances[loan] = safeAdd(balances[loan], currencyAmount);
                            balance = safeAdd(balance, currencyAmount);
                        }
                    
                    
                        /// transfers the requested currencyAmount to the address of the loan owner
                        /// the method triggers the distributor to ensure the shelf has enough currency
                        function withdraw(uint loan, uint currencyAmount, address usr) external owner(loan) note {
                            require(nftLocked(loan), "nft-not-locked");
                            require(currencyAmount <= balances[loan], "withdraw-amount-too-high");
                    
                            distributor.balance();
                            balances[loan] = safeSub(balances[loan], currencyAmount);
                            balance = safeSub(balance, currencyAmount);
                            require(currency.transferFrom(address(this), usr, currencyAmount), "currency-transfer-failed");
                        }
                    
                        /// repays the entire or partial debt of a loan
                        function repay(uint loan, uint currencyAmount) external owner(loan) note {
                            require(nftLocked(loan), "nft-not-locked");
                            require(balances[loan] == 0, "withdraw-required-before-repay");
                            _repay(loan, msg.sender, currencyAmount);
                        }
                    
                        /// a collector can recover defaulted loans
                        /// it is not required to recover the entire loan debt
                        function recover(uint loan, address usr, uint currencyAmount) external auth note {
                            pile.accrue(loan);
                    
                            uint loanDebt = pile.debt(loan);
                    
                            require(currency.transferFrom(usr, address(this), currencyAmount), "currency-transfer-failed");
                    
                            ceiling.repay(loan, loanDebt);
                            // sets loan debt to 0
                            pile.decDebt(loan, loanDebt);
                            resetLoanBalance(loan);
                            distributor.balance();
                        }
                    
                        function _repay(uint loan, address usr, uint currencyAmount) internal {
                            pile.accrue(loan);
                            uint loanDebt = pile.debt(loan);
                            
                            // only repay max loan debt
                            if (currencyAmount > loanDebt) {
                                currencyAmount = loanDebt;
                            }
                            require(currency.transferFrom(usr, address(this), currencyAmount), "currency-transfer-failed");
                            ceiling.repay(loan, currencyAmount);
                            pile.decDebt(loan, currencyAmount);
                            distributor.balance();
                        }
                    
                        /// locks an nft in the shelf
                        /// requires an issued loan
                        function lock(uint loan) external owner(loan) note {
                            if(address(subscriber) != address(0)) {
                                subscriber.unlockEvent(loan);
                            }
                            NFTLike_2(shelf[loan].registry).transferFrom(msg.sender, address(this), shelf[loan].tokenId);
                        }
                    
                        /// unlocks an nft in the shelf
                        /// requires zero debt
                        function unlock(uint loan) external owner(loan) note {
                            require(pile.debt(loan) == 0, "loan-has-outstanding-debt");
                            NFTLike_2(shelf[loan].registry).transferFrom(address(this), msg.sender, shelf[loan].tokenId);
                        }
                    
                        function nftLocked(uint loan) public view returns (bool) {
                            return NFTLike_2(shelf[loan].registry).ownerOf(shelf[loan].tokenId) == address(this);
                        }
                    
                        /// a loan can be claimed by a collector if the loan debt is above the loan threshold
                        /// transfers the nft to the collector
                        function claim(uint loan, address usr) public auth note {
                            NFTLike_2(shelf[loan].registry).transferFrom(address(this), usr, shelf[loan].tokenId);
                        }
                    
                        function resetLoanBalance(uint loan) internal {
                            uint loanBalance = balances[loan];
                            if (loanBalance  > 0) {
                                balances[loan] = 0;
                                balance = safeSub(balance, loanBalance);
                            }
                        }
                    }
                    

                    File 2 of 7: Pile
                    // Verified using https://dapp.tools
                    
                    // hevm: flattened sources of src/borrower/pile.sol
                    pragma solidity >=0.5.15 >=0.5.15 <0.6.0;
                    
                    ////// lib/tinlake-auth/lib/ds-note/src/note.sol
                    /// note.sol -- the `note' modifier, for logging calls as events
                    
                    // 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.15; */
                    
                    contract DSNote {
                        event LogNote(
                            bytes4   indexed  sig,
                            address  indexed  guy,
                            bytes32  indexed  foo,
                            bytes32  indexed  bar,
                            uint256           wad,
                            bytes             fax
                        ) anonymous;
                    
                        modifier note {
                            bytes32 foo;
                            bytes32 bar;
                            uint256 wad;
                    
                            assembly {
                                foo := calldataload(4)
                                bar := calldataload(36)
                                wad := callvalue()
                            }
                    
                            _;
                    
                            emit LogNote(msg.sig, msg.sender, foo, bar, wad, msg.data);
                        }
                    }
                    
                    ////// lib/tinlake-auth/src/auth.sol
                    // Copyright (C) Centrifuge 2020, based on MakerDAO dss https://github.com/makerdao/dss
                    //
                    // 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.15 <0.6.0; */
                    
                    /* import "ds-note/note.sol"; */
                    
                    contract Auth is DSNote {
                        mapping (address => uint) public wards;
                        function rely(address usr) public auth note { wards[usr] = 1; }
                        function deny(address usr) public auth note { wards[usr] = 0; }
                        modifier auth { require(wards[msg.sender] == 1); _; }
                    }
                    
                    ////// lib/tinlake-math/src/math.sol
                    // Copyright (C) 2018 Rain <[email protected]>
                    //
                    // 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.15 <0.6.0; */
                    
                    contract Math {
                        uint256 constant ONE = 10 ** 27;
                    
                        function safeAdd(uint x, uint y) public pure returns (uint z) {
                            require((z = x + y) >= x, "safe-add-failed");
                        }
                    
                        function safeSub(uint x, uint y) public pure returns (uint z) {
                            require((z = x - y) <= x, "safe-sub-failed");
                        }
                    
                        function safeMul(uint x, uint y) public pure returns (uint z) {
                            require(y == 0 || (z = x * y) / y == x, "safe-mul-failed");
                        }
                    
                        function safeDiv(uint x, uint y) public pure returns (uint z) {
                            z = x / y;
                        }
                    
                        function rmul(uint x, uint y) public pure returns (uint z) {
                            z = safeMul(x, y) / ONE;
                        }
                    
                        function rdiv(uint x, uint y) public pure returns (uint z) {
                            require(y > 0, "division by zero");
                            z = safeAdd(safeMul(x, ONE), y / 2) / y;
                        }
                    
                        function rdivup(uint x, uint y) internal pure returns (uint z) {
                            require(y > 0, "division by zero");
                            // always rounds up
                            z = safeAdd(safeMul(x, ONE), safeSub(y, 1)) / y;
                        }
                    
                    
                    }
                    
                    ////// lib/tinlake-math/src/interest.sol
                    // Copyright (C) 2018 Rain <[email protected]> and Centrifuge, referencing MakerDAO dss => https://github.com/makerdao/dss/blob/master/src/pot.sol
                    //
                    // 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.15 <0.6.0; */
                    
                    /* import "./math.sol"; */
                    
                    contract Interest is Math {
                        // @notice This function provides compounding in seconds
                        // @param chi Accumulated interest rate over time
                        // @param ratePerSecond Interest rate accumulation per second in RAD(10ˆ27)
                        // @param lastUpdated When the interest rate was last updated
                        // @param pie Total sum of all amounts accumulating under one interest rate, divided by that rate
                        // @return The new accumulated rate, as well as the difference between the debt calculated with the old and new accumulated rates.
                        function compounding(uint chi, uint ratePerSecond, uint lastUpdated, uint pie) public view returns (uint, uint) {
                            require(block.timestamp >= lastUpdated, "tinlake-math/invalid-timestamp");
                            require(chi != 0);
                            // instead of a interestBearingAmount we use a accumulated interest rate index (chi)
                            uint updatedChi = _chargeInterest(chi ,ratePerSecond, lastUpdated, block.timestamp);
                            return (updatedChi, safeSub(rmul(updatedChi, pie), rmul(chi, pie)));
                        }
                    
                        // @notice This function charge interest on a interestBearingAmount
                        // @param interestBearingAmount is the interest bearing amount
                        // @param ratePerSecond Interest rate accumulation per second in RAD(10ˆ27)
                        // @param lastUpdated last time the interest has been charged
                        // @return interestBearingAmount + interest
                        function chargeInterest(uint interestBearingAmount, uint ratePerSecond, uint lastUpdated) public view returns (uint) {
                            if (block.timestamp >= lastUpdated) {
                                interestBearingAmount = _chargeInterest(interestBearingAmount, ratePerSecond, lastUpdated, block.timestamp);
                            }
                            return interestBearingAmount;
                        }
                    
                        function _chargeInterest(uint interestBearingAmount, uint ratePerSecond, uint lastUpdated, uint current) internal pure returns (uint) {
                            return rmul(rpow(ratePerSecond, current - lastUpdated, ONE), interestBearingAmount);
                        }
                    
                    
                        // convert pie to debt/savings amount
                        function toAmount(uint chi, uint pie) public pure returns (uint) {
                            return rmul(pie, chi);
                        }
                    
                        // convert debt/savings amount to pie
                        function toPie(uint chi, uint amount) public pure returns (uint) {
                            return rdivup(amount, chi);
                        }
                    
                        function rpow(uint x, uint n, uint base) public pure returns (uint z) {
                            assembly {
                                switch x case 0 {switch n case 0 {z := base} default {z := 0}}
                                default {
                                    switch mod(n, 2) case 0 { z := base } default { z := x }
                                    let half := div(base, 2)  // for rounding.
                                    for { n := div(n, 2) } n { n := div(n,2) } {
                                    let xx := mul(x, x)
                                    if iszero(eq(div(xx, x), x)) { revert(0,0) }
                                    let xxRound := add(xx, half)
                                    if lt(xxRound, xx) { revert(0,0) }
                                    x := div(xxRound, base)
                                    if mod(n,2) {
                                        let zx := mul(z, x)
                                        if and(iszero(iszero(x)), iszero(eq(div(zx, x), z))) { revert(0,0) }
                                        let zxRound := add(zx, half)
                                        if lt(zxRound, zx) { revert(0,0) }
                                        z := div(zxRound, base)
                                    }
                                }
                                }
                            }
                        }
                    }
                    
                    ////// src/borrower/pile.sol
                    // Copyright (C) 2018  Rain <[email protected]>, Centrifuge
                    //
                    // 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.15 <0.6.0; */
                    
                    /* import "ds-note/note.sol"; */
                    /* import "tinlake-math/interest.sol"; */
                    /* import "tinlake-auth/auth.sol"; */
                    
                    // ## Interest Group based Pile
                    // The following is one implementation of a debt module. It keeps track of different buckets of interest rates and is optimized for many loans per interest bucket. It keeps track of interest
                    // rate accumulators (chi values) for all interest rate categories. It calculates debt each
                    // loan according to its interest rate category and pie value.
                    contract Pile is DSNote, Auth, Interest {
                        // --- Data ---
                    
                        /// stores all needed information of an interest rate group
                        struct Rate {
                            uint   pie;                 // Total debt of all loans with this rate
                            uint   chi;                 // Accumulated rates
                            uint   ratePerSecond;       // Accumulation per second
                            uint48 lastUpdated;         // Last time the rate was accumulated
                            uint   fixedRate;           // fixed rate applied to each loan of the group
                        }
                    
                        /// Interest Rate Groups are identified by a `uint` and stored in a mapping
                        mapping (uint => Rate) public rates;
                    
                        /// mapping of all loan debts
                        /// the debt is stored as pie
                        /// pie is defined as pie = debt/chi therefore debt = pie * chi
                        /// where chi is the accumulated interest rate index over time
                        mapping (uint => uint) public pie;
                        /// loan => rate
                        mapping (uint => uint) public loanRates;
                    
                    
                        /// total debt of all ongoing loans
                        uint public total;
                    
                        constructor() public {
                            wards[msg.sender] = 1;
                            /// pre-definition for loans without interest rates
                            rates[0].chi = ONE;
                            rates[0].ratePerSecond = ONE;
                        }
                    
                         // --- Public Debt Methods  ---
                        /// increases the debt of a loan by a currencyAmount
                        /// a change of the loan debt updates the rate debt and total debt
                        function incDebt(uint loan, uint currencyAmount) external auth note { 
                            uint rate = loanRates[loan];
                            require(now == rates[rate].lastUpdated, "rate-group-not-updated");
                            currencyAmount = safeAdd(currencyAmount, rmul(currencyAmount, rates[rate].fixedRate));
                            uint pieAmount = toPie(rates[rate].chi, currencyAmount);
                    
                            pie[loan] = safeAdd(pie[loan], pieAmount);
                            rates[rate].pie = safeAdd(rates[rate].pie, pieAmount);
                            total = safeAdd(total, currencyAmount);
                        }
                    
                        /// decrease the loan's debt by a currencyAmount
                        /// a change of the loan debt updates the rate debt and total debt
                        function decDebt(uint loan, uint currencyAmount) external auth note {
                            uint rate = loanRates[loan];
                            require(now == rates[rate].lastUpdated, "rate-group-not-updated");
                            uint pieAmount = toPie(rates[rate].chi, currencyAmount);
                    
                            pie[loan] = safeSub(pie[loan], pieAmount);
                            rates[rate].pie = safeSub(rates[rate].pie, pieAmount);
                    
                            if (currencyAmount > total) {
                                total = 0;
                                return;
                            }
                    
                            total = safeSub(total, currencyAmount);
                        }
                    
                        /// returns the current debt based on actual block.timestamp (now)
                        function debt(uint loan) external view returns (uint) {
                            uint rate_ = loanRates[loan];
                            uint chi_ = rates[rate_].chi;
                            if (now >= rates[rate_].lastUpdated) {
                                chi_ = chargeInterest(rates[rate_].chi, rates[rate_].ratePerSecond, rates[rate_].lastUpdated);
                            }
                            return toAmount(chi_, pie[loan]);
                        }
                    
                        /// returns the total debt of a interest rate group
                        function rateDebt(uint rate) external view returns (uint) {
                            uint chi_ = rates[rate].chi;
                            uint pie_ = rates[rate].pie;
                    
                            if (now >= rates[rate].lastUpdated) {
                                chi_ = chargeInterest(rates[rate].chi, rates[rate].ratePerSecond, rates[rate].lastUpdated);
                            }
                            return toAmount(chi_, pie_);
                        }
                    
                        // --- Interest Rate Group Implementation ---
                    
                        // set rate loanRates for a loan
                        function setRate(uint loan, uint rate) external auth note {
                            require(pie[loan] == 0, "non-zero-debt");
                            // rate category has to be initiated
                            require(rates[rate].chi != 0, "rate-group-not-set");
                            loanRates[loan] = rate;
                        }
                    
                        // change rate loanRates for a loan
                        function changeRate(uint loan, uint newRate) external auth note {
                            require(rates[newRate].chi != 0, "rate-group-not-set");
                            uint currentRate = loanRates[loan];
                            drip(currentRate);
                            drip(newRate);
                            uint pie_ = pie[loan];
                            uint debt_ = toAmount(rates[currentRate].chi, pie_);
                            rates[currentRate].pie = safeSub(rates[currentRate].pie, pie_);
                            pie[loan] = toPie(rates[newRate].chi, debt_);
                            rates[newRate].pie = safeAdd(rates[newRate].pie, pie[loan]);
                            loanRates[loan] = newRate;
                        }
                    
                        // set/change the interest rate of a rate category
                        function file(bytes32 what, uint rate, uint value) external auth note {
                            if (what == "rate") {
                                require(value != 0, "rate-per-second-can-not-be-0");
                                if (rates[rate].chi == 0) {
                                    rates[rate].chi = ONE;
                                    rates[rate].lastUpdated = uint48(now);
                                } else {
                                    drip(rate);
                                } 
                                rates[rate].ratePerSecond = value;
                            } else if (what == "fixedRate") {
                                rates[rate].fixedRate = value;
                            } else revert("unknown parameter");
                        }
                    
                        // accrue needs to be called before any debt amounts are modified by an external component
                        function accrue(uint loan) external {
                            drip(loanRates[loan]);
                        }
                    
                        // drip updates the chi of the rate category by compounding the interest and
                        // updates the total debt
                        function drip(uint rate) public {        
                            if (now >= rates[rate].lastUpdated) {
                                (uint chi, uint deltaInterest) = compounding(rates[rate].chi, rates[rate].ratePerSecond, rates[rate].lastUpdated, rates[rate].pie);
                                rates[rate].chi = chi;
                                rates[rate].lastUpdated = uint48(now);
                                total = safeAdd(total, deltaInterest);
                            }
                        }
                    }
                    

                    File 3 of 7: Reserve
                    // Verified using https://dapp.tools
                    
                    // hevm: flattened sources of src/lender/reserve.sol
                    pragma solidity >=0.5.15 >=0.5.15 <0.6.0;
                    
                    ////// lib/tinlake-auth/lib/ds-note/src/note.sol
                    /// note.sol -- the `note' modifier, for logging calls as events
                    
                    // 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.15; */
                    
                    contract DSNote {
                        event LogNote(
                            bytes4   indexed  sig,
                            address  indexed  guy,
                            bytes32  indexed  foo,
                            bytes32  indexed  bar,
                            uint256           wad,
                            bytes             fax
                        ) anonymous;
                    
                        modifier note {
                            bytes32 foo;
                            bytes32 bar;
                            uint256 wad;
                    
                            assembly {
                                foo := calldataload(4)
                                bar := calldataload(36)
                                wad := callvalue()
                            }
                    
                            _;
                    
                            emit LogNote(msg.sig, msg.sender, foo, bar, wad, msg.data);
                        }
                    }
                    
                    ////// lib/tinlake-auth/src/auth.sol
                    // Copyright (C) Centrifuge 2020, based on MakerDAO dss https://github.com/makerdao/dss
                    //
                    // 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.15 <0.6.0; */
                    
                    /* import "ds-note/note.sol"; */
                    
                    contract Auth is DSNote {
                        mapping (address => uint) public wards;
                        function rely(address usr) public auth note { wards[usr] = 1; }
                        function deny(address usr) public auth note { wards[usr] = 0; }
                        modifier auth { require(wards[msg.sender] == 1); _; }
                    }
                    
                    ////// lib/tinlake-math/src/math.sol
                    // Copyright (C) 2018 Rain <[email protected]>
                    //
                    // 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.15 <0.6.0; */
                    
                    contract Math {
                        uint256 constant ONE = 10 ** 27;
                    
                        function safeAdd(uint x, uint y) public pure returns (uint z) {
                            require((z = x + y) >= x, "safe-add-failed");
                        }
                    
                        function safeSub(uint x, uint y) public pure returns (uint z) {
                            require((z = x - y) <= x, "safe-sub-failed");
                        }
                    
                        function safeMul(uint x, uint y) public pure returns (uint z) {
                            require(y == 0 || (z = x * y) / y == x, "safe-mul-failed");
                        }
                    
                        function safeDiv(uint x, uint y) public pure returns (uint z) {
                            z = x / y;
                        }
                    
                        function rmul(uint x, uint y) public pure returns (uint z) {
                            z = safeMul(x, y) / ONE;
                        }
                    
                        function rdiv(uint x, uint y) public pure returns (uint z) {
                            require(y > 0, "division by zero");
                            z = safeAdd(safeMul(x, ONE), y / 2) / y;
                        }
                    
                        function rdivup(uint x, uint y) internal pure returns (uint z) {
                            require(y > 0, "division by zero");
                            // always rounds up
                            z = safeAdd(safeMul(x, ONE), safeSub(y, 1)) / y;
                        }
                    
                    
                    }
                    
                    ////// src/lender/reserve.sol
                    /* pragma solidity >=0.5.15 <0.6.0; */
                    
                    /* import "tinlake-math/math.sol"; */
                    /* import "tinlake-auth/auth.sol"; */
                    
                    contract ERC20Like_2 {
                        function balanceOf(address) public view returns (uint256);
                    
                        function transferFrom(
                            address,
                            address,
                            uint256
                        ) public returns (bool);
                    
                        function mint(address, uint256) public;
                    
                        function burn(address, uint256) public;
                    
                        function totalSupply() public view returns (uint256);
                    }
                    
                    contract ShelfLike_3 {
                        function balanceRequest() public returns (bool requestWant, uint256 amount);
                    }
                    
                    contract AssessorLike_4 {
                        function repaymentUpdate(uint amount) public;
                        function borrowUpdate(uint amount) public;
                    }
                    
                    // The reserve keeps track of the currency and the bookkeeping
                    // of the total balance
                    contract Reserve is Math, Auth {
                        ERC20Like_2 public currency;
                        ShelfLike_3 public shelf;
                        AssessorLike_4 public assessor;
                    
                        // currency available for borrowing new loans
                        uint256 public currencyAvailable;
                    
                        // address or contract which holds the currency
                        // by default it is address(this)
                        address pot;
                    
                        // total currency in the reserve
                        uint public balance_;
                    
                        constructor(address currency_) public {
                            wards[msg.sender] = 1;
                            currency = ERC20Like_2(currency_);
                            pot = address(this);
                        }
                    
                        function file(bytes32 what, uint amount) public auth {
                            if (what == "currencyAvailable") {
                                currencyAvailable = amount;
                            } else revert();
                        }
                    
                        function depend(bytes32 contractName, address addr) public auth {
                            if (contractName == "shelf") {
                                shelf = ShelfLike_3(addr);
                            } else if (contractName == "currency") {
                                currency = ERC20Like_2(addr);
                            } else if (contractName == "assessor") {
                                assessor = AssessorLike_4(addr);
                            } else if (contractName == "pot") {
                                pot = addr;
                            } else revert();
                        }
                    
                        function totalBalance() public view returns (uint) {
                            return balance_;
                        }
                    
                        // deposits currency in the the reserve
                        function deposit(uint currencyAmount) public auth {
                            _deposit(msg.sender, currencyAmount);
                        }
                    
                        function _deposit(address usr, uint currencyAmount) internal {
                            require(currency.transferFrom(usr, pot, currencyAmount), "reserve-deposit-failed");
                            balance_ = safeAdd(balance_, currencyAmount);
                        }
                    
                        // remove currency from the reserve
                        function payout(uint currencyAmount) public auth {
                            _payout(msg.sender, currencyAmount);
                        }
                    
                        function _payout(address usr, uint currencyAmount)  internal {
                          require(currency.transferFrom(pot, usr, currencyAmount), "reserve-payout-failed");
                          balance_ = safeSub(balance_, currencyAmount);
                        }
                    
                        // balance handles currency requests from the borrower side
                        // currency is moved between shelf and reserve if needed
                        function balance() public {
                            (bool requestWant, uint256 currencyAmount) = shelf.balanceRequest();
                            if (requestWant) {
                                require(
                                    currencyAvailable >= currencyAmount,
                                    "not-enough-currency-reserve"
                                );
                    
                                currencyAvailable = safeSub(currencyAvailable, currencyAmount);
                                _payout(address(shelf), currencyAmount);
                                assessor.borrowUpdate(currencyAmount);
                                return;
                            }
                            _deposit(address(shelf), currencyAmount);
                            assessor.repaymentUpdate(currencyAmount);
                        }
                    }
                    

                    File 4 of 7: 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);
                        }
                    }

                    File 5 of 7: Title
                    // Verified using https://dapp.tools
                    
                    // hevm: flattened sources of lib/tinlake-title/src/title.sol
                    pragma solidity >=0.5.15 >=0.5.0 <0.6.0 >=0.5.15 <0.6.0;
                    
                    ////// lib/tinlake-auth/lib/ds-note/src/note.sol
                    /// note.sol -- the `note' modifier, for logging calls as events
                    
                    // 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.15; */
                    
                    contract DSNote {
                        event LogNote(
                            bytes4   indexed  sig,
                            address  indexed  guy,
                            bytes32  indexed  foo,
                            bytes32  indexed  bar,
                            uint256           wad,
                            bytes             fax
                        ) anonymous;
                    
                        modifier note {
                            bytes32 foo;
                            bytes32 bar;
                            uint256 wad;
                    
                            assembly {
                                foo := calldataload(4)
                                bar := calldataload(36)
                                wad := callvalue()
                            }
                    
                            _;
                    
                            emit LogNote(msg.sig, msg.sender, foo, bar, wad, msg.data);
                        }
                    }
                    
                    ////// lib/tinlake-auth/src/auth.sol
                    // Copyright (C) Centrifuge 2020, based on MakerDAO dss https://github.com/makerdao/dss
                    //
                    // 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.15 <0.6.0; */
                    
                    /* import "ds-note/note.sol"; */
                    
                    contract Auth is DSNote {
                        mapping (address => uint) public wards;
                        function rely(address usr) public auth note { wards[usr] = 1; }
                        function deny(address usr) public auth note { wards[usr] = 0; }
                        modifier auth { require(wards[msg.sender] == 1); _; }
                    }
                    
                    ////// lib/tinlake-title/src/openzeppelin-solidity/math/SafeMath.sol
                    /* pragma solidity ^0.5.0; */
                    
                    /**
                     * @title SafeMath
                     * @dev Unsigned math operations with safety checks that revert on error.
                     */
                    library SafeMath {
                        /**
                         * @dev Multiplies two unsigned integers, reverts on overflow.
                         */
                        function mul(uint256 a, uint256 b) internal pure returns (uint256) {
                            // Gas optimization: this is cheaper than requiring 'a' not being zero, but the
                            // benefit is lost if 'b' is also tested.
                            // See: https://github.com/OpenZeppelin/openzeppelin-solidity/pull/522
                            if (a == 0) {
                                return 0;
                            }
                    
                            uint256 c = a * b;
                            require(c / a == b, "SafeMath: multiplication overflow");
                    
                            return c;
                        }
                    
                        /**
                         * @dev Integer division of two unsigned integers truncating the quotient, reverts on division by zero.
                         */
                        function div(uint256 a, uint256 b) internal pure returns (uint256) {
                            // Solidity only automatically asserts when dividing by 0
                            require(b > 0, "SafeMath: division by zero");
                            uint256 c = a / b;
                            // assert(a == b * c + a % b); // There is no case in which this doesn't hold
                    
                            return c;
                        }
                    
                        /**
                         * @dev Subtracts two unsigned integers, reverts on overflow (i.e. if subtrahend is greater than minuend).
                         */
                        function sub(uint256 a, uint256 b) internal pure returns (uint256) {
                            require(b <= a, "SafeMath: subtraction overflow");
                            uint256 c = a - b;
                    
                            return c;
                        }
                    
                        /**
                         * @dev Adds two unsigned integers, reverts on overflow.
                         */
                        function add(uint256 a, uint256 b) internal pure returns (uint256) {
                            uint256 c = a + b;
                            require(c >= a, "SafeMath: addition overflow");
                    
                            return c;
                        }
                    
                        /**
                         * @dev Divides two unsigned integers and returns the remainder (unsigned integer modulo),
                         * reverts when dividing by zero.
                         */
                        function mod(uint256 a, uint256 b) internal pure returns (uint256) {
                            require(b != 0, "SafeMath: modulo by zero");
                            return a % b;
                        }
                    }
                    
                    ////// lib/tinlake-title/src/openzeppelin-solidity/drafts/Counters.sol
                    /* pragma solidity ^0.5.0; */
                    
                    /* import "../math/SafeMath.sol"; */
                    
                    /**
                     * @title Counters
                     * @author Matt Condon (@shrugs)
                     * @dev Provides counters that can only be incremented or decremented by one. This can be used e.g. to track the number
                     * of elements in a mapping, issuing ERC721 ids, or counting request ids.
                     *
                     * Include with `using Counters for Counters.Counter;`
                     * Since it is not possible to overflow a 256 bit integer with increments of one, `increment` can skip the SafeMath
                     * overflow check, thereby saving gas. This does assume however correct usage, in that the underlying `_value` is never
                     * directly accessed.
                     */
                    library Counters {
                        using SafeMath for uint256;
                    
                        struct Counter {
                            // This variable should never be directly accessed by users of the library: interactions must be restricted to
                            // the library's function. As of Solidity v0.5.2, this cannot be enforced, though there is a proposal to add
                            // this feature: see https://github.com/ethereum/solidity/issues/4637
                            uint256 _value; // default: 0
                        }
                    
                        function current(Counter storage counter) internal view returns (uint256) {
                            return counter._value;
                        }
                    
                        function increment(Counter storage counter) internal {
                            counter._value += 1;
                        }
                    
                        function decrement(Counter storage counter) internal {
                            counter._value = counter._value.sub(1);
                        }
                    }
                    
                    ////// lib/tinlake-title/src/openzeppelin-solidity/introspection/IERC165.sol
                    /* pragma solidity ^0.5.0; */
                    
                    /**
                     * @title IERC165
                     * @dev https://eips.ethereum.org/EIPS/eip-165
                     */
                    interface IERC165 {
                        /**
                         * @notice Query if a contract implements an interface
                         * @param interfaceId The interface identifier, as specified in ERC-165
                         * @dev Interface identification is specified in ERC-165. This function
                         * uses less than 30,000 gas.
                         */
                        function supportsInterface(bytes4 interfaceId) external view returns (bool);
                    }
                    
                    ////// lib/tinlake-title/src/openzeppelin-solidity/introspection/ERC165.sol
                    /* pragma solidity ^0.5.0; */
                    
                    /* import "./IERC165.sol"; */
                    
                    /**
                     * @title ERC165
                     * @author Matt Condon (@shrugs)
                     * @dev Implements ERC165 using a lookup table.
                     */
                    contract ERC165 is IERC165 {
                        /*
                         * bytes4(keccak256('supportsInterface(bytes4)')) == 0x01ffc9a7
                         */
                        bytes4 private constant _INTERFACE_ID_ERC165 = 0x01ffc9a7;
                    
                        /**
                         * @dev Mapping of interface ids to whether or not it's supported.
                         */
                        mapping(bytes4 => bool) private _supportedInterfaces;
                    
                        /**
                         * @dev A contract implementing SupportsInterfaceWithLookup
                         * implements ERC165 itself.
                         */
                        constructor () internal {
                            _registerInterface(_INTERFACE_ID_ERC165);
                        }
                    
                        /**
                         * @dev Implement supportsInterface(bytes4) using a lookup table.
                         */
                        function supportsInterface(bytes4 interfaceId) external view returns (bool) {
                            return _supportedInterfaces[interfaceId];
                        }
                    
                        /**
                         * @dev Internal method for registering an interface.
                         */
                        function _registerInterface(bytes4 interfaceId) internal {
                            require(interfaceId != 0xffffffff, "ERC165: invalid interface id");
                            _supportedInterfaces[interfaceId] = true;
                        }
                    }
                    
                    ////// lib/tinlake-title/src/openzeppelin-solidity/token/ERC721/IERC721.sol
                    /* pragma solidity ^0.5.0; */
                    
                    /* import "../../introspection/IERC165.sol"; */
                    
                    /**
                     * @title ERC721 Non-Fungible Token Standard basic interface
                     * @dev see https://eips.ethereum.org/EIPS/eip-721
                     */
                    contract IERC721 is IERC165 {
                        event Transfer(address indexed from, address indexed to, uint256 indexed tokenId);
                        event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId);
                        event ApprovalForAll(address indexed owner, address indexed operator, bool approved);
                    
                        function balanceOf(address owner) public view returns (uint256 balance);
                        function ownerOf(uint256 tokenId) public view returns (address owner);
                    
                        function approve(address to, uint256 tokenId) public;
                        function getApproved(uint256 tokenId) public view returns (address operator);
                    
                        function setApprovalForAll(address operator, bool _approved) public;
                        function isApprovedForAll(address owner, address operator) public view returns (bool);
                    
                        function transferFrom(address from, address to, uint256 tokenId) public;
                        function safeTransferFrom(address from, address to, uint256 tokenId) public;
                    
                        function safeTransferFrom(address from, address to, uint256 tokenId, bytes memory data) public;
                    }
                    
                    ////// lib/tinlake-title/src/openzeppelin-solidity/token/ERC721/IERC721Receiver.sol
                    /* pragma solidity ^0.5.0; */
                    
                    /**
                     * @title ERC721 token receiver interface
                     * @dev Interface for any contract that wants to support safeTransfers
                     * from ERC721 asset contracts.
                     */
                    contract IERC721Receiver {
                        /**
                         * @notice Handle the receipt of an NFT
                         * @dev The ERC721 smart contract calls this function on the recipient
                         * after a `safeTransfer`. This function MUST return the function selector,
                         * otherwise the caller will revert the transaction. The selector to be
                         * returned can be obtained as `this.onERC721Received.selector`. This
                         * function MAY throw to revert and reject the transfer.
                         * Note: the ERC721 contract address is always the message sender.
                         * @param operator The address which called `safeTransferFrom` function
                         * @param from The address which previously owned the token
                         * @param tokenId The NFT identifier which is being transferred
                         * @param data Additional data with no specified format
                         * @return bytes4 `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))`
                         */
                        function onERC721Received(address operator, address from, uint256 tokenId, bytes memory data)
                        public returns (bytes4);
                    }
                    
                    ////// lib/tinlake-title/src/openzeppelin-solidity/utils/Address.sol
                    /* pragma solidity ^0.5.0; */
                    
                    /**
                     * Utility library of inline functions on addresses
                     */
                    library Address {
                        /**
                         * Returns whether the target address is a contract
                         * @dev This function will return false if invoked during the constructor of a contract,
                         * as the code is not actually created until after the constructor finishes.
                         * @param account address of the account to check
                         * @return whether the target address is a contract
                         */
                        function isContract(address account) internal view returns (bool) {
                            uint256 size;
                            // XXX Currently there is no better way to check if there is a contract in an address
                            // than to check the size of the code at that address.
                            // See https://ethereum.stackexchange.com/a/14016/36603
                            // for more details about how this works.
                            // TODO Check this again before the Serenity release, because all addresses will be
                            // contracts then.
                            // solhint-disable-next-line no-inline-assembly
                            assembly { size := extcodesize(account) }
                            return size > 0;
                        }
                    }
                    
                    ////// lib/tinlake-title/src/openzeppelin-solidity/token/ERC721/ERC721.sol
                    /* pragma solidity ^0.5.0; */
                    
                    /* import "./IERC721.sol"; */
                    /* import "./IERC721Receiver.sol"; */
                    /* import "../../math/SafeMath.sol"; */
                    /* import "../../utils/Address.sol"; */
                    /* import "../../drafts/Counters.sol"; */
                    /* import "../../introspection/ERC165.sol"; */
                    
                    /**
                     * @title ERC721 Non-Fungible Token Standard basic implementation
                     * @dev see https://eips.ethereum.org/EIPS/eip-721
                     */
                    contract ERC721 is ERC165, IERC721 {
                        using SafeMath for uint256;
                        using Address for address;
                        using Counters for Counters.Counter;
                    
                        // Equals to `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))`
                        // which can be also obtained as `IERC721Receiver(0).onERC721Received.selector`
                        bytes4 private constant _ERC721_RECEIVED = 0x150b7a02;
                    
                        // Mapping from token ID to owner
                        mapping (uint256 => address) private _tokenOwner;
                    
                        // Mapping from token ID to approved address
                        mapping (uint256 => address) private _tokenApprovals;
                    
                        // Mapping from owner to number of owned token
                        mapping (address => Counters.Counter) private _ownedTokensCount;
                    
                        // Mapping from owner to operator approvals
                        mapping (address => mapping (address => bool)) private _operatorApprovals;
                    
                        /*
                         *     bytes4(keccak256('balanceOf(address)')) == 0x70a08231
                         *     bytes4(keccak256('ownerOf(uint256)')) == 0x6352211e
                         *     bytes4(keccak256('approve(address,uint256)')) == 0x095ea7b3
                         *     bytes4(keccak256('getApproved(uint256)')) == 0x081812fc
                         *     bytes4(keccak256('setApprovalForAll(address,bool)')) == 0xa22cb465
                         *     bytes4(keccak256('isApprovedForAll(address,address)')) == 0xe985e9c
                         *     bytes4(keccak256('transferFrom(address,address,uint256)')) == 0x23b872dd
                         *     bytes4(keccak256('safeTransferFrom(address,address,uint256)')) == 0x42842e0e
                         *     bytes4(keccak256('safeTransferFrom(address,address,uint256,bytes)')) == 0xb88d4fde
                         *
                         *     => 0x70a08231 ^ 0x6352211e ^ 0x095ea7b3 ^ 0x081812fc ^
                         *        0xa22cb465 ^ 0xe985e9c ^ 0x23b872dd ^ 0x42842e0e ^ 0xb88d4fde == 0x80ac58cd
                         */
                        bytes4 private constant _INTERFACE_ID_ERC721 = 0x80ac58cd;
                    
                        constructor () public {
                            // register the supported interfaces to conform to ERC721 via ERC165
                            _registerInterface(_INTERFACE_ID_ERC721);
                        }
                    
                        /**
                         * @dev Gets the balance of the specified address.
                         * @param owner address to query the balance of
                         * @return uint256 representing the amount owned by the passed address
                         */
                        function balanceOf(address owner) public view returns (uint256) {
                            require(owner != address(0), "ERC721: balance query for the zero address");
                    
                            return _ownedTokensCount[owner].current();
                        }
                    
                        /**
                         * @dev Gets the owner of the specified token ID.
                         * @param tokenId uint256 ID of the token to query the owner of
                         * @return address currently marked as the owner of the given token ID
                         */
                        function ownerOf(uint256 tokenId) public view returns (address) {
                            address owner = _tokenOwner[tokenId];
                            require(owner != address(0), "ERC721: owner query for nonexistent token");
                    
                            return owner;
                        }
                    
                        /**
                         * @dev Approves another address to transfer the given token ID
                         * The zero address indicates there is no approved address.
                         * There can only be one approved address per token at a given time.
                         * Can only be called by the token owner or an approved operator.
                         * @param to address to be approved for the given token ID
                         * @param tokenId uint256 ID of the token to be approved
                         */
                        function approve(address to, uint256 tokenId) public {
                            address owner = ownerOf(tokenId);
                            require(to != owner, "ERC721: approval to current owner");
                    
                            require(msg.sender == owner || isApprovedForAll(owner, msg.sender),
                                "ERC721: approve caller is not owner nor approved for all"
                            );
                    
                            _tokenApprovals[tokenId] = to;
                            emit Approval(owner, to, tokenId);
                        }
                    
                        /**
                         * @dev Gets the approved address for a token ID, or zero if no address set
                         * Reverts if the token ID does not exist.
                         * @param tokenId uint256 ID of the token to query the approval of
                         * @return address currently approved for the given token ID
                         */
                        function getApproved(uint256 tokenId) public view returns (address) {
                            require(_exists(tokenId), "ERC721: approved query for nonexistent token");
                    
                            return _tokenApprovals[tokenId];
                        }
                    
                        /**
                         * @dev Sets or unsets the approval of a given operator
                         * An operator is allowed to transfer all tokens of the sender on their behalf.
                         * @param to operator address to set the approval
                         * @param approved representing the status of the approval to be set
                         */
                        function setApprovalForAll(address to, bool approved) public {
                            require(to != msg.sender, "ERC721: approve to caller");
                    
                            _operatorApprovals[msg.sender][to] = approved;
                            emit ApprovalForAll(msg.sender, to, approved);
                        }
                    
                        /**
                         * @dev Tells whether an operator is approved by a given owner.
                         * @param owner owner address which you want to query the approval of
                         * @param operator operator address which you want to query the approval of
                         * @return bool whether the given operator is approved by the given owner
                         */
                        function isApprovedForAll(address owner, address operator) public view returns (bool) {
                            return _operatorApprovals[owner][operator];
                        }
                    
                        /**
                         * @dev Transfers the ownership of a given token ID to another address.
                         * Usage of this method is discouraged, use `safeTransferFrom` whenever possible.
                         * Requires the msg.sender to be the owner, approved, or operator.
                         * @param from current owner of the token
                         * @param to address to receive the ownership of the given token ID
                         * @param tokenId uint256 ID of the token to be transferred
                         */
                        function transferFrom(address from, address to, uint256 tokenId) public {
                            //solhint-disable-next-line max-line-length
                            require(_isApprovedOrOwner(msg.sender, tokenId), "ERC721: transfer caller is not owner nor approved");
                    
                            _transferFrom(from, to, tokenId);
                        }
                    
                        /**
                         * @dev Safely transfers the ownership of a given token ID to another address
                         * If the target address is a contract, it must implement `onERC721Received`,
                         * which is called upon a safe transfer, and return the magic value
                         * `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))`; otherwise,
                         * the transfer is reverted.
                         * Requires the msg.sender to be the owner, approved, or operator
                         * @param from current owner of the token
                         * @param to address to receive the ownership of the given token ID
                         * @param tokenId uint256 ID of the token to be transferred
                         */
                        function safeTransferFrom(address from, address to, uint256 tokenId) public {
                            safeTransferFrom(from, to, tokenId, "");
                        }
                    
                        /**
                         * @dev Safely transfers the ownership of a given token ID to another address
                         * If the target address is a contract, it must implement `onERC721Received`,
                         * which is called upon a safe transfer, and return the magic value
                         * `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))`; otherwise,
                         * the transfer is reverted.
                         * Requires the msg.sender to be the owner, approved, or operator
                         * @param from current owner of the token
                         * @param to address to receive the ownership of the given token ID
                         * @param tokenId uint256 ID of the token to be transferred
                         * @param _data bytes data to send along with a safe transfer check
                         */
                        function safeTransferFrom(address from, address to, uint256 tokenId, bytes memory _data) public {
                            transferFrom(from, to, tokenId);
                            require(_checkOnERC721Received(from, to, tokenId, _data), "ERC721: transfer to non ERC721Receiver implementer");
                        }
                    
                        /**
                         * @dev Returns whether the specified token exists.
                         * @param tokenId uint256 ID of the token to query the existence of
                         * @return bool whether the token exists
                         */
                        function _exists(uint256 tokenId) internal view returns (bool) {
                            address owner = _tokenOwner[tokenId];
                            return owner != address(0);
                        }
                    
                        /**
                         * @dev Returns whether the given spender can transfer a given token ID.
                         * @param spender address of the spender to query
                         * @param tokenId uint256 ID of the token to be transferred
                         * @return bool whether the msg.sender is approved for the given token ID,
                         * is an operator of the owner, or is the owner of the token
                         */
                        function _isApprovedOrOwner(address spender, uint256 tokenId) internal view returns (bool) {
                            require(_exists(tokenId), "ERC721: operator query for nonexistent token");
                            address owner = ownerOf(tokenId);
                            return (spender == owner || getApproved(tokenId) == spender || isApprovedForAll(owner, spender));
                        }
                    
                        /**
                         * @dev Internal function to mint a new token.
                         * Reverts if the given token ID already exists.
                         * @param to The address that will own the minted token
                         * @param tokenId uint256 ID of the token to be minted
                         */
                        function _mint(address to, uint256 tokenId) internal {
                            require(to != address(0), "ERC721: mint to the zero address");
                            require(!_exists(tokenId), "ERC721: token already minted");
                    
                            _tokenOwner[tokenId] = to;
                            _ownedTokensCount[to].increment();
                    
                            emit Transfer(address(0), to, tokenId);
                        }
                    
                        /**
                         * @dev Internal function to burn a specific token.
                         * Reverts if the token does not exist.
                         * Deprecated, use _burn(uint256) instead.
                         * @param owner owner of the token to burn
                         * @param tokenId uint256 ID of the token being burned
                         */
                        function _burn(address owner, uint256 tokenId) internal {
                            require(ownerOf(tokenId) == owner, "ERC721: burn of token that is not own");
                    
                            _clearApproval(tokenId);
                    
                            _ownedTokensCount[owner].decrement();
                            _tokenOwner[tokenId] = address(0);
                    
                            emit Transfer(owner, address(0), tokenId);
                        }
                    
                        /**
                         * @dev Internal function to burn a specific token.
                         * Reverts if the token does not exist.
                         * @param tokenId uint256 ID of the token being burned
                         */
                        function _burn(uint256 tokenId) internal {
                            _burn(ownerOf(tokenId), tokenId);
                        }
                    
                        /**
                         * @dev Internal function to transfer ownership of a given token ID to another address.
                         * As opposed to transferFrom, this imposes no restrictions on msg.sender.
                         * @param from current owner of the token
                         * @param to address to receive the ownership of the given token ID
                         * @param tokenId uint256 ID of the token to be transferred
                         */
                        function _transferFrom(address from, address to, uint256 tokenId) internal {
                            require(ownerOf(tokenId) == from, "ERC721: transfer of token that is not own");
                            require(to != address(0), "ERC721: transfer to the zero address");
                    
                            _clearApproval(tokenId);
                    
                            _ownedTokensCount[from].decrement();
                            _ownedTokensCount[to].increment();
                    
                            _tokenOwner[tokenId] = to;
                    
                            emit Transfer(from, to, tokenId);
                        }
                    
                        /**
                         * @dev Internal function to invoke `onERC721Received` on a target address.
                         * The call is not executed if the target address is not a contract.
                         * @param from address representing the previous owner of the given token ID
                         * @param to target address that will receive the tokens
                         * @param tokenId uint256 ID of the token to be transferred
                         * @param _data bytes optional data to send along with the call
                         * @return bool whether the call correctly returned the expected magic value
                         */
                        function _checkOnERC721Received(address from, address to, uint256 tokenId, bytes memory _data)
                            internal returns (bool)
                        {
                            if (!to.isContract()) {
                                return true;
                            }
                    
                            bytes4 retval = IERC721Receiver(to).onERC721Received(msg.sender, from, tokenId, _data);
                            return (retval == _ERC721_RECEIVED);
                        }
                    
                        /**
                         * @dev Private function to clear current approval of a given token ID.
                         * @param tokenId uint256 ID of the token to be transferred
                         */
                        function _clearApproval(uint256 tokenId) private {
                            if (_tokenApprovals[tokenId] != address(0)) {
                                _tokenApprovals[tokenId] = address(0);
                            }
                        }
                    }
                    
                    ////// lib/tinlake-title/src/openzeppelin-solidity/token/ERC721/IERC721Metadata.sol
                    /* pragma solidity ^0.5.0; */
                    
                    /* import "./IERC721.sol"; */
                    
                    /**
                     * @title ERC-721 Non-Fungible Token Standard, optional metadata extension
                     * @dev See https://eips.ethereum.org/EIPS/eip-721
                     */
                    contract IERC721Metadata is IERC721 {
                        function name() external view returns (string memory);
                        function symbol() external view returns (string memory);
                        function tokenURI(uint256 tokenId) external view returns (string memory);
                    }
                    
                    ////// lib/tinlake-title/src/openzeppelin-solidity/token/ERC721/ERC721Metadata.sol
                    /* pragma solidity ^0.5.0; */
                    
                    /* import "./ERC721.sol"; */
                    /* import "./IERC721Metadata.sol"; */
                    /* import "../../introspection/ERC165.sol"; */
                    
                    contract ERC721Metadata is ERC165, ERC721, IERC721Metadata {
                        // Token name
                        string private _name;
                    
                        // Token symbol
                        string private _symbol;
                    
                        // Optional mapping for token URIs
                        mapping(uint256 => string) private _tokenURIs;
                    
                        /*
                         *     bytes4(keccak256('name()')) == 0x06fdde03
                         *     bytes4(keccak256('symbol()')) == 0x95d89b41
                         *     bytes4(keccak256('tokenURI(uint256)')) == 0xc87b56dd
                         *
                         *     => 0x06fdde03 ^ 0x95d89b41 ^ 0xc87b56dd == 0x5b5e139f
                         */
                        bytes4 private constant _INTERFACE_ID_ERC721_METADATA = 0x5b5e139f;
                    
                        /**
                         * @dev Constructor function
                         */
                        constructor (string memory name, string memory symbol) public {
                            _name = name;
                            _symbol = symbol;
                    
                            // register the supported interfaces to conform to ERC721 via ERC165
                            _registerInterface(_INTERFACE_ID_ERC721_METADATA);
                        }
                    
                        /**
                         * @dev Gets the token name.
                         * @return string representing the token name
                         */
                        function name() external view returns (string memory) {
                            return _name;
                        }
                    
                        /**
                         * @dev Gets the token symbol.
                         * @return string representing the token symbol
                         */
                        function symbol() external view returns (string memory) {
                            return _symbol;
                        }
                    
                        /**
                         * @dev Returns an URI for a given token ID.
                         * Throws if the token ID does not exist. May return an empty string.
                         * @param tokenId uint256 ID of the token to query
                         */
                        function tokenURI(uint256 tokenId) external view returns (string memory) {
                            require(_exists(tokenId), "ERC721Metadata: URI query for nonexistent token");
                            return _tokenURIs[tokenId];
                        }
                    
                        /**
                         * @dev Internal function to set the token URI for a given token.
                         * Reverts if the token ID does not exist.
                         * @param tokenId uint256 ID of the token to set its URI
                         * @param uri string URI to assign
                         */
                        function _setTokenURI(uint256 tokenId, string memory uri) internal {
                            require(_exists(tokenId), "ERC721Metadata: URI set of nonexistent token");
                            _tokenURIs[tokenId] = uri;
                        }
                    
                        /**
                         * @dev Internal function to burn a specific token.
                         * Reverts if the token does not exist.
                         * Deprecated, use _burn(uint256) instead.
                         * @param owner owner of the token to burn
                         * @param tokenId uint256 ID of the token being burned by the msg.sender
                         */
                        function _burn(address owner, uint256 tokenId) internal {
                            super._burn(owner, tokenId);
                    
                            // Clear metadata (if any)
                            if (bytes(_tokenURIs[tokenId]).length != 0) {
                                delete _tokenURIs[tokenId];
                            }
                        }
                    }
                    
                    ////// lib/tinlake-title/src/title.sol
                    // title.sol -- NFT to manage access rights to contracts
                    // Copyright (C) 2019 lucasvo
                    
                    // 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.15 <0.6.0; */
                    
                    /* import { ERC721Metadata } from "./openzeppelin-solidity/token/ERC721/ERC721Metadata.sol"; */
                    /* import { Auth } from "tinlake-auth/auth.sol"; */
                    
                    contract Title is Auth, ERC721Metadata {
                        // --- Data ---
                        uint public count;
                        string public uri;
                    
                        constructor (string memory name, string memory symbol) ERC721Metadata(name, symbol) public {
                            wards[msg.sender] = 1;
                            count = 1;
                        }
                    
                        // --- Title ---
                        function issue (address usr) public auth returns (uint) {
                            return _issue(usr);
                        }
                    
                        function _issue (address usr) internal returns (uint) {
                            _mint(usr, count);
                            count += 1; // can't overflow, not enough gas in the world to pay for 2**256 nfts.
                            return count-1;
                        }
                    
                        function close (uint tkn) public auth {
                            _burn(tkn);
                        }
                    }
                    
                    contract TitleLike_1 {
                        function ownerOf (uint) public returns (address);
                    }
                    
                    contract TitleOwned {
                        TitleLike_1 title;
                        constructor (address title_) public {
                            title = TitleLike_1(title_);
                        }
                    
                        modifier owner (uint loan) { require(title.ownerOf(loan) == msg.sender); _; }
                    }
                    

                    File 6 of 7: NAVFeed
                    // Verified using https://dapp.tools
                    
                    // hevm: flattened sources of src/borrower/feed/navfeed.sol
                    pragma solidity >=0.5.15 >=0.5.15 <0.6.0;
                    pragma experimental ABIEncoderV2;
                    
                    ////// lib/tinlake-auth/lib/ds-note/src/note.sol
                    /// note.sol -- the `note' modifier, for logging calls as events
                    
                    // 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.15; */
                    
                    contract DSNote {
                        event LogNote(
                            bytes4   indexed  sig,
                            address  indexed  guy,
                            bytes32  indexed  foo,
                            bytes32  indexed  bar,
                            uint256           wad,
                            bytes             fax
                        ) anonymous;
                    
                        modifier note {
                            bytes32 foo;
                            bytes32 bar;
                            uint256 wad;
                    
                            assembly {
                                foo := calldataload(4)
                                bar := calldataload(36)
                                wad := callvalue()
                            }
                    
                            _;
                    
                            emit LogNote(msg.sig, msg.sender, foo, bar, wad, msg.data);
                        }
                    }
                    
                    ////// lib/tinlake-auth/src/auth.sol
                    // Copyright (C) Centrifuge 2020, based on MakerDAO dss https://github.com/makerdao/dss
                    //
                    // 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.15 <0.6.0; */
                    
                    /* import "ds-note/note.sol"; */
                    
                    contract Auth is DSNote {
                        mapping (address => uint) public wards;
                        function rely(address usr) public auth note { wards[usr] = 1; }
                        function deny(address usr) public auth note { wards[usr] = 0; }
                        modifier auth { require(wards[msg.sender] == 1); _; }
                    }
                    
                    ////// lib/tinlake-math/src/math.sol
                    // Copyright (C) 2018 Rain <[email protected]>
                    //
                    // 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.15 <0.6.0; */
                    
                    contract Math {
                        uint256 constant ONE = 10 ** 27;
                    
                        function safeAdd(uint x, uint y) public pure returns (uint z) {
                            require((z = x + y) >= x, "safe-add-failed");
                        }
                    
                        function safeSub(uint x, uint y) public pure returns (uint z) {
                            require((z = x - y) <= x, "safe-sub-failed");
                        }
                    
                        function safeMul(uint x, uint y) public pure returns (uint z) {
                            require(y == 0 || (z = x * y) / y == x, "safe-mul-failed");
                        }
                    
                        function safeDiv(uint x, uint y) public pure returns (uint z) {
                            z = x / y;
                        }
                    
                        function rmul(uint x, uint y) public pure returns (uint z) {
                            z = safeMul(x, y) / ONE;
                        }
                    
                        function rdiv(uint x, uint y) public pure returns (uint z) {
                            require(y > 0, "division by zero");
                            z = safeAdd(safeMul(x, ONE), y / 2) / y;
                        }
                    
                        function rdivup(uint x, uint y) internal pure returns (uint z) {
                            require(y > 0, "division by zero");
                            // always rounds up
                            z = safeAdd(safeMul(x, ONE), safeSub(y, 1)) / y;
                        }
                    
                    
                    }
                    
                    ////// lib/tinlake-math/src/interest.sol
                    // Copyright (C) 2018 Rain <[email protected]> and Centrifuge, referencing MakerDAO dss => https://github.com/makerdao/dss/blob/master/src/pot.sol
                    //
                    // 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.15 <0.6.0; */
                    
                    /* import "./math.sol"; */
                    
                    contract Interest is Math {
                        // @notice This function provides compounding in seconds
                        // @param chi Accumulated interest rate over time
                        // @param ratePerSecond Interest rate accumulation per second in RAD(10ˆ27)
                        // @param lastUpdated When the interest rate was last updated
                        // @param pie Total sum of all amounts accumulating under one interest rate, divided by that rate
                        // @return The new accumulated rate, as well as the difference between the debt calculated with the old and new accumulated rates.
                        function compounding(uint chi, uint ratePerSecond, uint lastUpdated, uint pie) public view returns (uint, uint) {
                            require(block.timestamp >= lastUpdated, "tinlake-math/invalid-timestamp");
                            require(chi != 0);
                            // instead of a interestBearingAmount we use a accumulated interest rate index (chi)
                            uint updatedChi = _chargeInterest(chi ,ratePerSecond, lastUpdated, block.timestamp);
                            return (updatedChi, safeSub(rmul(updatedChi, pie), rmul(chi, pie)));
                        }
                    
                        // @notice This function charge interest on a interestBearingAmount
                        // @param interestBearingAmount is the interest bearing amount
                        // @param ratePerSecond Interest rate accumulation per second in RAD(10ˆ27)
                        // @param lastUpdated last time the interest has been charged
                        // @return interestBearingAmount + interest
                        function chargeInterest(uint interestBearingAmount, uint ratePerSecond, uint lastUpdated) public view returns (uint) {
                            if (block.timestamp >= lastUpdated) {
                                interestBearingAmount = _chargeInterest(interestBearingAmount, ratePerSecond, lastUpdated, block.timestamp);
                            }
                            return interestBearingAmount;
                        }
                    
                        function _chargeInterest(uint interestBearingAmount, uint ratePerSecond, uint lastUpdated, uint current) internal pure returns (uint) {
                            return rmul(rpow(ratePerSecond, current - lastUpdated, ONE), interestBearingAmount);
                        }
                    
                    
                        // convert pie to debt/savings amount
                        function toAmount(uint chi, uint pie) public pure returns (uint) {
                            return rmul(pie, chi);
                        }
                    
                        // convert debt/savings amount to pie
                        function toPie(uint chi, uint amount) public pure returns (uint) {
                            return rdivup(amount, chi);
                        }
                    
                        function rpow(uint x, uint n, uint base) public pure returns (uint z) {
                            assembly {
                                switch x case 0 {switch n case 0 {z := base} default {z := 0}}
                                default {
                                    switch mod(n, 2) case 0 { z := base } default { z := x }
                                    let half := div(base, 2)  // for rounding.
                                    for { n := div(n, 2) } n { n := div(n,2) } {
                                    let xx := mul(x, x)
                                    if iszero(eq(div(xx, x), x)) { revert(0,0) }
                                    let xxRound := add(xx, half)
                                    if lt(xxRound, xx) { revert(0,0) }
                                    x := div(xxRound, base)
                                    if mod(n,2) {
                                        let zx := mul(z, x)
                                        if and(iszero(iszero(x)), iszero(eq(div(zx, x), z))) { revert(0,0) }
                                        let zxRound := add(zx, half)
                                        if lt(zxRound, zx) { revert(0,0) }
                                        z := div(zxRound, base)
                                    }
                                }
                                }
                            }
                        }
                    }
                    
                    ////// src/borrower/feed/buckets.sol
                    // Copyright (C) 2020 Centrifuge
                    // 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.15; */
                    
                    // the buckets contract stores values in a map using a timestamp as a key
                    // each value store a pointer the next value in a linked list
                    // to improve performance/gas efficiency while iterating over all values in a timespan
                    contract Buckets {
                        // abstract contract
                        constructor() internal {}
                    
                        struct Bucket {
                            uint value;
                            uint next;
                        }
                    
                        // timestamp => bucket
                        mapping (uint => Bucket) public buckets;
                    
                        // pointer to the first bucket and last bucket
                        uint public firstBucket;
                        uint public lastBucket;
                    
                        uint constant public NullDate = 1;
                    
                        function addBucket(uint timestamp, uint value) internal {
                            buckets[timestamp].value = value;
                    
                            if (firstBucket == 0) {
                                firstBucket = timestamp;
                                buckets[timestamp].next = NullDate;
                                lastBucket = firstBucket;
                                return;
                            }
                    
                            // new bucket before first one
                            if (timestamp < firstBucket) {
                                buckets[timestamp].next = firstBucket;
                                firstBucket = timestamp;
                                return;
                            }
                    
                            // find predecessor bucket by going back in time
                            // instead of iterating the linked list from the first bucket
                            // assuming its more gas efficient to iterate over time instead of iterating the list from the beginning
                            // not true if buckets are only sparsely populated over long periods of time
                            uint prev = timestamp;
                            while(buckets[prev].next == 0) {prev = prev - 1 days;}
                    
                            if (buckets[prev].next == NullDate) {
                                lastBucket = timestamp;
                            }
                            buckets[timestamp].next = buckets[prev].next;
                            buckets[prev].next = timestamp;
                        }
                    
                        function removeBucket(uint timestamp) internal {
                            buckets[timestamp].value = 0;
                            _removeBucket(timestamp);
                            buckets[timestamp].next = 0;
                        }
                    
                        function _removeBucket(uint timestamp) internal {
                            if(firstBucket == lastBucket) {
                                lastBucket = 0;
                                firstBucket = 0;
                                return;
                            }
                    
                            if (timestamp != firstBucket) {
                                uint prev = timestamp - 1 days;
                                // assuming its more gas efficient to iterate over time instead of iterating the list from the beginning
                                // not true if buckets are only sparsely populated over long periods of time
                                while(buckets[prev].next != timestamp) {prev = prev - 1 days;}
                                buckets[prev].next = buckets[timestamp].next;
                                if(timestamp == lastBucket) {
                                    lastBucket = prev;
                                }
                                return;
                            }
                    
                            firstBucket = buckets[timestamp].next;
                        }
                    }
                    
                    ////// src/borrower/feed/nftfeed.sol
                    // Copyright (C) 2020 Centrifuge
                    
                    // 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.15 <0.6.0; */
                    
                    /* import "ds-note/note.sol"; */
                    /* import "tinlake-auth/auth.sol"; */
                    /* import "tinlake-math/math.sol"; */
                    
                    contract ShelfLike_2 {
                        function shelf(uint loan) public view returns (address registry, uint tokenId);
                        function nftlookup(bytes32 nftID) public returns (uint loan);
                    }
                    
                    contract PileLike_2 {
                        function setRate(uint loan, uint rate) public;
                        function debt(uint loan) public returns (uint);
                        function pie(uint loan) public returns (uint);
                        function changeRate(uint loan, uint newRate) public;
                        function loanRates(uint loan) public returns (uint);
                        function file(bytes32, uint, uint) public;
                        function rates(uint rate) public view returns (uint, uint, uint ,uint48, uint);
                        function total() public view returns (uint);
                        function rateDebt(uint rate) public view returns (uint);
                    }
                    
                    // The NFTFeed stores values and risk group of nfts that are used as collateral in tinlake. A risk group contains: thresholdRatio, ceilingRatio & interstRate.
                    // The risk groups for a tinlake deployment are defined on contract creation and can not be changed afterwards.
                    // Loan parameters like interstRate, max borrow amount and liquidation threshold are determined based on the value and risk group of the underlying collateral nft.
                    contract BaseNFTFeed is DSNote, Auth, Math {
                    
                        // nftID => nftValues
                        mapping (bytes32 => uint) public nftValues;
                        // nftID => risk
                        mapping (bytes32 => uint) public risk;
                    
                        // risk => thresholdRatio
                        // thresholdRatio is used to determine the liquidation threshold of the loan. thresholdRatio * nftValue = liquidation threshold
                        // When loan debt reaches the liquidation threshold, it can be seized and collected by a whitelisted keeper.
                        mapping (uint => uint) public thresholdRatio;
                    
                        // risk => ceilingRatio
                        // ceilingRatio is used to determine the ax borrow amount (ceiling) of a loan. ceilingRatio * nftValue = max borrow amount
                        // When loan debt reaches the liquidation threshold, it can be seized and collected by a whitelisted keeper.
                        mapping (uint => uint) public ceilingRatio;
                    
                        // loan => borrowed
                        // stores the already borrowed amounts for each loan
                        // required to track the borrowed currency amount without accrued interest
                        mapping (uint => uint) public borrowed;
                    
                        PileLike_2 pile;
                        ShelfLike_2 shelf;
                    
                        constructor () public {
                            wards[msg.sender] = 1;
                        }
                    
                         // part of Feed interface
                        function file(bytes32 name, uint value) public auth {}
                    
                        /// sets the dependency to another contract
                        function depend(bytes32 contractName, address addr) external auth {
                            if (contractName == "pile") {pile = PileLike_2(addr);}
                            else if (contractName == "shelf") { shelf = ShelfLike_2(addr); }
                            else revert();
                        }
                    
                        // returns a unique id based on the nft registry and tokenId
                        // the nftID is used to set the risk group and value for nfts
                        function nftID(address registry, uint tokenId) public pure returns (bytes32) {
                            return keccak256(abi.encodePacked(registry, tokenId));
                        }
                    
                        // returns the nftID for the underlying collateral nft
                        function nftID(uint loan) public view returns (bytes32) {
                            (address registry, uint tokenId) = shelf.shelf(loan);
                            return nftID(registry, tokenId);
                        }
                    
                        function file(bytes32 name, uint risk_, uint thresholdRatio_, uint ceilingRatio_, uint rate_) public auth {
                            if(name == "riskGroupNFT") {
                                require(ceilingRatio[risk_] == 0, "risk-group-in-usage");
                                thresholdRatio[risk_] = thresholdRatio_;
                                ceilingRatio[risk_] = ceilingRatio_;
                                // set interestRate for risk group
                                pile.file("rate", risk_, rate_);
                            } else {revert ("unkown name");}
                        }
                    
                        ///  -- Oracle Updates --
                    
                        // The nft value is to be updated by authenticated oracles
                        function update(bytes32 nftID_,  uint value) public auth {
                            // switch of collateral risk group results in new: ceiling, threshold for existing loan
                            nftValues[nftID_] = value;
                        }
                    
                         // The nft value & risk group is to be updated by authenticated oracles
                        function update(bytes32 nftID_, uint value, uint risk_) public auth {
                            // the risk group has to exist
                            require(thresholdRatio[risk_] != 0, "threshold for risk group not defined");
                    
                            // switch of collateral risk group results in new: ceiling, threshold and interest rate for existing loan
                            // change to new rate interestRate immediately in pile if loan debt exists
                            uint loan = shelf.nftlookup(nftID_);
                            if (pile.pie(loan) != 0) {
                                pile.changeRate(loan, risk_);
                            }
                            risk[nftID_] = risk_;
                            nftValues[nftID_] = value;
                        }
                    
                        // function checks if the borrow amount does not exceed the max allowed borrow amount (=ceiling)
                        function borrow(uint loan, uint amount) external auth returns (uint) {
                            // increase borrowed amount -> note: max allowed borrow amount does not include accrued interest
                            borrowed[loan] = safeAdd(borrowed[loan], amount);
                    
                            require(currentCeiling(loan) >= borrowed[loan], "borrow-amount-too-high");
                            return amount;
                        }
                    
                        // part of Feed interface
                        function repay(uint, uint amount) external auth returns (uint) {
                            // note: borrowed amount is not decreased as the feed implements the principal and not credit line method
                            return amount;
                        }
                    
                        // borrowEvent is called by the shelf in the borrow method
                        function borrowEvent(uint loan) public auth {
                            uint risk_ = risk[nftID(loan)];
                    
                            // when issued every loan has per default interest rate of risk group 0.
                            // correct interest rate has to be set on first borrow event
                            if(pile.loanRates(loan) != risk_) {
                                // set loan interest rate to the one of the correct risk group
                                pile.setRate(loan, risk_);
                            }
                        }
                    
                        // part of Feed interface
                        function unlockEvent(uint loan) public auth {}
                    
                        ///  -- Getter methods --
                        // returns the ceiling of a loan
                        // the ceiling defines the maximum amount which can be borrowed
                        function ceiling(uint loan) public view returns (uint) {
                            if (borrowed[loan] > currentCeiling(loan)) {
                                return 0;
                            }
                            return safeSub(currentCeiling(loan), borrowed[loan]);
                        }
                    
                        function currentCeiling(uint loan) public view returns(uint) {
                            bytes32 nftID_ = nftID(loan);
                            return rmul(nftValues[nftID_], ceilingRatio[risk[nftID_]]);
                        }
                    
                        // returns the threshold of a loan
                        // if the loan debt is above the loan threshold the NFT can be seized
                        function threshold(uint loan) public view returns (uint) {
                            bytes32 nftID_ = nftID(loan);
                            return rmul(nftValues[nftID_], thresholdRatio[risk[nftID_]]);
                        }
                    
                        /// implements feed interface and returns poolValue as the total debt of all loans
                        function totalValue() public view returns (uint) {
                            return pile.total();
                        }
                    }
                    
                    ////// src/fixed_point.sol
                    // Copyright (C) 2020 Centrifuge
                    // 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.15 <0.6.0; */
                    
                    contract FixedPoint {
                        struct Fixed27 {
                            uint value;
                        }
                    }
                    
                    ////// src/borrower/feed/navfeed.sol
                    // Copyright (C) 2020 Centrifuge
                    // 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.15 <0.6.0; */
                    /* pragma experimental ABIEncoderV2; */
                    
                    /* import "ds-note/note.sol"; */
                    /* import "tinlake-auth/auth.sol"; */
                    /* import "tinlake-math/interest.sol"; */
                    /* import "./nftfeed.sol"; */
                    /* import "./buckets.sol"; */
                    /* import "../../fixed_point.sol"; */
                    
                    // The Nav Feed contract extends the functionality of the NFT Feed by the Net Asset Value (NAV) computation of a Tinlake pool.
                    // NAV is computed as the sum of all discounted future values (fv) of ongoing loans (debt > 0) in the pool.
                    // The applied discountRate is dependant on the maturity data of the underlying collateral. The discount decreases with the maturity date approaching.
                    // To optimize the NAV calculation the discounting of future values happens bucketwise. FVs from assets with the same maturity date are added to one bucket.
                    // This safes iterations & gas, as the same discountRates can be applied per bucket.
                    contract NAVFeed is BaseNFTFeed, Interest, Buckets, FixedPoint {
                    
                        // maturityDate is the expected date of repayment for an asset
                        // nftID => maturityDate
                        mapping (bytes32 => uint) public maturityDate;
                    
                        // recoveryRatePD is a combined rate that includes the probability of default for an asset of a certain risk group and its recovery rate
                        // risk => recoveryRatePD
                        mapping (uint => Fixed27) public recoveryRatePD;
                    
                        // futureValue of an asset based on the loan debt, interest rate, maturity date and recoveryRatePD
                        // nftID => futureValue
                        mapping (bytes32 => uint) public futureValue;
                    
                        WriteOff [5] public writeOffs;
                    
                        struct WriteOff {
                            uint rateGroup;
                            // denominated in (10^27)
                            Fixed27 percentage;
                        }
                    
                        // discount rate applied on every asset's fv depending on its maturityDate. The discount decreases with the maturityDate approaching.
                        Fixed27 public discountRate;
                    
                        // approximatedNAV is calculated in case of borrows & repayments between epoch executions.
                        // It decreases/increases the NAV by the repaid/borrowed amount without running the NAV calculation routine.
                        // This is required for more accurate Senior & JuniorAssetValue estimations between epochs
                        uint public approximatedNAV;
                    
                        // rate group for write-offs in pile contract
                        uint constant public  WRITE_OFF_PHASE_A = 1000;
                        uint constant public  WRITE_OFF_PHASE_B = 1001;
                        uint constant public  WRITE_OFF_PHASE_C = 1002;
                        uint constant public  WRITE_OFF_PHASE_D = 1003;
                        uint constant public  WRITE_OFF_PHASE_E = 1004;
                    
                        constructor () public {
                            wards[msg.sender] = 1;
                        }
                    
                        function init() public {
                            require(ceilingRatio[0] == 0, "already-initialized");
                    
                            // gas optimized initialization of writeOffs and risk groups
                            // write off are hardcoded in the contract instead of init function params
                    
                            // risk groups are extended by the recoveryRatePD parameter compared with NFTFeed
                    
                            // The following score cards just examples that are mostly optimized for the system test cases
                    
                            // risk group: 0
                    
                            // risk group: 0 - APR: 2.78%  
                            file("riskGroup", 0, ONE, ONE, uint(1000000000881532217148655504), 99.9836*10**25);
                            // risk group: 1 - APR: 3.02%
                            file("riskGroup", 1, ONE, ONE, uint(1000000000957635717909690512), 99.9803*10**25);
                            // risk group: 2 - APR: 3.26%
                            file("riskGroup", 2, ONE, ONE, uint(1000000001033739218670725520), 99.977*10**25);
                            // risk group: 3 - APR: 3.5%
                            file("riskGroup", 3, ONE, ONE, uint(1000000001109842719431760527), 99.9737*10**25);
                            // risk group: 4 - APR: 3.75%
                            file("riskGroup", 4, ONE, ONE, uint(1000000001189117199391171993), 99.9704*10**25);
                            // risk group: 5 - APR: 3.99%
                            file("riskGroup", 5, ONE, ONE, uint(1000000001265220700152207001), 99.9671*10**25);
                            // risk group: 6 - APR: 4.23%
                            file("riskGroup", 6, ONE, ONE, uint(1000000001341324200913242009), 99.9638*10**25);
                            // risk group: 7 - APR: 4.47%
                            file("riskGroup", 7, ONE, ONE, uint(1000000001417427701674277016), 99.9605*10**25);
                            // risk group: 8 - APR: 4.7%
                            file("riskGroup", 8, ONE, ONE, uint(1000000001490360223236935565), 99.9573*10**25);
                            // risk group: 9 - APR: 4.94%
                            file("riskGroup", 9, ONE, ONE, uint(1000000001566463723997970573), 99.954*10**25);
                            // risk group: 10 - APR: 5.18%
                            file("riskGroup", 10, ONE, ONE, uint(1000000001642567224759005580), 99.9507*10**25);
                            // risk group: 11 - APR: 5.42%
                            file("riskGroup", 11, ONE, ONE, uint(1000000001718670725520040588), 99.9474*10**25);
                            // risk group: 12 - APR: 5.65%
                            file("riskGroup", 12, ONE, ONE, uint(1000000001791603247082699137), 99.9441*10**25);
                            // risk group: 13 - APR: 5.89%
                            file("riskGroup", 13, ONE, ONE, uint(1000000001867706747843734145), 99.9408*10**25);
                            // risk group: 14 - APR: 6.13%
                            file("riskGroup", 14, ONE, ONE, uint(1000000001943810248604769152), 99.9375*10**25);
                            // risk group: 15 - APR: 6.36%
                            file("riskGroup", 15, ONE, ONE, uint(1000000002016742770167427701), 99.9342*10**25);
                            // risk group: 16 - APR: 6.59%
                            file("riskGroup", 16, ONE, ONE, uint(1000000002089675291730086250), 99.931*10**25);
                            // risk group: 17 - APR: 6.83%
                            file("riskGroup", 17, ONE, ONE, uint(1000000002165778792491121258), 99.9277*10**25);
                            // risk group: 18 - APR: 7.06%
                            file("riskGroup", 18, ONE, ONE, uint(1000000002238711314053779807), 99.9244*10**25);
                            // risk group: 19 - APR: 7.29%
                            file("riskGroup", 19, ONE, ONE, uint(1000000002311643835616438356), 99.9211*10**25);
                            // risk group: 20 - APR: 7.53%
                            file("riskGroup", 20, ONE, ONE, uint(1000000002387747336377473363), 99.9178*10**25);
                            // risk group: 21 - APR: 7.76%
                            file("riskGroup", 21, ONE, ONE, uint(1000000002460679857940131912), 99.9145*10**25);
                            // risk group: 22 - APR: 7.99%
                            file("riskGroup", 22, ONE, ONE, uint(1000000002533612379502790461), 99.9112*10**25);
                            // risk group: 23 - APR: 8.22%
                            file("riskGroup", 23, ONE, ONE, uint(1000000002606544901065449010), 99.9079*10**25);
                            // risk group: 24 - APR: 8.45%
                            file("riskGroup", 24, ONE, ONE, uint(1000000002679477422628107559), 99.9047*10**25);
                            // risk group: 25 - APR: 8.68%
                            file("riskGroup", 25, ONE, ONE, uint(1000000002752409944190766108), 99.9014*10**25);
                            // risk group: 26 - APR: 8.91%
                            file("riskGroup", 26, ONE, ONE, uint(1000000002825342465753424657), 99.8981*10**25);
                            // risk group: 27 - APR: 9.14%
                            file("riskGroup", 27, ONE, ONE, uint(1000000002898274987316083206), 99.8948*10**25);
                            // risk group: 28 - APR: 9.36%
                            file("riskGroup", 28, ONE, ONE, uint(1000000002968036529680365296), 99.8915*10**25);
                            // risk group: 29 - APR: 9.59%
                            file("riskGroup", 29, ONE, ONE, uint(1000000003040969051243023845), 99.8882*10**25);
                            // risk group: 30 - APR: 9.82%
                            file("riskGroup", 30, ONE, ONE, uint(1000000003113901572805682394), 99.8849*10**25);
                            // risk group: 31 - APR: 10.04%
                            file("riskGroup", 31, ONE, ONE, uint(1000000003183663115169964485), 99.8816*10**25);
                            // risk group: 32 - APR: 10.27%
                            file("riskGroup", 32, ONE, ONE, uint(1000000003256595636732623033), 99.8784*10**25);
                            // risk group: 33 - APR: 10.5%
                            file("riskGroup", 33, ONE, ONE, uint(1000000003329528158295281582), 99.8751*10**25);
                            // risk group: 34 - APR: 10.72%
                            file("riskGroup", 34, ONE, ONE, uint(1000000003399289700659563673), 99.8718*10**25);
                            // risk group: 35 - APR: 10.95%
                            file("riskGroup", 35, ONE, ONE, uint(1000000003472222222222222222), 99.8685*10**25);
                            // risk group: 36 - APR: 11.17%
                            file("riskGroup", 36, ONE, ONE, uint(1000000003541983764586504312), 99.8652*10**25);
                            // risk group: 37 - APR: 11.39%
                            file("riskGroup", 37, ONE, ONE, uint(1000000003611745306950786402), 99.8619*10**25);
                            // risk group: 38 - APR: 11.62%
                            file("riskGroup", 38, ONE, ONE, uint(1000000003684677828513444951), 99.8586*10**25);
                            // risk group: 39 - APR: 11.84%
                            file("riskGroup", 39, ONE, ONE, uint(1000000003754439370877727042), 99.8553*10**25);
                            // risk group: 40 - APR: 12.06% 
                            file("riskGroup", 40, ONE, ONE, uint(1000000003824200913242009132), 99.8521*10**25);
                                    
                            /// Overdue loans (= loans that were not repaid by the maturityDate) are moved to write Offs
                            // write-off group: 0 - 0% write off, 7.50% interest
                            setWriteOff(0, WRITE_OFF_PHASE_A, uint(1000000002378234398782343987), ONE);
                            // write-off group: 1 - 10% write off, 7.50% interest
                            setWriteOff(1, WRITE_OFF_PHASE_B, uint(1000000002378234398782343987), 90*10**25);
                            // write-off group: 2 - 25% write off
                            setWriteOff(2, WRITE_OFF_PHASE_C, uint(1000000000000000000000000000), 75*10**25);
                            // write-off group: 3 - 50% write off
                            setWriteOff(3, WRITE_OFF_PHASE_D, uint(1000000000000000000000000000), 50*10**25);
                            // write-off group: 4 - 100% write off
                            setWriteOff(4, WRITE_OFF_PHASE_E, uint(1000000000000000000000000000), 0);
                    
                    
                        }
                    
                        function file(bytes32 name, uint risk_, uint thresholdRatio_, uint ceilingRatio_, uint rate_, uint recoveryRatePD_) public auth  {
                            if(name == "riskGroup") {
                                file("riskGroupNFT", risk_, thresholdRatio_, ceilingRatio_, rate_);
                                recoveryRatePD[risk_] = Fixed27(recoveryRatePD_);
                    
                            } else {revert ("unknown name");}
                        }
                    
                        function setWriteOff(uint phase_, uint group_, uint rate_, uint writeOffPercentage_) internal {
                            writeOffs[phase_] = WriteOff(group_, Fixed27(writeOffPercentage_));
                            pile.file("rate", group_, rate_);
                        }
                    
                        function uniqueDayTimestamp(uint timestamp) public pure returns (uint) {
                            return (1 days) * (timestamp/(1 days));
                        }
                    
                        /// maturityDate is a unix timestamp
                        function file(bytes32 name, bytes32 nftID_, uint maturityDate_) public auth {
                            // maturity date only can be changed when there is no debt on the collateral -> futureValue == 0
                            if (name == "maturityDate") {
                                require((futureValue[nftID_] == 0), "can-not-change-maturityDate-outstanding-debt");
                                maturityDate[nftID_] = uniqueDayTimestamp(maturityDate_);
                            } else { revert("unknown config parameter");}
                        }
                    
                        function file(bytes32 name, uint value) public auth {
                            if (name == "discountRate") {
                                discountRate = Fixed27(value);
                            } else { revert("unknown config parameter");}
                        }
                    
                        // In case of successful borrow the approximatedNAV is increased by the borrowed amount
                        function borrow(uint loan, uint amount) external auth returns(uint navIncrease) {
                            navIncrease = _borrow(loan, amount);
                            approximatedNAV = safeAdd(approximatedNAV, navIncrease);
                            return navIncrease;
                        }
                    
                        // On borrow: the discounted future value of the asset is computed based on the loan amount and addeed to the bucket with the according maturity Date
                        function _borrow(uint loan, uint amount) internal returns(uint navIncrease) {
                            // ceiling check uses existing loan debt
                            require(ceiling(loan) >= safeAdd(borrowed[loan], amount), "borrow-amount-too-high");
                    
                            bytes32 nftID_ = nftID(loan);
                            uint maturityDate_ = maturityDate[nftID_];
                            // maturity date has to be a value in the future
                            require(maturityDate_ > block.timestamp, "maturity-date-is-not-in-the-future");
                    
                            // calculate amount including fixed fee if applicatable
                            (, , , , uint fixedRate) = pile.rates(pile.loanRates(loan));
                            uint amountIncludingFixed =  safeAdd(amount, rmul(amount, fixedRate));
                            // calculate future value FV
                            uint fv = calcFutureValue(loan, amountIncludingFixed, maturityDate_, recoveryRatePD[risk[nftID_]].value);
                            futureValue[nftID_] = safeAdd(futureValue[nftID_], fv);
                    
                            // add future value to the bucket of assets with the same maturity date
                            if (buckets[maturityDate_].value == 0) {
                                addBucket(maturityDate_, fv);
                            } else {
                                buckets[maturityDate_].value = safeAdd(buckets[maturityDate_].value, fv);
                            }
                    
                            // increase borrowed amount for future ceiling computations
                            borrowed[loan] = safeAdd(borrowed[loan], amount);
                    
                            // return increase NAV amount
                            return calcDiscount(fv, uniqueDayTimestamp(block.timestamp), maturityDate_);
                        }
                    
                        // calculate the future value based on the amount, maturityDate interestRate and recoveryRate
                        function calcFutureValue(uint loan, uint amount, uint maturityDate_, uint recoveryRatePD_) public returns(uint) {
                            // retrieve interest rate from the pile
                            (, ,uint loanInterestRate, ,) = pile.rates(pile.loanRates(loan));
                            return rmul(rmul(rpow(loanInterestRate, safeSub(maturityDate_, uniqueDayTimestamp(now)), ONE), amount), recoveryRatePD_);
                        }
                    
                        /// update the nft value and change the risk group
                        function update(bytes32 nftID_, uint value, uint risk_) public auth {
                            nftValues[nftID_] = value;
                    
                            // no change in risk group
                            if (risk_ == risk[nftID_]) {
                                return;
                            }
                    
                            // nfts can only be added to risk groups that are part of the score card
                            require(thresholdRatio[risk_] != 0, "risk group not defined in contract");
                            risk[nftID_] = risk_;
                    
                            // no currencyAmount borrowed yet
                            if (futureValue[nftID_] == 0) {
                                return;
                            }
                    
                            uint loan = shelf.nftlookup(nftID_);
                            uint maturityDate_ = maturityDate[nftID_];
                    
                            // Changing the risk group of an nft, might lead to a new interest rate for the dependant loan.
                            // New interest rate leads to a future value.
                            // recalculation required
                            buckets[maturityDate_].value = safeSub(buckets[maturityDate_].value, futureValue[nftID_]);
                    
                            futureValue[nftID_] = calcFutureValue(loan, pile.debt(loan), maturityDate[nftID_], recoveryRatePD[risk[nftID_]].value);
                            buckets[maturityDate_].value = safeAdd(buckets[maturityDate_].value, futureValue[nftID_]);
                        }
                    
                        // In case of successful repayment the approximatedNAV is decreased by the repaid amount
                        function repay(uint loan, uint amount) external auth returns (uint navDecrease) {
                            navDecrease = _repay(loan, amount);
                            if (navDecrease > approximatedNAV) {
                                approximatedNAV = 0;
                            }
                    
                            if(navDecrease < approximatedNAV) {
                                approximatedNAV = safeSub(approximatedNAV, navDecrease);
                                return navDecrease;
                            }
                    
                            approximatedNAV = 0;
                            return navDecrease;
                        }
                    
                        // On repayment: adjust future value bucket according to repayment amount
                        function _repay(uint loan, uint amount) internal returns (uint navDecrease) {
                            bytes32 nftID_ = nftID(loan);
                            uint maturityDate_ = maturityDate[nftID_];
                    
                    
                            // no fv decrease calculation needed if maturity date is in the past
                            if (maturityDate_ < uniqueDayTimestamp(block.timestamp)) {
                                // if a loan is overdue, the portfolio value is initially equal to the existing debt
                                // it will be reduced by a write off factor once it is moved to a write off group
                                return amount;
                            }
                    
                            // remove future value for loan from bucket
                            buckets[maturityDate_].value = safeSub(buckets[maturityDate_].value, futureValue[nftID_]);
                    
                            uint debt = pile.debt(loan);
                            debt = safeSub(debt, amount);
                    
                            uint fv = 0;
                            uint preFutureValue = futureValue[nftID_];
                    
                            // in case of partial repayment, compute the fv of the remaining debt and add to the according fv bucket
                            if (debt != 0) {
                                fv = calcFutureValue(loan, debt, maturityDate_, recoveryRatePD[risk[nftID_]].value);
                                buckets[maturityDate_].value = safeAdd(buckets[maturityDate_].value, fv);
                            }
                    
                            futureValue[nftID_] = fv;
                    
                            // remove buckets if no remaining assets
                            if (buckets[maturityDate_].value == 0 && firstBucket != 0) {
                                removeBucket(maturityDate_);
                            }
                    
                            // return decrease NAV amount
                            return calcDiscount(safeSub(preFutureValue, fv), uniqueDayTimestamp(block.timestamp), maturityDate_);
                        }
                    
                        function calcDiscount(uint amount, uint normalizedBlockTimestamp, uint maturityDate_) public view returns (uint result) {
                            return rdiv(amount, rpow(discountRate.value, safeSub(maturityDate_, normalizedBlockTimestamp), ONE));
                        }
                    
                    
                        /// calculates the total discount of all buckets with a timestamp > block.timestamp
                        function calcTotalDiscount() public view returns(uint) {
                            uint normalizedBlockTimestamp = uniqueDayTimestamp(block.timestamp);
                            uint sum = 0;
                    
                            uint currDate = normalizedBlockTimestamp;
                    
                            if (currDate > lastBucket) {
                                return 0;
                            }
                    
                            // only buckets after the block.timestamp are relevant for the discount
                            // assuming its more gas efficient to iterate over time to find the first one instead of iterating the list from the beginning
                            // not true if buckets are only sparsely populated over long periods of time
                            while(buckets[currDate].next == 0) { currDate = currDate + 1 days; }
                    
                            while(currDate != NullDate)
                            {
                                sum = safeAdd(sum, calcDiscount(buckets[currDate].value, normalizedBlockTimestamp, currDate));
                                currDate = buckets[currDate].next;
                            }
                            return sum;
                        }
                    
                        /// returns the NAV (net asset value) of the pool
                        function currentNAV() public view returns(uint) {
                            // calculates the NAV for ongoing loans with a maturityDate date in the future
                            uint nav_ = calcTotalDiscount();
                            // include ovedue assets to the current NAV calculation
                            for (uint i = 0; i < writeOffs.length; i++) {
                                // multiply writeOffGroupDebt with the writeOff rate
                                nav_ = safeAdd(nav_, rmul(pile.rateDebt(writeOffs[i].rateGroup), writeOffs[i].percentage.value));
                            }
                            return nav_;
                        }
                    
                        function calcUpdateNAV() public returns(uint) {
                            // approximated NAV is updated and at this point in time 100% correct
                            approximatedNAV = currentNAV();
                            return approximatedNAV;
                        }
                    
                        /// workaround for transition phase between V2 & V3
                        function totalValue() public view returns(uint) {
                            return currentNAV();
                        }
                    
                        function dateBucket(uint timestamp) public view returns (uint) {
                            return buckets[timestamp].value;
                        }
                    }
                    

                    File 7 of 7: Assessor
                    // Verified using https://dapp.tools
                    
                    // hevm: flattened sources of src/lender/assessor.sol
                    pragma solidity >=0.5.15 >=0.5.15 <0.6.0;
                    
                    ////// lib/tinlake-auth/lib/ds-note/src/note.sol
                    /// note.sol -- the `note' modifier, for logging calls as events
                    
                    // 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.15; */
                    
                    contract DSNote {
                        event LogNote(
                            bytes4   indexed  sig,
                            address  indexed  guy,
                            bytes32  indexed  foo,
                            bytes32  indexed  bar,
                            uint256           wad,
                            bytes             fax
                        ) anonymous;
                    
                        modifier note {
                            bytes32 foo;
                            bytes32 bar;
                            uint256 wad;
                    
                            assembly {
                                foo := calldataload(4)
                                bar := calldataload(36)
                                wad := callvalue()
                            }
                    
                            _;
                    
                            emit LogNote(msg.sig, msg.sender, foo, bar, wad, msg.data);
                        }
                    }
                    
                    ////// lib/tinlake-auth/src/auth.sol
                    // Copyright (C) Centrifuge 2020, based on MakerDAO dss https://github.com/makerdao/dss
                    //
                    // 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.15 <0.6.0; */
                    
                    /* import "ds-note/note.sol"; */
                    
                    contract Auth is DSNote {
                        mapping (address => uint) public wards;
                        function rely(address usr) public auth note { wards[usr] = 1; }
                        function deny(address usr) public auth note { wards[usr] = 0; }
                        modifier auth { require(wards[msg.sender] == 1); _; }
                    }
                    
                    ////// lib/tinlake-math/src/math.sol
                    // Copyright (C) 2018 Rain <[email protected]>
                    //
                    // 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.15 <0.6.0; */
                    
                    contract Math {
                        uint256 constant ONE = 10 ** 27;
                    
                        function safeAdd(uint x, uint y) public pure returns (uint z) {
                            require((z = x + y) >= x, "safe-add-failed");
                        }
                    
                        function safeSub(uint x, uint y) public pure returns (uint z) {
                            require((z = x - y) <= x, "safe-sub-failed");
                        }
                    
                        function safeMul(uint x, uint y) public pure returns (uint z) {
                            require(y == 0 || (z = x * y) / y == x, "safe-mul-failed");
                        }
                    
                        function safeDiv(uint x, uint y) public pure returns (uint z) {
                            z = x / y;
                        }
                    
                        function rmul(uint x, uint y) public pure returns (uint z) {
                            z = safeMul(x, y) / ONE;
                        }
                    
                        function rdiv(uint x, uint y) public pure returns (uint z) {
                            require(y > 0, "division by zero");
                            z = safeAdd(safeMul(x, ONE), y / 2) / y;
                        }
                    
                        function rdivup(uint x, uint y) internal pure returns (uint z) {
                            require(y > 0, "division by zero");
                            // always rounds up
                            z = safeAdd(safeMul(x, ONE), safeSub(y, 1)) / y;
                        }
                    
                    
                    }
                    
                    ////// lib/tinlake-math/src/interest.sol
                    // Copyright (C) 2018 Rain <[email protected]> and Centrifuge, referencing MakerDAO dss => https://github.com/makerdao/dss/blob/master/src/pot.sol
                    //
                    // 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.15 <0.6.0; */
                    
                    /* import "./math.sol"; */
                    
                    contract Interest is Math {
                        // @notice This function provides compounding in seconds
                        // @param chi Accumulated interest rate over time
                        // @param ratePerSecond Interest rate accumulation per second in RAD(10ˆ27)
                        // @param lastUpdated When the interest rate was last updated
                        // @param pie Total sum of all amounts accumulating under one interest rate, divided by that rate
                        // @return The new accumulated rate, as well as the difference between the debt calculated with the old and new accumulated rates.
                        function compounding(uint chi, uint ratePerSecond, uint lastUpdated, uint pie) public view returns (uint, uint) {
                            require(block.timestamp >= lastUpdated, "tinlake-math/invalid-timestamp");
                            require(chi != 0);
                            // instead of a interestBearingAmount we use a accumulated interest rate index (chi)
                            uint updatedChi = _chargeInterest(chi ,ratePerSecond, lastUpdated, block.timestamp);
                            return (updatedChi, safeSub(rmul(updatedChi, pie), rmul(chi, pie)));
                        }
                    
                        // @notice This function charge interest on a interestBearingAmount
                        // @param interestBearingAmount is the interest bearing amount
                        // @param ratePerSecond Interest rate accumulation per second in RAD(10ˆ27)
                        // @param lastUpdated last time the interest has been charged
                        // @return interestBearingAmount + interest
                        function chargeInterest(uint interestBearingAmount, uint ratePerSecond, uint lastUpdated) public view returns (uint) {
                            if (block.timestamp >= lastUpdated) {
                                interestBearingAmount = _chargeInterest(interestBearingAmount, ratePerSecond, lastUpdated, block.timestamp);
                            }
                            return interestBearingAmount;
                        }
                    
                        function _chargeInterest(uint interestBearingAmount, uint ratePerSecond, uint lastUpdated, uint current) internal pure returns (uint) {
                            return rmul(rpow(ratePerSecond, current - lastUpdated, ONE), interestBearingAmount);
                        }
                    
                    
                        // convert pie to debt/savings amount
                        function toAmount(uint chi, uint pie) public pure returns (uint) {
                            return rmul(pie, chi);
                        }
                    
                        // convert debt/savings amount to pie
                        function toPie(uint chi, uint amount) public pure returns (uint) {
                            return rdivup(amount, chi);
                        }
                    
                        function rpow(uint x, uint n, uint base) public pure returns (uint z) {
                            assembly {
                                switch x case 0 {switch n case 0 {z := base} default {z := 0}}
                                default {
                                    switch mod(n, 2) case 0 { z := base } default { z := x }
                                    let half := div(base, 2)  // for rounding.
                                    for { n := div(n, 2) } n { n := div(n,2) } {
                                    let xx := mul(x, x)
                                    if iszero(eq(div(xx, x), x)) { revert(0,0) }
                                    let xxRound := add(xx, half)
                                    if lt(xxRound, xx) { revert(0,0) }
                                    x := div(xxRound, base)
                                    if mod(n,2) {
                                        let zx := mul(z, x)
                                        if and(iszero(iszero(x)), iszero(eq(div(zx, x), z))) { revert(0,0) }
                                        let zxRound := add(zx, half)
                                        if lt(zxRound, zx) { revert(0,0) }
                                        z := div(zxRound, base)
                                    }
                                }
                                }
                            }
                        }
                    }
                    
                    ////// src/fixed_point.sol
                    // Copyright (C) 2020 Centrifuge
                    // 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.15 <0.6.0; */
                    
                    contract FixedPoint {
                        struct Fixed27 {
                            uint value;
                        }
                    }
                    
                    ////// src/lender/assessor.sol
                    // Copyright (C) 2020 Centrifuge
                    // 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.15 <0.6.0; */
                    
                    /* import "./../fixed_point.sol"; */
                    /* import "tinlake-auth/auth.sol"; */
                    /* import "tinlake-math/interest.sol"; */
                    
                    interface NAVFeedLike_2 {
                        function calcUpdateNAV() external returns (uint);
                        function approximatedNAV() external view returns (uint);
                        function currentNAV() external view returns(uint);
                    }
                    
                    interface TrancheLike_2 {
                        function tokenSupply() external view returns (uint);
                    }
                    
                    interface ReserveLike_2 {
                        function totalBalance() external view returns(uint);
                        function file(bytes32 what, uint currencyAmount) external;
                    }
                    
                    contract Assessor is Auth, FixedPoint, Interest {
                        // senior ratio from the last epoch executed
                        Fixed27        public seniorRatio;
                    
                        // the seniorAsset value is stored in two variables
                        // seniorDebt is the interest bearing amount for senior
                        uint           public seniorDebt_;
                        // senior balance is the rest which is not used as interest
                        // bearing amount
                        uint           public seniorBalance_;
                    
                        // interest rate per second for senior tranche
                        Fixed27         public seniorInterestRate;
                    
                        // last time the senior interest has been updated
                        uint            public lastUpdateSeniorInterest;
                    
                        Fixed27         public maxSeniorRatio;
                        Fixed27         public minSeniorRatio;
                    
                        uint            public maxReserve;
                    
                        TrancheLike_2     public seniorTranche;
                        TrancheLike_2     public juniorTranche;
                        NAVFeedLike_2     public navFeed;
                        ReserveLike_2     public reserve;
                    
                        constructor() public {
                            wards[msg.sender] = 1;
                            seniorInterestRate.value = ONE;
                            lastUpdateSeniorInterest = block.timestamp;
                            seniorRatio.value = 0;
                        }
                    
                        function depend(bytes32 contractName, address addr) public auth {
                            if (contractName == "navFeed") {
                                navFeed = NAVFeedLike_2(addr);
                            } else if (contractName == "seniorTranche") {
                                seniorTranche = TrancheLike_2(addr);
                            } else if (contractName == "juniorTranche") {
                                juniorTranche = TrancheLike_2(addr);
                            } else if (contractName == "reserve") {
                                reserve = ReserveLike_2(addr);
                            } else revert();
                        }
                    
                        function file(bytes32 name, uint value) public auth {
                            if(name == "seniorInterestRate") {
                                seniorInterestRate = Fixed27(value);
                            }
                            else if (name == "maxReserve") {maxReserve = value;}
                            else if (name == "maxSeniorRatio") {
                                require(value > minSeniorRatio.value, "value-too-small");
                                maxSeniorRatio = Fixed27(value);
                            }
                            else if (name == "minSeniorRatio") {
                                require(value < maxSeniorRatio.value, "value-too-big");
                                minSeniorRatio = Fixed27(value);
                            }
                            else {revert("unknown-variable");}
                        }
                    
                        function reBalance(uint seniorAsset_, uint seniorRatio_) internal {
                            // re-balancing according to new ratio
                            // we use the approximated NAV here because during the submission period
                            // new loans might have been repaid in the meanwhile which are not considered in the epochNAV
                            seniorDebt_ = rmul(navFeed.approximatedNAV(), seniorRatio_);
                            if(seniorDebt_ > seniorAsset_) {
                                seniorDebt_ = seniorAsset_;
                                seniorBalance_ = 0;
                                return;
                            }
                            seniorBalance_ = safeSub(seniorAsset_, seniorDebt_);
                        }
                    
                        function changeSeniorAsset(uint seniorRatio_, uint seniorSupply, uint seniorRedeem) external auth {
                            dripSeniorDebt();
                            uint seniorAsset = safeSub(safeAdd(safeAdd(seniorDebt_, seniorBalance_),seniorSupply), seniorRedeem);
                            reBalance(seniorAsset, seniorRatio_);
                            seniorRatio = Fixed27(seniorRatio_);
                        }
                    
                        function seniorRatioBounds() public view returns (uint minSeniorRatio_, uint maxSeniorRatio_) {
                            return (minSeniorRatio.value, maxSeniorRatio.value);
                        }
                    
                        function calcUpdateNAV() external returns (uint) {
                             return navFeed.calcUpdateNAV();
                        }
                    
                        function calcSeniorTokenPrice() external view returns(uint) {
                            return calcSeniorTokenPrice(navFeed.currentNAV(), reserve.totalBalance());
                        }
                    
                        function calcJuniorTokenPrice() external view returns(uint) {
                            return calcJuniorTokenPrice(navFeed.currentNAV(), reserve.totalBalance());
                        }
                    
                        function calcTokenPrices() external view returns (uint, uint) {
                            uint epochNAV = navFeed.currentNAV();
                            uint epochReserve = reserve.totalBalance();
                            return calcTokenPrices(epochNAV, epochReserve);
                        }
                    
                        function calcTokenPrices(uint epochNAV, uint epochReserve) public view returns (uint, uint) {
                            return (calcJuniorTokenPrice(epochNAV, epochReserve), calcSeniorTokenPrice(epochNAV, epochReserve));
                        }
                    
                        function calcSeniorTokenPrice(uint epochNAV, uint epochReserve) public view returns(uint) {
                            if ((epochNAV == 0 && epochReserve == 0) || seniorTranche.tokenSupply() == 0) {
                                // initial token price at start 1.00
                                return ONE;
                            }
                            uint totalAssets = safeAdd(epochNAV, epochReserve);
                            uint seniorAssetValue = calcSeniorAssetValue(seniorDebt(), seniorBalance_);
                    
                            if(totalAssets < seniorAssetValue) {
                                seniorAssetValue = totalAssets;
                            }
                            return rdiv(seniorAssetValue, seniorTranche.tokenSupply());
                        }
                    
                        function calcJuniorTokenPrice(uint epochNAV, uint epochReserve) public view returns(uint) {
                            if ((epochNAV == 0 && epochReserve == 0) || juniorTranche.tokenSupply() == 0) {
                                // initial token price at start 1.00
                                return ONE;
                            }
                            uint totalAssets = safeAdd(epochNAV, epochReserve);
                            uint seniorAssetValue = calcSeniorAssetValue(seniorDebt(), seniorBalance_);
                    
                            if(totalAssets < seniorAssetValue) {
                                return 0;
                            }
                    
                            return rdiv(safeSub(totalAssets, seniorAssetValue), juniorTranche.tokenSupply());
                        }
                    
                        /// repayment update keeps track of senior bookkeeping for repaid loans
                        /// the seniorDebt needs to be decreased
                        function repaymentUpdate(uint currencyAmount) public auth {
                            dripSeniorDebt();
                    
                            uint decAmount = rmul(currencyAmount, seniorRatio.value);
                    
                            if (decAmount > seniorDebt_) {
                                seniorBalance_ = calcSeniorAssetValue(seniorDebt_, seniorBalance_);
                                seniorDebt_ = 0;
                                return;
                            }
                    
                            seniorBalance_ = safeAdd(seniorBalance_, decAmount);
                            // seniorDebt needs to be decreased for loan repayments
                            seniorDebt_ = safeSub(seniorDebt_, decAmount);
                            lastUpdateSeniorInterest = block.timestamp;
                    
                        }
                        /// borrow update keeps track of the senior bookkeeping for new borrowed loans
                        /// the seniorDebt needs to be increased to accumulate interest
                        function borrowUpdate(uint currencyAmount) public auth {
                            dripSeniorDebt();
                    
                            // the current senior ratio defines
                            // interest bearing amount (seniorDebt) increase
                            uint incAmount = rmul(currencyAmount, seniorRatio.value);
                    
                            // this case should most likely never happen
                            if (incAmount > seniorBalance_) {
                                // all the currency of senior is used as interest bearing currencyAmount
                                seniorDebt_ = calcSeniorAssetValue(seniorDebt_, seniorBalance_);
                                seniorBalance_ = 0;
                                return;
                            }
                    
                            // seniorDebt needs to be increased for loan borrows
                            seniorDebt_ = safeAdd(seniorDebt_, incAmount);
                            seniorBalance_ = safeSub(seniorBalance_, incAmount);
                            lastUpdateSeniorInterest = block.timestamp;
                        }
                    
                        function calcSeniorAssetValue(uint _seniorDebt, uint _seniorBalance) public pure returns(uint) {
                            return safeAdd(_seniorDebt, _seniorBalance);
                        }
                    
                        function dripSeniorDebt() public returns (uint) {
                            uint newSeniorDebt = seniorDebt();
                    
                            if (newSeniorDebt > seniorDebt_) {
                                seniorDebt_ = newSeniorDebt;
                                lastUpdateSeniorInterest = block.timestamp;
                            }
                    
                            return seniorDebt_;
                        }
                    
                        function seniorDebt() public view returns (uint) {
                            if (now >= lastUpdateSeniorInterest) {
                                return chargeInterest(seniorDebt_, seniorInterestRate.value, lastUpdateSeniorInterest);
                            }
                            return seniorDebt_;
                        }
                    
                        function seniorBalance() public view returns (uint) {
                            return seniorBalance_;
                        }
                    
                        function totalBalance() public view returns (uint) {
                            return reserve.totalBalance();
                        }
                    
                        // changes the total amount available for borrowing loans
                        function changeReserveAvailable(uint currencyAmount) public auth {
                            reserve.file("currencyAvailable", currencyAmount);
                        }
                    }