Try   HackMD

non-custodial flash loans with transient storage

Powerful pattern enabled by transient storage.

The user calls NonCustodialFlashLoans#startLoan, and control is handed back to them using the IStartCallback#start callback.

The user can then borrow as much of any token they want, from any user that has sent approval, within this callback by calling NonCustodialFlashLoans#borrow.

The contract uses transient storage to track what has been borrowed and check the tokens are returned at the end. The SLOAD/SSTORE’s make this pattern gas-infeasible without transient storage.

Credit: @sendmoodz for this idea

Disclaimer: Unaudited code


interface IStartCallback {
    /// @notice Called on the `msg.sender` to hand over control to them.
    /// Expectation is that msg.sender#start will borrow tokens using NonCustodialFlashLoans#borrow, 
    /// then return them to the original user before control is handed back to #start.
    function start() external;
}

contract NonCustodialFlashLoans {
    struct Borrow {
        uint256 lenderStartingBalance;
        address lender;
        IERC20 token;
    }

    // The full list of borrows that have occured in the current transaction.
    Borrow[] public transient borrows;

    // The user borrowing. Borrower is able to call #borrow to release tokens.
    address public transient borrower;

    /// @notice Entry Point. Start borrowing from the users that have approved this contract.
    function startLoan() external {
        require(borrower == address(0)); // prevent reentrance

        // TSTORE it!
        borrower = msg.sender;

        /// Hand control to the caller so they can start borrowing tokens
        IStartCallback(msg.sender).start();

        // At this point `msg.sender` should have returned any tokens that 
        // were borrowed to each lender. Check this and revert if not!
        for (uint256 i = 0; i < borrows.length; i++) {
            Borrow transient borrow = borrows[i]; // TLOAD!
            require(
                borrow.token.balanceOf(borrow.lender) >= borrow.lenderStartingBalance,
                'You must pay back the person you borrowed from!'
            );
        }

        borrows.length = 0; // this doesn't actually work in recent solidity versions for storage arrays, but we only need to set the length of the array, you can also use TSTORE directly

        borrower = address(0); // clearing this allows it to be called again in the same transaction
    }

    // Only callable by `borrower`. Used to borrow tokens.
    function borrow(
        address from,
        IERC20 token,
        uint256 amount,
        address to
    ) external {
        require(msg.sender == borrower, 'Must be called from within the IStartCallback#start');

        // TSTORE what has been borrowed
        borrows.push(Borrow({lenderStartingBalance: token.balanceOf(from), lender: from, token: token}));
				
        token.transferFrom(from, to, amount);
    }
}