Skip to content

Instantly share code, notes, and snippets.

@moodysalem
Last active April 1, 2022 22:05
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save moodysalem/75dac17848cad4633d59f630b2a8b5d1 to your computer and use it in GitHub Desktop.
Save moodysalem/75dac17848cad4633d59f630b2a8b5d1 to your computer and use it in GitHub Desktop.
Transient Storage Example
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.13;
/// @notice The canonical ERC20 interface
interface IERC20 {
function balanceOf(address account) external view returns (uint256);
function transfer(address recipient, uint256 amount) external returns (bool);
function transferFrom(
address sender,
address recipient,
uint256 amount
) external returns (bool);
}
/// @notice Called on the sender of any call to start
interface IStartCallback {
/// @notice Called on the `msg.sender` when they call into #start
function started(bytes memory data) external;
}
/// @notice Anyone can approve a token for this contract, allowing others to borrow their token and earn fees
/// @dev Why? Holding custody of certain tokens infers certain rights. E.g.: the ability to vote on a governance proposal.
contract NoncustodialFlashLoans {
/// @notice The fee in bips that is charged on all borrowing
uint256 public immutable fee;
constructor(uint256 _fee) {
fee = _fee;
}
struct Borrow {
uint256 startingBalance;
address from;
IERC20 token;
uint256 amount;
}
//// THESE SHOULD BE TRANSIENT!
/// @notice The full list of borrows that have happened in the borrow context
Borrow[] public /* transient */ borrows;
/// @notice Allow borrowing a particular token from a user only once
mapping(bytes32 => bool) public /* transient */ alreadyBorrowedToken;
/// @notice The user that is currently borrowing tokens
address public /* transient */ borrower;
//// END TRANSIENT
/// @notice Start borrowing from the users that have approved this contract
function start(bytes memory data) external {
require(borrower == address(0), 'Cannot re-enter this function');
borrower = msg.sender;
IStartCallback(msg.sender).started(data);
for (uint256 i = 0; i < borrowedAmounts.length; i++) {
Borrow storage borrow = borrows[i];
bytes32 key = keccak256(abi.encode(borrow.from, borrow.token));
require(
borrow.token.balanceOf(borrow.from) >=
borrow.startingBalance + ((borrow.amount * fee) / 10_000),
'You must pay back the person you borrowed from!'
);
delete alreadyBorrowedToken[key];
}
delete borrows;
borrower = address(0);
}
function borrow(
address from,
IERC20 token,
uint256 amount,
address to
) external {
require(msg.sender == borrower, 'Must be called from within the IStartCallback#started');
bytes32 key = keccak256(abi.encode(from, token));
require(!alreadyBorrowedToken[key], 'Already borrowed this token from this address');
// Remember the borrower borrowed!
borrows.push(Borrow({startingBalance: token.balanceOf(from), from: from, token: token, amount: amount}));
alreadyBorrowedToken[key] = true;
token.transferFrom(from, to, amount);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment