Burrowsource, exactly what is deployedall files

src/BurrowVaultLite.sol

// SPDX-License-Identifier: MIT
pragma solidity 0.8.26;

interface IERC20 {
    function transfer(address to, uint256 v) external returns (bool);
    function transferFrom(address f, address t, uint256 v) external returns (bool);
    function balanceOf(address a) external view returns (uint256);
}
interface IERC721 {
    function transferFrom(address f, address t, uint256 id) external;
    function setApprovalForAll(address op, bool ok) external;
    function ownerOf(uint256 id) external view returns (address);
}
interface IStaking {
    function deposit(address collection, uint256[] calldata tokenIds) external;
    function withdraw(address collection, uint256[] calldata tokenIds) external;
    function claim(address recipient) external;
    function setClaimRecipient(address recipient) external;
    function claimable(address account) external view returns (uint256);
    function accountPower(address account) external view returns (uint256);
}
interface IValuer {
    function creditLine(uint256[] calldata tokenIds) external view returns (uint256);
    function ltvBps() external view returns (uint256);
    function lastPriceChange() external view returns (uint256);
}

/// @title Burrow vault, lean build
/// @notice Lenders supply SQK and hold bSQK. Borrowers escrow Squeek NFTs, which the
/// vault re-stakes on Squeekchain, and borrow SQK against the mining power of those
/// NFTs. Staking rewards earned by the escrowed collateral pay the debt down by
/// themselves. Marketplace and v4 swap fees arrive through donate() and raise the
/// share price. Deliberately small: Sepolia currently charges about 1,550 gas per byte
/// of deployed code against a 16,777,216 gas cap per transaction, so a contract has to
/// stay near 10 KB to exist at all.
contract BurrowVaultLite {
    // --- ERC20 (bSQK) ---
    string public constant name = "Burrow SQK";
    string public constant symbol = "bSQK";
    uint8 public constant decimals = 18;
    uint256 public totalSupply;
    mapping(address => uint256) public balanceOf;
    mapping(address => mapping(address => uint256)) public allowance;
    event Transfer(address indexed from, address indexed to, uint256 value);
    event Approval(address indexed owner, address indexed spender, uint256 value);

    // --- config ---
    IERC20 public immutable sqk;
    IERC721 public immutable squeek;
    IStaking public immutable staking;
    IValuer public immutable valuer;
    uint256 public immutable ratePerSecond;      // 1e18 scale
    uint256 public constant MAX_RATE = uint256(0.5e18) / 365 days;
    uint256 public constant MINIMUM_LIQUIDITY = 1000;
    uint256 public constant LIQ_BONUS_BPS = 1000;        // liquidator pays value less this
    uint256 public constant LIQ_THRESHOLD_BPS = 7500;    // debt/value that opens liquidation; borrow cap is the 60% line
    uint256 public constant MAX_IDS = 32;
    uint256 public constant FREEZE_AFTER_PRICE_MOVE = 1 hours;

    // --- lending state ---
    struct Position { uint256 principal; uint256 index; uint256 power; uint256 rewardMark; uint256[] ids; }
    mapping(address => Position) private _pos;
    uint256 public borrowIndex = 1e18;           // compounding debt index
    uint256 public lastAccrual;
    uint256 public scaledDebt;                   // sum of principal/index, 1e18 scale
    uint256 public totalPower;                   // escrowed NFT power
    uint256 public rewardPerPower;               // 1e18 scale, grows on sweep
    uint256 public unsettled;                    // swept SQK not yet credited to any debt; not lender money
    uint256 public tracked;                      // SQK this contract moved itself; the rest is rewards

    event Deposit(address indexed who, uint256 assets, uint256 shares);
    event Withdraw(address indexed who, uint256 shares, uint256 assets);
    event Opened(address indexed who, uint256 power);
    event Borrowed(address indexed who, uint256 amount);
    event Repaid(address indexed who, uint256 amount);
    event Closed(address indexed who);
    event Swept(uint256 amount, uint256 perPower);
    event Donated(address indexed from, uint256 amount);
    event Liquidated(address indexed borrower, address indexed by, uint256 debt, uint256 paid);
    event Surplus(address indexed who, uint256 amount);

    error Zero();
    error BadIds();
    error NoPosition();
    error HasPosition();
    error OverLine(uint256 debt, uint256 line);
    error Healthy();
    error Frozen();
    error NoLiquidity(uint256 want, uint256 have);
    error Slip();
    error MinFirstDeposit();
    error DebtOutstanding();

    constructor(address sqk_, address squeek_, address staking_, address valuer_, uint256 rate_) {
        if (sqk_.code.length == 0 || squeek_.code.length == 0 || staking_.code.length == 0 || valuer_.code.length == 0) revert Zero();
        if (rate_ > MAX_RATE) revert Zero();
        sqk = IERC20(sqk_); squeek = IERC721(squeek_); staking = IStaking(staking_); valuer = IValuer(valuer_);
        ratePerSecond = rate_; lastAccrual = block.timestamp;
        IERC721(squeek_).setApprovalForAll(staking_, true);
        IStaking(staking_).setClaimRecipient(address(this));
    }

    // --- ERC20 ---
    function approve(address s, uint256 v) external returns (bool) { allowance[msg.sender][s] = v; emit Approval(msg.sender, s, v); return true; }
    function transfer(address t, uint256 v) external returns (bool) { _move(msg.sender, t, v); return true; }
    function transferFrom(address f, address t, uint256 v) external returns (bool) {
        uint256 a = allowance[f][msg.sender];
        if (a != type(uint256).max) allowance[f][msg.sender] = a - v;
        _move(f, t, v); return true;
    }
    function _move(address f, address t, uint256 v) private {
        if (t == address(0)) revert Zero();
        balanceOf[f] -= v; unchecked { balanceOf[t] += v; } emit Transfer(f, t, v);
    }
    function _mint(address t, uint256 v) private { totalSupply += v; unchecked { balanceOf[t] += v; } emit Transfer(address(0), t, v); }
    function _burn(address f, uint256 v) private { balanceOf[f] -= v; unchecked { totalSupply -= v; } emit Transfer(f, address(0), v); }

    // --- accounting ---
    function accrue() public {
        uint256 dt = block.timestamp - lastAccrual;
        if (dt == 0) return;
        lastAccrual = block.timestamp;
        if (scaledDebt != 0) borrowIndex += borrowIndex * ratePerSecond * dt / 1e18;
    }
    function totalBorrows() public view returns (uint256) {
        uint256 dt = block.timestamp - lastAccrual;
        uint256 idx = scaledDebt == 0 ? borrowIndex : borrowIndex + borrowIndex * ratePerSecond * dt / 1e18;
        return scaledDebt * idx / 1e18;
    }
    /// @dev Swept rewards sit in this contract until a borrower settles, so they are
    /// excluded here and from idle liquidity; otherwise the share price would jump on
    /// every sweep and fall again as positions settle.
    function totalAssets() public view returns (uint256) { return idle() + totalBorrows(); }
    function idle() public view returns (uint256) { return tracked - unsettled; }
    function convertToShares(uint256 a) public view returns (uint256) {
        uint256 s = totalSupply; return s == 0 ? a : a * s / totalAssets();
    }
    function convertToAssets(uint256 sh) public view returns (uint256) {
        uint256 s = totalSupply; return s == 0 ? sh : sh * totalAssets() / s;
    }

    function debtOf(address b) public view returns (uint256) {
        Position storage p = _pos[b];
        if (p.index == 0) return 0;
        uint256 dt = block.timestamp - lastAccrual;
        uint256 idx = scaledDebt == 0 ? borrowIndex : borrowIndex + borrowIndex * ratePerSecond * dt / 1e18;
        uint256 gross = p.principal * idx / p.index;
        uint256 credit = p.power * (rewardPerPower - p.rewardMark) / 1e18;
        return credit >= gross ? 0 : gross - credit;
    }
    function creditLineOf(address b) public view returns (uint256) {
        Position storage p = _pos[b];
        return p.ids.length == 0 ? 0 : valuer.creditLine(p.ids);
    }
    function positionIds(address b) external view returns (uint256[] memory) { return _pos[b].ids; }
    /// @notice Debt above which `liquidate` is open for this borrower.
    function liquidationThresholdOf(address b) external view returns (uint256) { return creditLineOf(b) * 10000 / valuer.ltvBps() * LIQ_THRESHOLD_BPS / 10000; }
    function positionPower(address b) external view returns (uint256) { return _pos[b].power; }

    function _settle(address b) private returns (Position storage p) {
        accrue();
        p = _pos[b];
        if (p.index == 0) return p;
        sweepRewards();                        // credit everything mined up to this moment
        uint256 gross = p.principal * borrowIndex / p.index;
        uint256 credit = p.power * (rewardPerPower - p.rewardMark) / 1e18;
        unsettled = credit > unsettled ? 0 : unsettled - credit;   // rounding dust only
        uint256 net = credit >= gross ? 0 : gross - credit;
        scaledDebt -= p.principal * 1e18 / p.index;
        p.principal = net;
        p.index = borrowIndex;
        p.rewardMark = rewardPerPower;
        scaledDebt += net * 1e18 / borrowIndex;
        if (credit > gross) { _push(b, credit - gross); emit Surplus(b, credit - gross); }
    }

    // --- lenders ---
    function deposit(uint256 assets, uint256 minShares) external returns (uint256 shares) {
        if (assets == 0) revert Zero();
        accrue();
        if (totalSupply == 0) {
            if (assets <= MINIMUM_LIQUIDITY) revert MinFirstDeposit();
            _mint(address(this), totalAssets() + MINIMUM_LIQUIDITY);
            shares = assets - MINIMUM_LIQUIDITY;
        } else {
            shares = convertToShares(assets);
        }
        if (shares == 0 || shares < minShares) revert Slip();
        _mint(msg.sender, shares);
        _pull(msg.sender, assets);
        emit Deposit(msg.sender, assets, shares);
    }

    function withdraw(uint256 shares, uint256 minAssets) external returns (uint256 assets) {
        if (shares == 0) revert Zero();
        accrue();
        assets = convertToAssets(shares);
        if (assets == 0 || assets < minAssets) revert Slip();
        uint256 cash = idle();
        if (assets > cash) revert NoLiquidity(assets, cash);
        _burn(msg.sender, shares);
        _push(msg.sender, assets);
        emit Withdraw(msg.sender, shares, assets);
    }

    /// @notice Route a fee in. Raises the share price for every holder, mints nothing.
    function donate(uint256 amount) external {
        if (amount == 0) revert Zero();
        _pull(msg.sender, amount);
        emit Donated(msg.sender, amount);
    }

    // --- borrowers ---
    function openPosition(uint256[] calldata ids) external {
        if (ids.length == 0 || ids.length > MAX_IDS) revert BadIds();
        Position storage p = _pos[msg.sender];
        if (p.ids.length != 0) revert HasPosition();
        accrue();
        sweepRewards();                        // rewards mined before this position never reach it
        uint256 prev;
        for (uint256 i; i < ids.length; ++i) {
            if (i != 0 && ids[i] <= prev) revert BadIds();
            prev = ids[i];
            squeek.transferFrom(msg.sender, address(this), ids[i]);
            p.ids.push(ids[i]);
        }
        // Power is what Squeekchain itself credits the vault for these NFTs, so reward
        // splitting between borrowers matches the real reward stream exactly.
        uint256 power = staking.accountPower(address(this));
        staking.deposit(address(squeek), ids);
        power = staking.accountPower(address(this)) - power;
        if (power == 0 || valuer.creditLine(ids) == 0) revert Zero();
        p.power = power; p.index = borrowIndex; p.rewardMark = rewardPerPower;
        totalPower += power;
        emit Opened(msg.sender, power);
    }

    function borrow(uint256 amount) external {
        if (amount == 0) revert Zero();
        Position storage p = _settle(msg.sender);
        if (p.ids.length == 0) revert NoPosition();
        uint256 cash = idle();
        if (amount > cash) revert NoLiquidity(amount, cash);
        uint256 newDebt = p.principal + amount;
        uint256 line = valuer.creditLine(p.ids);
        if (newDebt > line) revert OverLine(newDebt, line);
        scaledDebt -= p.principal * 1e18 / p.index;
        p.principal = newDebt;
        scaledDebt += newDebt * 1e18 / borrowIndex;
        _push(msg.sender, amount);
        emit Borrowed(msg.sender, amount);
    }

    function repay(uint256 amount) external {
        Position storage p = _settle(msg.sender);
        if (p.ids.length == 0) revert NoPosition();
        if (amount > p.principal) amount = p.principal;
        if (amount == 0) revert Zero();
        scaledDebt -= p.principal * 1e18 / p.index;
        p.principal -= amount;
        scaledDebt += p.principal * 1e18 / borrowIndex;
        _pull(msg.sender, amount);
        emit Repaid(msg.sender, amount);
    }

    function closePosition() external {
        Position storage p = _settle(msg.sender);
        if (p.ids.length == 0) revert NoPosition();
        if (p.principal != 0) revert DebtOutstanding();
        _release(msg.sender, p);
        emit Closed(msg.sender);
    }

    /// @notice Pull in what the escrowed collateral mined and credit it against debt,
    /// pro rata by power. Anyone may call it; the hamster pays the loans down.
    /// @dev Measures against `tracked`, not a before/after balance, so rewards that
    /// anyone pushed here earlier via staking.claim(vault) still reach the borrowers.
    function sweepRewards() public returns (uint256 amount) {
        staking.claim(address(this));
        amount = sqk.balanceOf(address(this)) - tracked;
        tracked += amount;
        if (amount == 0 || totalPower == 0) return amount;
        uint256 per = amount * 1e18 / totalPower;
        rewardPerPower += per;
        unsettled += per * totalPower / 1e18;
        emit Swept(amount, per);
    }

    function liquidate(address borrower) external {
        if (block.timestamp < valuer.lastPriceChange() + FREEZE_AFTER_PRICE_MOVE) revert Frozen();
        Position storage p = _settle(borrower);
        if (p.ids.length == 0) revert NoPosition();
        uint256 debt = p.principal;
        uint256 value = valuer.creditLine(p.ids) * 10000 / valuer.ltvBps();
        if (debt <= value * LIQ_THRESHOLD_BPS / 10000) revert Healthy();
        // Liquidator takes every NFT and pays collateral value less LIQ_BONUS_BPS. Anything
        // above the debt goes back to the borrower; any shortfall is written off against
        // the share price, so a sunk position can always be cleared.
        uint256 pay = value * (10000 - LIQ_BONUS_BPS) / 10000;
        scaledDebt -= debt * 1e18 / p.index;
        p.principal = 0;
        _pull(msg.sender, pay);
        uint256[] memory ids = _release(borrower, p);
        for (uint256 i; i < ids.length; ++i) squeek.transferFrom(address(this), msg.sender, ids[i]);
        if (pay > debt) _push(borrower, pay - debt);
        emit Liquidated(borrower, msg.sender, debt, pay);
    }

    function _release(address b, Position storage p) private returns (uint256[] memory ids) {
        ids = p.ids;
        staking.withdraw(address(squeek), ids);
        totalPower -= p.power;
        delete _pos[b];
        if (msg.sender == b) for (uint256 i; i < ids.length; ++i) squeek.transferFrom(address(this), b, ids[i]);
    }

    function _pull(address f, uint256 v) private { tracked += v; if (!sqk.transferFrom(f, address(this), v)) revert Zero(); }
    function _push(address t, uint256 v) private { tracked -= v; if (!sqk.transfer(t, v)) revert Zero(); }

    function onERC721Received(address, address, uint256, bytes calldata) external pure returns (bytes4) {
        return this.onERC721Received.selector;
    }
}