Last active
April 1, 2022 22:05
-
-
Save moodysalem/75dac17848cad4633d59f630b2a8b5d1 to your computer and use it in GitHub Desktop.
Transient Storage Example
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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