flashloan-rs 0.2.1

Minimal Multicall3 Flashloan Module
Documentation
/// @title Auth
/// @notice SPDX-License-Identifier: MIT
/// @author asnared <https://github.com/abigger87>
/// @notice An application-independent module providing a flexible and updatable auth pattern
/// @notice Adapted from <https://github.com/transmissions11/solmate/blob/main/src/auth/Auth.sol>

// Interface
// #define function isAuthorized(address,bytes4) view returns (bool)
#define function setAuthority(address) nonpayable returns ()
#define function setOwner(address) nonpayable returns ()
#define function owner() view returns (address)
#define function authority() view returns (address)

// Events
#define event OwnerUpdated(address indexed user, address indexed newOwner)
#define event AuthorityUpdated(address indexed user, address indexed newAuthority)

// Event Signatures
#define constant AUTHORITY_UPDATED_SIG = 0xa3396fd7f6e0a21b50e5089d2da70d5ac0a3bbbd1f617a93f134b76389980198
#define constant OWNER_UPDATED_SIG = 0x8292fce18fa69edf4db7b94ea2e58241df0ae57f97e0a6c9b29067028bf92d76

// Storage Slots
#define constant OWNER = FREE_STORAGE_POINTER()
#define constant AUTHORITY = FREE_STORAGE_POINTER()

/// @notice Constructor
#define macro AUTH_CONSTRUCTOR() = takes (0) returns (0) {
  // Copy the owner into memory
  0x20                                // [size] - byte size to copy
  0x40 codesize sub                   // [offset, size] - offset in the code to copy from
  0x00                                // [mem, offset, size] - offset in memory to copy to
  codecopy                            // []

  // Copy the authority into memory
  0x20                                // [size] - byte size to copy
  0x20 codesize sub                   // [offset, size] - offset in the code to copy from
  0x20                                // [mem, offset, size] - offset in memory to copy to
  codecopy                            // []

  // Set the new owner
  0x00 mload                          // [owner]
  dup1                                // [owner, owner]
  [OWNER]                             // [OWNER, owner, owner]
  sstore                              // [owner]

  // Set the new Authority
  0x20 mload                          // [authority, owner]
  dup1                                // [authority, authority, owner]
  [AUTHORITY]                         // [AUTHORITY, authority, authority, owner]
  sstore                              // [authority, owner]

  // Emit the authority updated event
  caller                              // [from, authority, owner]
  __EVENT_HASH(AuthorityUpdated)      // [sig, from, authority, owner]
  0x00 0x00                           // [0, 0, sig, from, authority, owner]
  log3                                // [owner]

  // Emit the owner updated event
  caller                              // [from, owner]
  [OWNER_UPDATED_SIG]                 // [sig, from, owner]
  0x00 0x00                           // [0, 0, sig, from, owner]
  log3                                // []
}

/// @notice Modifier that enforces caller authorization
#define macro REQUIRES_AUTH() = takes(0) returns(0) {
  0x00 calldataload           // [msg.sig]
  caller                      // [msg.sender, msg.sig]
  IS_AUTHORIZED(authed)       // []
	0x00 0x00 revert
  authed:
}

/// @notice Helper to check if the caller is authorized
#define macro IS_AUTHORIZED(authed) = takes(2) returns(0) {
  // If msg.sender == owner, jump to authed
  caller                      // [msg.sender]
  [OWNER] sload               // [owner, msg.sender]
  eq <authed> jumpi           // [authed]

  // Continue if address(authority) != address(0)
  [AUTHORITY] sload          // [authority, user, sig]
  dup1                       // [authority, authority, user, sig]
  iszero post jumpi          // [authority, user, sig]

  // WARN: If an account has no code, the call will unintendedly return successfully
  dup1 extcodesize           // [code, authority, user, sig]
  iszero post jumpi          // [authority, user, sig]

  // Store the auth.canCall arguments in memory at offset 0
  // function canCall(address user, address target, bytes4 functionSig) external view returns (bool)
  0xb7009613 0xe0 shl        // [bytes4(canCall), authority, user, sig]
  0x00 mstore                // [authority, user, sig]
  swap1                      // [user, authority, sig]
  0x04 mstore                // [authority, sig]
  address                    // [address(this), authority, sig]
  0x24 mstore                // [authority, sig]
  swap1                      // [sig, authority]
  0x44 mstore                // [authority]

  // Call auth.canCall
  0x00                       // [retOffset, authority] - store the return data at memory offset 0
  0x60                       // [argSize, retOffset, authority] - three argument slots
  0x00                       // [argOffset, argSize, retOffset, authority] - arguments stored in memory at offset 0
  0x00                       // [value, argOffset, argSize, retOffset, authority]
  0x20                       // [retSize, value, argOffset, argSize, retOffset, authority] - returns a bool in one slot
  swap5                      // [authority, value, argOffset, argSize, retOffset, retSize] - authority is the target
  gas                        // [gas, authority, value, argOffset, argSize, retOffset, retSize]
  call                       // [call_reverted?]
  iszero post jumpi          // []

  // Get the return data
  0x20 0x00 0x00 0x00        // [destOffset, offset, size] - memory offset and size to get the return data from
  returndatacopy             // []
  0x00 mload                 // [retData]
  <authed> jumpi             // []

  // Jump dest for address(0) check
  post:
}

/// @notice Set the Authority
/// @param {newAuthority} [address] - The new authority
#define macro SET_AUTHORITY() = takes(0) returns(0) {
  // Check that the caller is authorized
  REQUIRES_AUTH()

  // Set the new authority
  0x04 calldataload                   // [newAuthority]
  dup1                                // [newAuthority, newAuthority]
  [AUTHORITY] sstore                  // [newAuthority]

  // Emit the authority updated event
  caller                              // [from, newAuthority]
  __EVENT_HASH(AuthorityUpdated)      // [sig, from, newAuthority]
  0x00 0x00                           // [0, 32, sig, from, newAuthority]
  log3                                // []

  stop
}

/// @notice Set the Owner
/// @param {newOwner} [address] - The new owner
#define macro SET_OWNER() = takes(0) returns(0) {
  // Check that the caller is authorized
  REQUIRES_AUTH()

  // Set the new owner
  0x04 calldataload           // [newOwner]
  dup1                        // [newOwner, newOwner]
  [OWNER] sstore              // [newOwner]

  // Emit the owner updated event
  caller                      // [from, newOwner]
  [OWNER_UPDATED_SIG]         // [sig, from, newOwner]
  0x00 0x00                   // [0, 32, sig, from, newOwner]
  log3                        // []

  stop
}

/// @notice Get the contract Owner
/// @return {owner} [address] - The contract owner
#define macro OWNER() = takes (0) returns (0) {
    [OWNER] sload             // [owner]
    0x00 mstore               // []
    0x20 0x00 return          // []
}

/// @notice Get the contract Authority
/// @return {authority} [address] - The contract authority
#define macro AUTHORITY() = takes (0) returns (0) {
    [AUTHORITY] sload         // [authority]
    0x00 mstore               // []
    0x20 0x00 return          // []
}

/// @notice Main Function Dispatcher
#define macro AUTH_MAIN() = takes (1) returns (1) {
    // Input Stack: [function_selector]

    dup1 __FUNC_SIG(setOwner)                 eq set_owner jumpi
    dup1 __FUNC_SIG(setAuthority)             eq set_authority jumpi
    dup1 __FUNC_SIG(owner)                    eq owner jumpi
    dup1 __FUNC_SIG(authority)                eq authority jumpi

    // Bubble up to parent if no function selector matches
    no_match jump

    set_owner:
        SET_OWNER()
    set_authority:
        SET_AUTHORITY()
    owner:
        OWNER()
    authority:
        AUTHORITY()

    no_match:
}