Address Contract Verified
Address
0xF5aCC1568706Fbf9A55a77DdBe8DF907Da95dD6B
Balance
0 ETH
Nonce
35
Code Size
786 bytes
Creator
Create2 Deployer at tx 0x21dad043...e99267
Indexed Transactions
0 (35 on-chain, 1.2% indexed)
Contract Bytecode
786 bytes
0x608060405234801561001057600080fd5b50600436106100365760003560e01c806386b2162e1461003b578063f28613d91461008b575b600080fd5b6100627f000000000000000000000000fe87400c401c0cb61ec3a923a5d9873090e8dafc81565b60405173ffffffffffffffffffffffffffffffffffffffff909116815260200160405180910390f35b610062610099366004610246565b60006100da7f000000000000000000000000fe87400c401c0cb61ec3a923a5d9873090e8dafc73ffffffffffffffffffffffffffffffffffffffff166101b4565b6040517f132e9b7100000000000000000000000000000000000000000000000000000000815290915073ffffffffffffffffffffffffffffffffffffffff82169063132e9b719061012f908590600401610282565b600060405180830381600087803b15801561014957600080fd5b505af115801561015d573d6000803e3d6000fd5b505050508073ffffffffffffffffffffffffffffffffffffffff167f7c19a2785f0fdff7e3b0a72613aacd7c4b9e612f8c57d686fd360e161804e22a836040516101a79190610282565b60405180910390a2919050565b60006040516c5af43d3d93803e605757fd5bf3605152826044526f593da1005b3d3d3d3d363d3d37363d736030527f9e4ac34f21c619cefc926c8bd93b54bf5a39c7ab2127a895af1cc0691d7e3dff6020527060593d8160093d39f336602c57343d527f6000526071600f6000f0915080604052506000606052806102415763301164256000526004601cfd5b919050565b60006060828403121561025857600080fd5b50919050565b803573ffffffffffffffffffffffffffffffffffffffff8116811461024157600080fd5b6060810173ffffffffffffffffffffffffffffffffffffffff806102a58561025e565b16835260208401358015158082146102bc57600080fd5b602085015250806102cf6040860161025e565b166040840152509291505056fea2646970667358221220f49ed6348b24682784af1f5529dc72d65e054d4638b337bdb0771d80a1f97aa764736f6c63430008110033
Verified Source Code Full Match
Compiler: v0.8.17+commit.8df45f5f
EVM: london
Optimization: Yes (1000000 runs)
PassThroughWalletImpl.sol 137 lines
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.8.17;
import {PausableImpl} from "splits-utils/PausableImpl.sol";
import {TokenUtils} from "splits-utils/TokenUtils.sol";
import {WalletImpl} from "splits-utils/WalletImpl.sol";
/// @title Pass-Through Wallet Implementation
/// @author 0xSplits
/// @notice A clone-implementation of a pass-through wallet.
/// Please be aware, owner has _FULL CONTROL_ of the deployment.
/// @dev This contract uses token = address(0) to refer to ETH.
contract PassThroughWalletImpl is WalletImpl, PausableImpl {
/// -----------------------------------------------------------------------
/// libraries
/// -----------------------------------------------------------------------
using TokenUtils for address;
/// -----------------------------------------------------------------------
/// structs
/// -----------------------------------------------------------------------
struct InitParams {
address owner;
bool paused;
address passThrough;
}
/// -----------------------------------------------------------------------
/// events
/// -----------------------------------------------------------------------
event SetPassThrough(address passThrough);
event PassThrough(address indexed passThrough, address[] tokens, uint256[] amounts);
// emitted in clone bytecode
event ReceiveETH(uint256 amount);
/// -----------------------------------------------------------------------
/// storage
/// -----------------------------------------------------------------------
/// -----------------------------------------------------------------------
/// storage - constants & immutables
/// -----------------------------------------------------------------------
address public immutable passThroughWalletFactory;
/// -----------------------------------------------------------------------
/// storage - mutables
/// -----------------------------------------------------------------------
/// slot 0 - 11 bytes free
/// OwnableImpl storage
/// address internal $owner;
/// 20 bytes
/// PausableImpl storage
/// bool internal $paused;
/// 1 byte
/// slot 1 - 12 bytes free
/// address to pass-through funds to
address internal $passThrough;
/// 20 bytes
/// -----------------------------------------------------------------------
/// constructor & initializer
/// -----------------------------------------------------------------------
constructor() {
passThroughWalletFactory = msg.sender;
}
function initializer(InitParams calldata params_) external {
// only passThroughWalletFactory may call `initializer`
if (msg.sender != passThroughWalletFactory) revert Unauthorized();
// don't need to init wallet separately
__initPausable({owner_: params_.owner, paused_: params_.paused});
$passThrough = params_.passThrough;
}
/// -----------------------------------------------------------------------
/// functions
/// -----------------------------------------------------------------------
/// -----------------------------------------------------------------------
/// functions - public & external - onlyOwner
/// -----------------------------------------------------------------------
/// set passThrough
function setPassThrough(address passThrough_) external onlyOwner {
$passThrough = passThrough_;
emit SetPassThrough(passThrough_);
}
/// -----------------------------------------------------------------------
/// functions - public & external - view
/// -----------------------------------------------------------------------
function passThrough() external view returns (address) {
return $passThrough;
}
/// -----------------------------------------------------------------------
/// functions - public & external - permissionless
/// -----------------------------------------------------------------------
/// emit event when receiving ETH
/// @dev implemented w/i clone bytecode
/* receive() external payable { */
/* emit ReceiveETH(msg.value); */
/* } */
/// send `tokens_` to `$passThrough`
function passThroughTokens(address[] calldata tokens_) external pausable returns (uint256[] memory amounts) {
address _passThrough = $passThrough;
uint256 length = tokens_.length;
amounts = new uint256[](length);
for (uint256 i; i < length;) {
address token = tokens_[i];
uint256 amount = token._balanceOf(address(this));
amounts[i] = amount;
token._safeTransfer(_passThrough, amount);
unchecked {
++i;
}
}
emit PassThrough(_passThrough, tokens_, amounts);
}
}
PassThroughWalletFactory.sol 33 lines
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.8.17;
import {LibClone} from "splits-utils/LibClone.sol";
import {PassThroughWalletImpl} from "./PassThroughWalletImpl.sol";
/// @title Pass-Through Wallet Factory
/// @author 0xSplits
/// @notice Factory for creating pass-through wallets.
/// @dev This contract uses token = address(0) to refer to ETH.
contract PassThroughWalletFactory {
using LibClone for address;
event CreatePassThroughWallet(
PassThroughWalletImpl indexed passThroughWallet, PassThroughWalletImpl.InitParams params
);
PassThroughWalletImpl public immutable passThroughWalletImpl;
constructor() {
passThroughWalletImpl = new PassThroughWalletImpl();
}
function createPassThroughWallet(PassThroughWalletImpl.InitParams calldata params_)
external
returns (PassThroughWalletImpl passThroughWallet)
{
passThroughWallet = PassThroughWalletImpl(payable(address(passThroughWalletImpl).clone()));
passThroughWallet.initializer(params_);
emit CreatePassThroughWallet({passThroughWallet: passThroughWallet, params: params_});
}
}
LibClone.sol 120 lines
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.8.17;
/// @title Modified minimal proxy
/// @author 0xSplits
/// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/LibClone.sol)
/// @dev Modified minimal proxy includes a `receive()` method that emits the
/// `ReceiveETH(uint256)` event to skip `DELEGATECALL` when there is no calldata.
/// Enables us to accept hard gas-capped `sends` & `transfers` for maximum backwards
/// composability.
library LibClone {
error DeploymentFailed();
uint256 private constant FREE_PTR = 0x40;
uint256 private constant ZERO_PTR = 0x60;
/// @dev Deploys a modified minimal proxy of `implementation`
function clone(address implementation) internal returns (address instance) {
assembly ("memory-safe") {
/**
* --------------------------------------------------------------------------+
* CREATION (9 bytes - 0x09) |
* --------------------------------------------------------------------------|
* Opcode | Mnemonic | Stack | Memory |
* --------------------------------------------------------------------------|
* 60 runSize | PUSH1 runSize | r | |
* 3d | RETURNDATASIZE | 0 r | |
* 81 | DUP2 | r 0 r | |
* 60 offset | PUSH1 offset | o r 0 r | |
* 3d | RETURNDATASIZE | 0 o r 0 r | |
* 39 | CODECOPY | 0 r | [0..runSize): runtime code |
* f3 | RETURN | | [0..runSize): runtime code |
* --------------------------------------------------------------------------|
* RUNTIME (89 bytes - 0x59) |
* --------------------------------------------------------------------------|
* Opcode | Mnemonic | Stack | Memory |
* --------------------------------------------------------------------------|
* |
* 36 | CALLDATASIZE | cds | |
* 60 0x2c | PUSH1 0x2c | 0x2c cds | |
* 57 | JUMPI | | |
* 34 | CALLVALUE | cv | |
* 3d | RETURNDATASIZE | 0 cv | |
* 52 | MSTORE | | [0..0x20): callvalue |
* 7f sig | PUSH32 0x9e.. | sig | [0..0x20): callvalue |
* 59 | MSIZE | 0x20 sig | [0..0x20): callvalue |
* 3d | RETURNDATASIZE | 0 0x20 sig | [0..0x20): callvalue |
* a1 | LOG1 | | [0..0x20): callvalue |
* 00 | STOP | | [0..0x20): callvalue |
* 5b | JUMPDEST | | |
* |
* ::: keep some values in stack ::::::::::::::::::::::::::::::::::::::::::: |
* 3d | RETURNDATASIZE | 0 | |
* 3d | RETURNDATASIZE | 0 0 | |
* 3d | RETURNDATASIZE | 0 0 0 | |
* 3d | RETURNDATASIZE | 0 0 0 0 | |
* |
* ::: copy calldata to memory ::::::::::::::::::::::::::::::::::::::::::::: |
* 36 | CALLDATASIZE | cds 0 0 0 0 | |
* 3d | RETURNDATASIZE | 0 cds 0 0 0 0 | |
* 3d | RETURNDATASIZE | 0 0 cds 0 0 0 0 | |
* 37 | CALLDATACOPY | 0 0 0 0 | [0..cds): calldata |
* |
* ::: delegate call to the implementation contract :::::::::::::::::::::::: |
* 36 | CALLDATASIZE | cds 0 0 0 0 | [0..cds): calldata |
* 3d | RETURNDATASIZE | 0 cds 0 0 0 0 | [0..cds): calldata |
* 73 addr | PUSH20 addr | addr 0 cds 0 0 0 0 | [0..cds): calldata |
* 5a | GAS | gas addr 0 cds 0 0 0 0 | [0..cds): calldata |
* f4 | DELEGATECALL | success 0 0 | [0..cds): calldata |
* |
* ::: copy return data to memory :::::::::::::::::::::::::::::::::::::::::: |
* 3d | RETURNDATASIZE | rds success 0 0 | [0..cds): calldata |
* 3d | RETURNDATASIZE | rds rds success 0 0 | [0..cds): calldata |
* 93 | SWAP4 | 0 rds success 0 rds | [0..cds): calldata |
* 80 | DUP1 | 0 0 rds success 0 rds | [0..cds): calldata |
* 3e | RETURNDATACOPY | success 0 rds | [0..rds): returndata |
* |
* 60 0x57 | PUSH1 0x57 | 0x57 success 0 rds | [0..rds): returndata |
* 57 | JUMPI | 0 rds | [0..rds): returndata |
* |
* ::: revert :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: |
* fd | REVERT | | [0..rds): returndata |
* |
* ::: return :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: |
* 5b | JUMPDEST | 0 rds | [0..rds): returndata |
* f3 | RETURN | | [0..rds): returndata |
* --------------------------------------------------------------------------+
* TOTAL INIT (98 bytes - 0x62) |
* --------------------------------------------------------------------------|
*/
// save free pointer
let fp := mload(FREE_PTR)
mstore(0x51, 0x5af43d3d93803e605757fd5bf3) // 13 bytes
mstore(0x44, implementation) // 20 bytes
mstore(0x30, 0x593da1005b3d3d3d3d363d3d37363d73) // 16 bytes
// `keccak256("ReceiveETH(uint256)")`
mstore(0x20, 0x9e4ac34f21c619cefc926c8bd93b54bf5a39c7ab2127a895af1cc0691d7e3dff) // 32 bytes
mstore(0x00, 0x60593d8160093d39f336602c57343d527f) // 17 bytes
// total: 113 bytes = 0x71
// offset: 15 bytes = 0x0f
// data: 98 bytes = 0x62
instance := create(0, 0x0f, 0x71)
// restore free pointer, zero slot
mstore(FREE_PTR, fp)
mstore(ZERO_PTR, 0)
// If `instance` is zero, revert.
if iszero(instance) {
// Store the function selector of `DeploymentFailed()`.
mstore(0x00, 0x30116425)
// Revert with (offset, size).
revert(0x1c, 0x04)
}
}
}
}
TokenUtils.sol 33 lines
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.8.17;
import {SafeTransferLib} from "solady/utils/SafeTransferLib.sol";
/// Library to handle basic token functions for ERC20s & ETH (represented by 0x0)
library TokenUtils {
using SafeTransferLib for address;
address internal constant ETH_ADDRESS = address(0);
function _isETH(address token) internal pure returns (bool) {
return (token == ETH_ADDRESS);
}
function _decimals(address token) internal view returns (uint8) {
return _isETH(token) ? 18 : ERC20(token).decimals();
}
function _balanceOf(address token, address addr) internal view returns (uint256) {
return _isETH(token) ? addr.balance : ERC20(token).balanceOf(addr);
}
function _safeTransfer(address token, address addr, uint256 amount) internal {
if (_isETH(token)) addr.safeTransferETH(amount);
else token.safeTransfer(addr, amount);
}
}
interface ERC20 {
function decimals() external view returns (uint8);
function balanceOf(address addr) external view returns (uint256);
}
WalletImpl.sol 69 lines
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.8.17;
import {ERC1155TokenReceiver} from "solmate/tokens/ERC1155.sol";
import {ERC721TokenReceiver} from "solmate/tokens/ERC721.sol";
import {OwnableImpl} from "./OwnableImpl.sol";
/// @title Wallet Implementation
/// @author 0xSplits
/// @notice Minimal smart wallet clone-implementation
abstract contract WalletImpl is OwnableImpl, ERC721TokenReceiver, ERC1155TokenReceiver {
struct Call {
address to;
uint256 value;
bytes data;
}
event ExecCalls(Call[] calls);
/// -----------------------------------------------------------------------
/// storage - mutables
/// -----------------------------------------------------------------------
/// slot 0 - 12 bytes free
/// OwnableImpl storage
/// address internal $owner;
/// 20 bytes
/// -----------------------------------------------------------------------
/// constructor & initializer
/// -----------------------------------------------------------------------
constructor() {}
function __initWallet(address owner_) internal {
OwnableImpl.__initOwnable(owner_);
}
/// -----------------------------------------------------------------------
/// functions - external & public - onlyOwner
/// -----------------------------------------------------------------------
/// allow owner to execute arbitrary calls
function execCalls(Call[] calldata calls_)
external
payable
onlyOwner
returns (uint256 blockNumber, bytes[] memory returnData)
{
blockNumber = block.number;
uint256 length = calls_.length;
returnData = new bytes[](length);
bool success;
for (uint256 i; i < length;) {
Call calldata calli = calls_[i];
(success, returnData[i]) = calli.to.call{value: calli.value}(calli.data);
require(success, string(returnData[i]));
unchecked {
++i;
}
}
emit ExecCalls(calls_);
}
}
OwnableImpl.sol 57 lines
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.8.17;
/// @title Ownable Implementation
/// @author 0xSplits
/// @notice Ownable clone-implementation
abstract contract OwnableImpl {
error Unauthorized();
event OwnershipTransferred(address indexed oldOwner, address indexed newOwner);
/// -----------------------------------------------------------------------
/// storage - mutables
/// -----------------------------------------------------------------------
/// slot 0 - 12 bytes free
address internal $owner;
/// 20 bytes
/// -----------------------------------------------------------------------
/// constructor & initializer
/// -----------------------------------------------------------------------
constructor() {}
function __initOwnable(address owner_) internal virtual {
emit OwnershipTransferred(address(0), owner_);
$owner = owner_;
}
/// -----------------------------------------------------------------------
/// modifiers
/// -----------------------------------------------------------------------
modifier onlyOwner() virtual {
if (msg.sender != owner()) revert Unauthorized();
_;
}
/// -----------------------------------------------------------------------
/// functions - public & external - onlyOwner
/// -----------------------------------------------------------------------
function transferOwnership(address owner_) public virtual onlyOwner {
$owner = owner_;
emit OwnershipTransferred(msg.sender, owner_);
}
/// -----------------------------------------------------------------------
/// functions - public & external - view
/// -----------------------------------------------------------------------
function owner() public view virtual returns (address) {
return $owner;
}
}
PausableImpl.sol 63 lines
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.8.17;
import {OwnableImpl} from "./OwnableImpl.sol";
/// @title Pausable Implementation
/// @author 0xSplits
/// @notice Pausable clone-implementation
abstract contract PausableImpl is OwnableImpl {
error Paused();
event SetPaused(bool paused);
/// -----------------------------------------------------------------------
/// storage - mutables
/// -----------------------------------------------------------------------
/// slot 0 - 11 bytes free
/// OwnableImpl storage
/// address internal $owner;
/// 20 bytes
bool internal $paused;
/// 1 byte
/// -----------------------------------------------------------------------
/// constructor & initializer
/// -----------------------------------------------------------------------
constructor() {}
function __initPausable(address owner_, bool paused_) internal virtual {
OwnableImpl.__initOwnable(owner_);
$paused = paused_;
}
/// -----------------------------------------------------------------------
/// modifiers
/// -----------------------------------------------------------------------
modifier pausable() virtual {
if (paused()) revert Paused();
_;
}
/// -----------------------------------------------------------------------
/// functions - public & external - onlyOwner
/// -----------------------------------------------------------------------
function setPaused(bool paused_) public virtual onlyOwner {
$paused = paused_;
emit SetPaused(paused_);
}
/// -----------------------------------------------------------------------
/// functions - public & external - view
/// -----------------------------------------------------------------------
function paused() public view virtual returns (bool) {
return $paused;
}
}
ERC721.sol 231 lines
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity >=0.8.0;
/// @notice Modern, minimalist, and gas efficient ERC-721 implementation.
/// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/tokens/ERC721.sol)
abstract contract ERC721 {
/*//////////////////////////////////////////////////////////////
EVENTS
//////////////////////////////////////////////////////////////*/
event Transfer(address indexed from, address indexed to, uint256 indexed id);
event Approval(address indexed owner, address indexed spender, uint256 indexed id);
event ApprovalForAll(address indexed owner, address indexed operator, bool approved);
/*//////////////////////////////////////////////////////////////
METADATA STORAGE/LOGIC
//////////////////////////////////////////////////////////////*/
string public name;
string public symbol;
function tokenURI(uint256 id) public view virtual returns (string memory);
/*//////////////////////////////////////////////////////////////
ERC721 BALANCE/OWNER STORAGE
//////////////////////////////////////////////////////////////*/
mapping(uint256 => address) internal _ownerOf;
mapping(address => uint256) internal _balanceOf;
function ownerOf(uint256 id) public view virtual returns (address owner) {
require((owner = _ownerOf[id]) != address(0), "NOT_MINTED");
}
function balanceOf(address owner) public view virtual returns (uint256) {
require(owner != address(0), "ZERO_ADDRESS");
return _balanceOf[owner];
}
/*//////////////////////////////////////////////////////////////
ERC721 APPROVAL STORAGE
//////////////////////////////////////////////////////////////*/
mapping(uint256 => address) public getApproved;
mapping(address => mapping(address => bool)) public isApprovedForAll;
/*//////////////////////////////////////////////////////////////
CONSTRUCTOR
//////////////////////////////////////////////////////////////*/
constructor(string memory _name, string memory _symbol) {
name = _name;
symbol = _symbol;
}
/*//////////////////////////////////////////////////////////////
ERC721 LOGIC
//////////////////////////////////////////////////////////////*/
function approve(address spender, uint256 id) public virtual {
address owner = _ownerOf[id];
require(msg.sender == owner || isApprovedForAll[owner][msg.sender], "NOT_AUTHORIZED");
getApproved[id] = spender;
emit Approval(owner, spender, id);
}
function setApprovalForAll(address operator, bool approved) public virtual {
isApprovedForAll[msg.sender][operator] = approved;
emit ApprovalForAll(msg.sender, operator, approved);
}
function transferFrom(
address from,
address to,
uint256 id
) public virtual {
require(from == _ownerOf[id], "WRONG_FROM");
require(to != address(0), "INVALID_RECIPIENT");
require(
msg.sender == from || isApprovedForAll[from][msg.sender] || msg.sender == getApproved[id],
"NOT_AUTHORIZED"
);
// Underflow of the sender's balance is impossible because we check for
// ownership above and the recipient's balance can't realistically overflow.
unchecked {
_balanceOf[from]--;
_balanceOf[to]++;
}
_ownerOf[id] = to;
delete getApproved[id];
emit Transfer(from, to, id);
}
function safeTransferFrom(
address from,
address to,
uint256 id
) public virtual {
transferFrom(from, to, id);
require(
to.code.length == 0 ||
ERC721TokenReceiver(to).onERC721Received(msg.sender, from, id, "") ==
ERC721TokenReceiver.onERC721Received.selector,
"UNSAFE_RECIPIENT"
);
}
function safeTransferFrom(
address from,
address to,
uint256 id,
bytes calldata data
) public virtual {
transferFrom(from, to, id);
require(
to.code.length == 0 ||
ERC721TokenReceiver(to).onERC721Received(msg.sender, from, id, data) ==
ERC721TokenReceiver.onERC721Received.selector,
"UNSAFE_RECIPIENT"
);
}
/*//////////////////////////////////////////////////////////////
ERC165 LOGIC
//////////////////////////////////////////////////////////////*/
function supportsInterface(bytes4 interfaceId) public view virtual returns (bool) {
return
interfaceId == 0x01ffc9a7 || // ERC165 Interface ID for ERC165
interfaceId == 0x80ac58cd || // ERC165 Interface ID for ERC721
interfaceId == 0x5b5e139f; // ERC165 Interface ID for ERC721Metadata
}
/*//////////////////////////////////////////////////////////////
INTERNAL MINT/BURN LOGIC
//////////////////////////////////////////////////////////////*/
function _mint(address to, uint256 id) internal virtual {
require(to != address(0), "INVALID_RECIPIENT");
require(_ownerOf[id] == address(0), "ALREADY_MINTED");
// Counter overflow is incredibly unrealistic.
unchecked {
_balanceOf[to]++;
}
_ownerOf[id] = to;
emit Transfer(address(0), to, id);
}
function _burn(uint256 id) internal virtual {
address owner = _ownerOf[id];
require(owner != address(0), "NOT_MINTED");
// Ownership check above ensures no underflow.
unchecked {
_balanceOf[owner]--;
}
delete _ownerOf[id];
delete getApproved[id];
emit Transfer(owner, address(0), id);
}
/*//////////////////////////////////////////////////////////////
INTERNAL SAFE MINT LOGIC
//////////////////////////////////////////////////////////////*/
function _safeMint(address to, uint256 id) internal virtual {
_mint(to, id);
require(
to.code.length == 0 ||
ERC721TokenReceiver(to).onERC721Received(msg.sender, address(0), id, "") ==
ERC721TokenReceiver.onERC721Received.selector,
"UNSAFE_RECIPIENT"
);
}
function _safeMint(
address to,
uint256 id,
bytes memory data
) internal virtual {
_mint(to, id);
require(
to.code.length == 0 ||
ERC721TokenReceiver(to).onERC721Received(msg.sender, address(0), id, data) ==
ERC721TokenReceiver.onERC721Received.selector,
"UNSAFE_RECIPIENT"
);
}
}
/// @notice A generic interface for a contract which properly accepts ERC721 tokens.
/// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/tokens/ERC721.sol)
abstract contract ERC721TokenReceiver {
function onERC721Received(
address,
address,
uint256,
bytes calldata
) external virtual returns (bytes4) {
return ERC721TokenReceiver.onERC721Received.selector;
}
}
ERC1155.sol 257 lines
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity >=0.8.0;
/// @notice Minimalist and gas efficient standard ERC1155 implementation.
/// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/tokens/ERC1155.sol)
abstract contract ERC1155 {
/*//////////////////////////////////////////////////////////////
EVENTS
//////////////////////////////////////////////////////////////*/
event TransferSingle(
address indexed operator,
address indexed from,
address indexed to,
uint256 id,
uint256 amount
);
event TransferBatch(
address indexed operator,
address indexed from,
address indexed to,
uint256[] ids,
uint256[] amounts
);
event ApprovalForAll(address indexed owner, address indexed operator, bool approved);
event URI(string value, uint256 indexed id);
/*//////////////////////////////////////////////////////////////
ERC1155 STORAGE
//////////////////////////////////////////////////////////////*/
mapping(address => mapping(uint256 => uint256)) public balanceOf;
mapping(address => mapping(address => bool)) public isApprovedForAll;
/*//////////////////////////////////////////////////////////////
METADATA LOGIC
//////////////////////////////////////////////////////////////*/
function uri(uint256 id) public view virtual returns (string memory);
/*//////////////////////////////////////////////////////////////
ERC1155 LOGIC
//////////////////////////////////////////////////////////////*/
function setApprovalForAll(address operator, bool approved) public virtual {
isApprovedForAll[msg.sender][operator] = approved;
emit ApprovalForAll(msg.sender, operator, approved);
}
function safeTransferFrom(
address from,
address to,
uint256 id,
uint256 amount,
bytes calldata data
) public virtual {
require(msg.sender == from || isApprovedForAll[from][msg.sender], "NOT_AUTHORIZED");
balanceOf[from][id] -= amount;
balanceOf[to][id] += amount;
emit TransferSingle(msg.sender, from, to, id, amount);
require(
to.code.length == 0
? to != address(0)
: ERC1155TokenReceiver(to).onERC1155Received(msg.sender, from, id, amount, data) ==
ERC1155TokenReceiver.onERC1155Received.selector,
"UNSAFE_RECIPIENT"
);
}
function safeBatchTransferFrom(
address from,
address to,
uint256[] calldata ids,
uint256[] calldata amounts,
bytes calldata data
) public virtual {
require(ids.length == amounts.length, "LENGTH_MISMATCH");
require(msg.sender == from || isApprovedForAll[from][msg.sender], "NOT_AUTHORIZED");
// Storing these outside the loop saves ~15 gas per iteration.
uint256 id;
uint256 amount;
for (uint256 i = 0; i < ids.length; ) {
id = ids[i];
amount = amounts[i];
balanceOf[from][id] -= amount;
balanceOf[to][id] += amount;
// An array can't have a total length
// larger than the max uint256 value.
unchecked {
++i;
}
}
emit TransferBatch(msg.sender, from, to, ids, amounts);
require(
to.code.length == 0
? to != address(0)
: ERC1155TokenReceiver(to).onERC1155BatchReceived(msg.sender, from, ids, amounts, data) ==
ERC1155TokenReceiver.onERC1155BatchReceived.selector,
"UNSAFE_RECIPIENT"
);
}
function balanceOfBatch(address[] calldata owners, uint256[] calldata ids)
public
view
virtual
returns (uint256[] memory balances)
{
require(owners.length == ids.length, "LENGTH_MISMATCH");
balances = new uint256[](owners.length);
// Unchecked because the only math done is incrementing
// the array index counter which cannot possibly overflow.
unchecked {
for (uint256 i = 0; i < owners.length; ++i) {
balances[i] = balanceOf[owners[i]][ids[i]];
}
}
}
/*//////////////////////////////////////////////////////////////
ERC165 LOGIC
//////////////////////////////////////////////////////////////*/
function supportsInterface(bytes4 interfaceId) public view virtual returns (bool) {
return
interfaceId == 0x01ffc9a7 || // ERC165 Interface ID for ERC165
interfaceId == 0xd9b67a26 || // ERC165 Interface ID for ERC1155
interfaceId == 0x0e89341c; // ERC165 Interface ID for ERC1155MetadataURI
}
/*//////////////////////////////////////////////////////////////
INTERNAL MINT/BURN LOGIC
//////////////////////////////////////////////////////////////*/
function _mint(
address to,
uint256 id,
uint256 amount,
bytes memory data
) internal virtual {
balanceOf[to][id] += amount;
emit TransferSingle(msg.sender, address(0), to, id, amount);
require(
to.code.length == 0
? to != address(0)
: ERC1155TokenReceiver(to).onERC1155Received(msg.sender, address(0), id, amount, data) ==
ERC1155TokenReceiver.onERC1155Received.selector,
"UNSAFE_RECIPIENT"
);
}
function _batchMint(
address to,
uint256[] memory ids,
uint256[] memory amounts,
bytes memory data
) internal virtual {
uint256 idsLength = ids.length; // Saves MLOADs.
require(idsLength == amounts.length, "LENGTH_MISMATCH");
for (uint256 i = 0; i < idsLength; ) {
balanceOf[to][ids[i]] += amounts[i];
// An array can't have a total length
// larger than the max uint256 value.
unchecked {
++i;
}
}
emit TransferBatch(msg.sender, address(0), to, ids, amounts);
require(
to.code.length == 0
? to != address(0)
: ERC1155TokenReceiver(to).onERC1155BatchReceived(msg.sender, address(0), ids, amounts, data) ==
ERC1155TokenReceiver.onERC1155BatchReceived.selector,
"UNSAFE_RECIPIENT"
);
}
function _batchBurn(
address from,
uint256[] memory ids,
uint256[] memory amounts
) internal virtual {
uint256 idsLength = ids.length; // Saves MLOADs.
require(idsLength == amounts.length, "LENGTH_MISMATCH");
for (uint256 i = 0; i < idsLength; ) {
balanceOf[from][ids[i]] -= amounts[i];
// An array can't have a total length
// larger than the max uint256 value.
unchecked {
++i;
}
}
emit TransferBatch(msg.sender, from, address(0), ids, amounts);
}
function _burn(
address from,
uint256 id,
uint256 amount
) internal virtual {
balanceOf[from][id] -= amount;
emit TransferSingle(msg.sender, from, address(0), id, amount);
}
}
/// @notice A generic interface for a contract which properly accepts ERC1155 tokens.
/// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/tokens/ERC1155.sol)
abstract contract ERC1155TokenReceiver {
function onERC1155Received(
address,
address,
uint256,
uint256,
bytes calldata
) external virtual returns (bytes4) {
return ERC1155TokenReceiver.onERC1155Received.selector;
}
function onERC1155BatchReceived(
address,
address,
uint256[] calldata,
uint256[] calldata,
bytes calldata
) external virtual returns (bytes4) {
return ERC1155TokenReceiver.onERC1155BatchReceived.selector;
}
}
SafeTransferLib.sol 354 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;
/// @notice Safe ETH and ERC20 transfer library that gracefully handles missing return values.
/// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/SafeTransferLib.sol)
/// @author Modified from Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/SafeTransferLib.sol)
/// @dev Caution! This library won't check that a token has code, responsibility is delegated to the caller.
library SafeTransferLib {
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* CUSTOM ERRORS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev The ETH transfer has failed.
error ETHTransferFailed();
/// @dev The ERC20 `transferFrom` has failed.
error TransferFromFailed();
/// @dev The ERC20 `transfer` has failed.
error TransferFailed();
/// @dev The ERC20 `approve` has failed.
error ApproveFailed();
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* CONSTANTS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Suggested gas stipend for contract receiving ETH
/// that disallows any storage writes.
uint256 internal constant _GAS_STIPEND_NO_STORAGE_WRITES = 2300;
/// @dev Suggested gas stipend for contract receiving ETH to perform a few
/// storage reads and writes, but low enough to prevent griefing.
/// Multiply by a small constant (e.g. 2), if needed.
uint256 internal constant _GAS_STIPEND_NO_GRIEF = 100000;
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* ETH OPERATIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Sends `amount` (in wei) ETH to `to`.
/// Reverts upon failure.
function safeTransferETH(address to, uint256 amount) internal {
/// @solidity memory-safe-assembly
assembly {
// Transfer the ETH and check if it succeeded or not.
if iszero(call(gas(), to, amount, 0, 0, 0, 0)) {
// Store the function selector of `ETHTransferFailed()`.
mstore(0x00, 0xb12d13eb)
// Revert with (offset, size).
revert(0x1c, 0x04)
}
}
}
/// @dev Force sends `amount` (in wei) ETH to `to`, with a `gasStipend`.
/// The `gasStipend` can be set to a low enough value to prevent
/// storage writes or gas griefing.
///
/// If sending via the normal procedure fails, force sends the ETH by
/// creating a temporary contract which uses `SELFDESTRUCT` to force send the ETH.
///
/// Reverts if the current contract has insufficient balance.
function forceSafeTransferETH(address to, uint256 amount, uint256 gasStipend) internal {
/// @solidity memory-safe-assembly
assembly {
// If insufficient balance, revert.
if lt(selfbalance(), amount) {
// Store the function selector of `ETHTransferFailed()`.
mstore(0x00, 0xb12d13eb)
// Revert with (offset, size).
revert(0x1c, 0x04)
}
// Transfer the ETH and check if it succeeded or not.
if iszero(call(gasStipend, to, amount, 0, 0, 0, 0)) {
mstore(0x00, to) // Store the address in scratch space.
mstore8(0x0b, 0x73) // Opcode `PUSH20`.
mstore8(0x20, 0xff) // Opcode `SELFDESTRUCT`.
// We can directly use `SELFDESTRUCT` in the contract creation.
// Compatible with `SENDALL`: https://eips.ethereum.org/EIPS/eip-4758
if iszero(create(amount, 0x0b, 0x16)) { revert(0, 0) } // For better gas estimation.
}
}
}
/// @dev Force sends `amount` (in wei) ETH to `to`, with a gas stipend
/// equal to `_GAS_STIPEND_NO_GRIEF`. This gas stipend is a reasonable default
/// for 99% of cases and can be overriden with the three-argument version of this
/// function if necessary.
///
/// If sending via the normal procedure fails, force sends the ETH by
/// creating a temporary contract which uses `SELFDESTRUCT` to force send the ETH.
///
/// Reverts if the current contract has insufficient balance.
function forceSafeTransferETH(address to, uint256 amount) internal {
// Manually inlined because the compiler doesn't inline functions with branches.
/// @solidity memory-safe-assembly
assembly {
// If insufficient balance, revert.
if lt(selfbalance(), amount) {
// Store the function selector of `ETHTransferFailed()`.
mstore(0x00, 0xb12d13eb)
// Revert with (offset, size).
revert(0x1c, 0x04)
}
// Transfer the ETH and check if it succeeded or not.
if iszero(call(_GAS_STIPEND_NO_GRIEF, to, amount, 0, 0, 0, 0)) {
mstore(0x00, to) // Store the address in scratch space.
mstore8(0x0b, 0x73) // Opcode `PUSH20`.
mstore8(0x20, 0xff) // Opcode `SELFDESTRUCT`.
// We can directly use `SELFDESTRUCT` in the contract creation.
// Compatible with `SENDALL`: https://eips.ethereum.org/EIPS/eip-4758
if iszero(create(amount, 0x0b, 0x16)) { revert(0, 0) } // For better gas estimation.
}
}
}
/// @dev Sends `amount` (in wei) ETH to `to`, with a `gasStipend`.
/// The `gasStipend` can be set to a low enough value to prevent
/// storage writes or gas griefing.
///
/// Simply use `gasleft()` for `gasStipend` if you don't need a gas stipend.
///
/// Note: Does NOT revert upon failure.
/// Returns whether the transfer of ETH is successful instead.
function trySafeTransferETH(address to, uint256 amount, uint256 gasStipend)
internal
returns (bool success)
{
/// @solidity memory-safe-assembly
assembly {
// Transfer the ETH and check if it succeeded or not.
success := call(gasStipend, to, amount, 0, 0, 0, 0)
}
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* ERC20 OPERATIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Sends `amount` of ERC20 `token` from `from` to `to`.
/// Reverts upon failure.
///
/// The `from` account must have at least `amount` approved for
/// the current contract to manage.
function safeTransferFrom(address token, address from, address to, uint256 amount) internal {
/// @solidity memory-safe-assembly
assembly {
let m := mload(0x40) // Cache the free memory pointer.
// Store the function selector of `transferFrom(address,address,uint256)`.
mstore(0x00, 0x23b872dd)
mstore(0x20, from) // Store the `from` argument.
mstore(0x40, to) // Store the `to` argument.
mstore(0x60, amount) // Store the `amount` argument.
if iszero(
and( // The arguments of `and` are evaluated from right to left.
// Set success to whether the call reverted, if not we check it either
// returned exactly 1 (can't just be non-zero data), or had no return data.
or(eq(mload(0x00), 1), iszero(returndatasize())),
call(gas(), token, 0, 0x1c, 0x64, 0x00, 0x20)
)
) {
// Store the function selector of `TransferFromFailed()`.
mstore(0x00, 0x7939f424)
// Revert with (offset, size).
revert(0x1c, 0x04)
}
mstore(0x60, 0) // Restore the zero slot to zero.
mstore(0x40, m) // Restore the free memory pointer.
}
}
/// @dev Sends all of ERC20 `token` from `from` to `to`.
/// Reverts upon failure.
///
/// The `from` account must have at least `amount` approved for
/// the current contract to manage.
function safeTransferAllFrom(address token, address from, address to)
internal
returns (uint256 amount)
{
/// @solidity memory-safe-assembly
assembly {
let m := mload(0x40) // Cache the free memory pointer.
mstore(0x00, 0x70a08231) // Store the function selector of `balanceOf(address)`.
mstore(0x20, from) // Store the `from` argument.
if iszero(
and( // The arguments of `and` are evaluated from right to left.
gt(returndatasize(), 0x1f), // At least 32 bytes returned.
staticcall(gas(), token, 0x1c, 0x24, 0x60, 0x20)
)
) {
// Store the function selector of `TransferFromFailed()`.
mstore(0x00, 0x7939f424)
// Revert with (offset, size).
revert(0x1c, 0x04)
}
// Store the function selector of `transferFrom(address,address,uint256)`.
mstore(0x00, 0x23b872dd)
mstore(0x40, to) // Store the `to` argument.
// The `amount` argument is already written to the memory word at 0x6a.
amount := mload(0x60)
if iszero(
and( // The arguments of `and` are evaluated from right to left.
// Set success to whether the call reverted, if not we check it either
// returned exactly 1 (can't just be non-zero data), or had no return data.
or(eq(mload(0x00), 1), iszero(returndatasize())),
call(gas(), token, 0, 0x1c, 0x64, 0x00, 0x20)
)
) {
// Store the function selector of `TransferFromFailed()`.
mstore(0x00, 0x7939f424)
// Revert with (offset, size).
revert(0x1c, 0x04)
}
mstore(0x60, 0) // Restore the zero slot to zero.
mstore(0x40, m) // Restore the free memory pointer.
}
}
/// @dev Sends `amount` of ERC20 `token` from the current contract to `to`.
/// Reverts upon failure.
function safeTransfer(address token, address to, uint256 amount) internal {
/// @solidity memory-safe-assembly
assembly {
mstore(0x1a, to) // Store the `to` argument.
mstore(0x3a, amount) // Store the `amount` argument.
// Store the function selector of `transfer(address,uint256)`,
// left by 6 bytes (enough for 8tb of memory represented by the free memory pointer).
// We waste 6-3 = 3 bytes to save on 6 runtime gas (PUSH1 0x224 SHL).
mstore(0x00, 0xa9059cbb000000000000)
if iszero(
and( // The arguments of `and` are evaluated from right to left.
// Set success to whether the call reverted, if not we check it either
// returned exactly 1 (can't just be non-zero data), or had no return data.
or(eq(mload(0x00), 1), iszero(returndatasize())),
call(gas(), token, 0, 0x16, 0x44, 0x00, 0x20)
)
) {
// Store the function selector of `TransferFailed()`.
mstore(0x00, 0x90b8ec18)
// Revert with (offset, size).
revert(0x1c, 0x04)
}
// Restore the part of the free memory pointer that was overwritten,
// which is guaranteed to be zero, if less than 8tb of memory is used.
mstore(0x3a, 0)
}
}
/// @dev Sends all of ERC20 `token` from the current contract to `to`.
/// Reverts upon failure.
function safeTransferAll(address token, address to) internal returns (uint256 amount) {
/// @solidity memory-safe-assembly
assembly {
mstore(0x00, 0x70a08231) // Store the function selector of `balanceOf(address)`.
mstore(0x20, address()) // Store the address of the current contract.
if iszero(
and( // The arguments of `and` are evaluated from right to left.
gt(returndatasize(), 0x1f), // At least 32 bytes returned.
staticcall(gas(), token, 0x1c, 0x24, 0x3a, 0x20)
)
) {
// Store the function selector of `TransferFailed()`.
mstore(0x00, 0x90b8ec18)
// Revert with (offset, size).
revert(0x1c, 0x04)
}
mstore(0x1a, to) // Store the `to` argument.
// The `amount` argument is already written to the memory word at 0x3a.
amount := mload(0x3a)
// Store the function selector of `transfer(address,uint256)`,
// left by 6 bytes (enough for 8tb of memory represented by the free memory pointer).
// We waste 6-3 = 3 bytes to save on 6 runtime gas (PUSH1 0x224 SHL).
mstore(0x00, 0xa9059cbb000000000000)
if iszero(
and( // The arguments of `and` are evaluated from right to left.
// Set success to whether the call reverted, if not we check it either
// returned exactly 1 (can't just be non-zero data), or had no return data.
or(eq(mload(0x00), 1), iszero(returndatasize())),
call(gas(), token, 0, 0x16, 0x44, 0x00, 0x20)
)
) {
// Store the function selector of `TransferFailed()`.
mstore(0x00, 0x90b8ec18)
// Revert with (offset, size).
revert(0x1c, 0x04)
}
// Restore the part of the free memory pointer that was overwritten,
// which is guaranteed to be zero, if less than 8tb of memory is used.
mstore(0x3a, 0)
}
}
/// @dev Sets `amount` of ERC20 `token` for `to` to manage on behalf of the current contract.
/// Reverts upon failure.
function safeApprove(address token, address to, uint256 amount) internal {
/// @solidity memory-safe-assembly
assembly {
mstore(0x1a, to) // Store the `to` argument.
mstore(0x3a, amount) // Store the `amount` argument.
// Store the function selector of `approve(address,uint256)`,
// left by 6 bytes (enough for 8tb of memory represented by the free memory pointer).
// We waste 6-3 = 3 bytes to save on 6 runtime gas (PUSH1 0x224 SHL).
mstore(0x00, 0x095ea7b3000000000000)
if iszero(
and( // The arguments of `and` are evaluated from right to left.
// Set success to whether the call reverted, if not we check it either
// returned exactly 1 (can't just be non-zero data), or had no return data.
or(eq(mload(0x00), 1), iszero(returndatasize())),
call(gas(), token, 0, 0x16, 0x44, 0x00, 0x20)
)
) {
// Store the function selector of `ApproveFailed()`.
mstore(0x00, 0x3e3f8f73)
// Revert with (offset, size).
revert(0x1c, 0x04)
}
// Restore the part of the free memory pointer that was overwritten,
// which is guaranteed to be zero, if less than 8tb of memory is used.
mstore(0x3a, 0)
}
}
/// @dev Returns the amount of ERC20 `token` owned by `account`.
/// Returns zero if the `token` does not exist.
function balanceOf(address token, address account) internal view returns (uint256 amount) {
/// @solidity memory-safe-assembly
assembly {
mstore(0x00, 0x70a08231) // Store the function selector of `balanceOf(address)`.
mstore(0x20, account) // Store the `account` argument.
amount :=
mul(
mload(0x20),
and( // The arguments of `and` are evaluated from right to left.
gt(returndatasize(), 0x1f), // At least 32 bytes returned.
staticcall(gas(), token, 0x1c, 0x24, 0x20, 0x20)
)
)
}
}
}
Read Contract
passThroughWalletImpl 0x86b2162e → address
Write Contract 1 functions
These functions modify contract state and require a wallet transaction to execute.
createPassThroughWallet 0xca85dc51
tuple params_
returns: address
Recent Transactions
This address has 35 on-chain transactions, but only 1.2% of the chain is indexed. Transactions will appear as indexing progresses. View on Etherscan →