Address Contract Verified
Address
0x981a6aC55c7D39f50666938CcD0df53D59797e87
Balance
0 ETH
Nonce
1
Code Size
4800 bytes
Creator
0x8cC55811...9186 at tx 0xa64629bc...446629
Indexed Transactions
0
Contract Bytecode
4800 bytes
0x60806040526004361061005e5760003560e01c806350879c1c1161004357806350879c1c1461009f578063c31c9c07146100fc578063d122d54a1461013057600080fd5b80631faf83c31461006a5780632602cbb71461007f57600080fd5b3661006557005b600080fd5b61007d61007836600461094b565b610164565b005b34801561008b57600080fd5b5061007d61009a3660046109b2565b61021c565b3480156100ab57600080fd5b506100d37f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc281565b60405173ffffffffffffffffffffffffffffffffffffffff909116815260200160405180910390f35b34801561010857600080fd5b506100d37f000000000000000000000000e592427a0aece92de3edee1f18e0157c0586156481565b34801561013c57600080fd5b506100d37f000000000000000000000000a244bbe019cf1ba177ee5a532250be2663fb55ca81565b73ffffffffffffffffffffffffffffffffffffffff82166392987f8561018a8380610a3b565b6101976020860186610aaa565b6040516020016101a79190610b95565b6040516020818303038152906040526040518463ffffffff1660e01b81526004016101d493929190610d98565b6020604051808303816000875af11580156101f3573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906102179190610ed5565b505050565b600061022a82840184610fb8565b80519091504780156102b8577f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc273ffffffffffffffffffffffffffffffffffffffff1663d0e30db0826040518263ffffffff1660e01b81526004016000604051808303818588803b15801561029e57600080fd5b505af11580156102b2573d6000803e3d6000fd5b50505050505b600073ffffffffffffffffffffffffffffffffffffffff8816156102fb576102f673ffffffffffffffffffffffffffffffffffffffff89163061083d565b6103a9565b6040517f70a082310000000000000000000000000000000000000000000000000000000081523060048201527f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc273ffffffffffffffffffffffffffffffffffffffff16906370a0823190602401602060405180830381865afa158015610385573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906103a99190610ed5565b835190915060005b818110156105075760008582815181106103cd576103cd61119a565b6020026020010151905060006103e882600001516014015190565b90506104397f000000000000000000000000e592427a0aece92de3edee1f18e0157c0586156483606001518373ffffffffffffffffffffffffffffffffffffffff166108729092919063ffffffff16565b6040517fc04b8d5900000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000e592427a0aece92de3edee1f18e0157c05861564169063c04b8d59906104ab9085906004016111c9565b6020604051808303816000875af11580156104ca573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906104ee9190610ed5565b6104f8908661125e565b945082600101925050506103b1565b5087821015610542576040517f356680b700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b602085015173ffffffffffffffffffffffffffffffffffffffff8a16610748576040517f70a082310000000000000000000000000000000000000000000000000000000081523060048201526000907f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc273ffffffffffffffffffffffffffffffffffffffff16906370a0823190602401602060405180830381865afa1580156105ef573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906106139190610ed5565b6040517f2e1a7d4d000000000000000000000000000000000000000000000000000000008152600481018290529091507f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc273ffffffffffffffffffffffffffffffffffffffff1690632e1a7d4d90602401600060405180830381600087803b15801561069e57600080fd5b505af11580156106b2573d6000803e3d6000fd5b505050503373ffffffffffffffffffffffffffffffffffffffff1663854bec878b6040518263ffffffff1660e01b81526004016000604051808303818588803b1580156106fe57600080fd5b505af1158015610712573d6000803e3d6000fd5b5050505050479450846000146107425761074273ffffffffffffffffffffffffffffffffffffffff8316866108c1565b50610831565b61076973ffffffffffffffffffffffffffffffffffffffff8b16338b610872565b6040517f70a082310000000000000000000000000000000000000000000000000000000081523060048201526000908a9073ffffffffffffffffffffffffffffffffffffffff8d16906370a0823190602401602060405180830381865afa1580156107d8573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906107fc9190610ed5565b6108069190611277565b9050801561082f5761082f73ffffffffffffffffffffffffffffffffffffffff8c1683836108e1565b505b50505050505050505050565b6000816014526f70a0823100000000000000000000000060005260208060246010865afa601f3d111660205102905092915050565b81601452806034526f095ea7b300000000000000000000000060005260206000604460106000875af13d1560016000511417166108b757633e3f8f736000526004601cfd5b6000603452505050565b60008060008084865af16108dd5763b12d13eb6000526004601cfd5b5050565b81601452806034526fa9059cbb00000000000000000000000060005260206000604460106000875af13d1560016000511417166108b7576390b8ec186000526004601cfd5b73ffffffffffffffffffffffffffffffffffffffff8116811461094857600080fd5b50565b6000806040838503121561095e57600080fd5b823561096981610926565b9150602083013567ffffffffffffffff81111561098557600080fd5b83016040818603121561099757600080fd5b809150509250929050565b80356109ad81610926565b919050565b600080600080606085870312156109c857600080fd5b84356109d381610926565b935060208501359250604085013567ffffffffffffffff808211156109f757600080fd5b818701915087601f830112610a0b57600080fd5b813581811115610a1a57600080fd5b886020828501011115610a2c57600080fd5b95989497505060200194505050565b60008083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe1843603018112610a7057600080fd5b83018035915067ffffffffffffffff821115610a8b57600080fd5b6020019150600581901b3603821315610aa357600080fd5b9250929050565b600082357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc1833603018112610ade57600080fd5b9190910192915050565b60008083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe1843603018112610b1d57600080fd5b830160208101925035905067ffffffffffffffff811115610b3d57600080fd5b803603821315610aa357600080fd5b8183528181602085013750600060208284010152600060207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f840116840101905092915050565b600060208083526060830184357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe1863603018112610bd257600080fd5b8501803583820167ffffffffffffffff821115610bee57600080fd5b8160051b803603821315610c0157600080fd5b604088870181905294839052608094908801850190858901600080368890037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff41015b87821015610cfd577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff808d87030184528635818112610c7f578384fd5b89018b810160a0610c908280610ae8565b9250818a52610ca2828b018483610b4c565b92505087830135610cb281610926565b73ffffffffffffffffffffffffffffffffffffffff16898f0152606083810135898b01528d840135908a015290910135968b0196909652958a0195928a019260019190910190610c43565b50505050610d0c878b016109a2565b73ffffffffffffffffffffffffffffffffffffffff1698019790975250949695505050505050565b6000815180845260005b81811015610d5a57602081850181015186830182015201610d3e565b5060006020828601015260207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f83011685010191505092915050565b60408082528181018490526000906060808401600587901b8501820188855b89811015610eb4577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa088840301845281357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff818c3603018112610e1857600080fd5b8b0160808135610e2781610926565b73ffffffffffffffffffffffffffffffffffffffff908116865260209083820135610e5181610926565b1686820152828901356fffffffffffffffffffffffffffffffff8116808214610e7957600080fd5b878b015250610e8a83890184610ae8565b93508289880152610e9e8388018583610b4c565b9782019796505093909301925050600101610db7565b50508581036020870152610ec88188610d34565b9998505050505050505050565b600060208284031215610ee757600080fd5b5051919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b6040805190810167ffffffffffffffff81118282101715610f4057610f40610eee565b60405290565b60405160a0810167ffffffffffffffff81118282101715610f4057610f40610eee565b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016810167ffffffffffffffff81118282101715610fb057610fb0610eee565b604052919050565b600060208284031215610fca57600080fd5b67ffffffffffffffff8083351115610fe157600080fd5b8235830160408186031215610ff557600080fd5b610ffd610f1d565b828235111561100b57600080fd5b8135820186601f82011261101e57600080fd5b838135111561102f5761102f610eee565b61103f6020823560051b01610f69565b81358082526020808301929160051b8401018981111561105e57600080fd5b602084015b8181101561117957878135111561107957600080fd5b803585017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe060a081838f030112156110b057600080fd5b6110b8610f46565b8a602084013511156110c957600080fd5b602083013583018e603f8201126110df57600080fd5b60208101358c8111156110f4576110f4610eee565b611105602085601f84011601610f69565b93508084528f604082840101111561111c57600080fd5b806040830160208601376000908401602001525081815261113f604084016109a2565b6020820152606083013560408201526080830135606082015260a08301356080820152808752505050602084019350602081019050611063565b505083525061118c9050602083016109a2565b602082015295945050505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b602081526000825160a060208401526111e560c0840182610d34565b905073ffffffffffffffffffffffffffffffffffffffff60208501511660408401526040840151606084015260608401516080840152608084015160a08401528091505092915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b808201808211156112715761127161122f565b92915050565b818103818111156112715761127161122f56fea26469706673582212200df864d4350734e1256b03291e5051b42eb906200a224a11f6ee54907e4331ab64736f6c63430008110033
Verified Source Code Full Match
Compiler: v0.8.17+commit.8df45f5f
EVM: london
Optimization: Yes (1000000 runs)
SwapperFactory.sol 56 lines
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.8.17;
import {IOracle} from "splits-oracle/interfaces/IOracle.sol";
import {OracleParams} from "splits-oracle/peripherals/OracleParams.sol";
import {LibClone} from "splits-utils/LibClone.sol";
import {SwapperImpl} from "./SwapperImpl.sol";
/// @title Swapper Factory
/// @author 0xSplits
/// @notice Factory for creating Swappers
/// @dev This contract uses token = address(0) to refer to ETH.
contract SwapperFactory {
using LibClone for address;
event CreateSwapper(SwapperImpl indexed swapper, SwapperImpl.InitParams params);
struct CreateSwapperParams {
address owner;
bool paused;
address beneficiary;
address tokenToBeneficiary;
OracleParams oracleParams;
uint32 defaultScaledOfferFactor;
SwapperImpl.SetPairScaledOfferFactorParams[] pairScaledOfferFactors;
}
SwapperImpl public immutable swapperImpl;
constructor() {
swapperImpl = new SwapperImpl();
}
/// -----------------------------------------------------------------------
/// functions - public & external
/// -----------------------------------------------------------------------
function createSwapper(CreateSwapperParams calldata params_) external returns (SwapperImpl swapper) {
IOracle oracle = params_.oracleParams._parseIntoOracle();
swapper = SwapperImpl(payable(address(swapperImpl).clone()));
SwapperImpl.InitParams memory swapperInitParams = SwapperImpl.InitParams({
owner: params_.owner,
paused: params_.paused,
beneficiary: params_.beneficiary,
tokenToBeneficiary: params_.tokenToBeneficiary,
oracle: oracle,
defaultScaledOfferFactor: params_.defaultScaledOfferFactor,
pairScaledOfferFactors: params_.pairScaledOfferFactors
});
swapper.initializer(swapperInitParams);
emit CreateSwapper({swapper: swapper, params: swapperInitParams});
}
}
UniV3Swap.sol 123 lines
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.8.17;
import {ERC20} from "solmate/tokens/ERC20.sol";
import {IOracle} from "splits-oracle/interfaces/IOracle.sol";
import {ISwapRouter} from "v3-periphery/interfaces/ISwapRouter.sol";
import {IWETH9} from "splits-utils/interfaces/external/IWETH9.sol";
import {QuoteParams} from "splits-utils/LibQuotes.sol";
import {SafeTransferLib} from "solady/utils/SafeTransferLib.sol";
import {TokenUtils} from "splits-utils/TokenUtils.sol";
import {ISwapperFlashCallback} from "../interfaces/ISwapperFlashCallback.sol";
import {SwapperImpl} from "../SwapperImpl.sol";
import {SwapperFactory} from "../SwapperFactory.sol";
/// @title Uniswap V3 Swapper Integration
/// @author 0xSplits
/// @notice Used by EOAs & simple bots to execute `Swapper#flash` with uniswap v3
/// @dev This contract uses token = address(0) to refer to ETH.
contract UniV3Swap is ISwapperFlashCallback {
using SafeTransferLib for address;
using TokenUtils for address;
error Unauthorized();
error InsufficientFunds();
struct InitFlashParams {
QuoteParams[] quoteParams;
FlashCallbackData flashCallbackData;
}
struct FlashCallbackData {
ISwapRouter.ExactInputParams[] exactInputParams;
address excessRecipient;
}
SwapperFactory public immutable swapperFactory;
ISwapRouter public immutable swapRouter;
IWETH9 public immutable weth9;
constructor(SwapperFactory swapperFactory_, ISwapRouter swapRouter_, IWETH9 weth9_) {
swapperFactory = swapperFactory_;
swapRouter = swapRouter_;
weth9 = weth9_;
}
/// receive from weth9
receive() external payable {}
/// begin `Swapper#flash`
/// @dev trader may pay ETH & include the extra WETH in `params_.exactInputParams` to make up
/// for `Swapper#oracle` shortfall. If swapper incentives are insufficient and they still want to push
/// funds to `beneficiary`. Recipient in `params_.exactInputParams` should always be _this_ contract
/// so it can handle the approval / payback for Swapper
function initFlash(SwapperImpl swapper, InitFlashParams calldata params_) external payable {
swapper.flash(params_.quoteParams, abi.encode(params_.flashCallbackData));
}
/// `Swapper#flash` callback
/// @dev by end of function if `tokenToBeneficiary_` is ETH, must have sent `amountToBeneficiary_`
/// to `Swapper#payback`. Otherwise, must approve Swapper to transfer `amountToBeneficiary_`
/// DO NOT HOLD FUNDS IN THIS CONTRACT WITHOUT ADDING PROPER VERIFICATION OF MSG.SENDER
function swapperFlashCallback(address tokenToBeneficiary_, uint256 amountToBeneficiary_, bytes calldata data_)
external
{
FlashCallbackData memory flashCallbackData = abi.decode(data_, (FlashCallbackData));
ISwapRouter.ExactInputParams[] memory exactInputParams = flashCallbackData.exactInputParams;
uint256 ethBalance = address(this).balance;
if (ethBalance != 0) {
weth9.deposit{value: ethBalance}();
}
uint256 totalOut = (tokenToBeneficiary_._isETH())
? weth9.balanceOf(address(this))
: tokenToBeneficiary_.balanceOf(address(this));
uint256 length = exactInputParams.length;
for (uint256 i; i < length;) {
ISwapRouter.ExactInputParams memory eip = exactInputParams[i];
address token = _getStartTokenFromPath(eip.path);
token.safeApprove(address(swapRouter), eip.amountIn);
totalOut += swapRouter.exactInput(eip);
unchecked {
++i;
}
}
if (totalOut < amountToBeneficiary_) revert InsufficientFunds();
address excessRecipient = flashCallbackData.excessRecipient;
if (tokenToBeneficiary_._isETH()) {
// withdraw WETH from uni swaps to ETH
uint256 weth9Balance = weth9.balanceOf(address(this));
weth9.withdraw(weth9Balance);
// send req'd amt to swapper#payback
SwapperImpl(msg.sender).payback{value: amountToBeneficiary_}();
// xfr excess out
ethBalance = address(this).balance;
if (ethBalance != 0) {
excessRecipient.safeTransferETH(ethBalance);
}
} else {
// approve swapper to xfr req'd amt out
tokenToBeneficiary_.safeApprove(msg.sender, amountToBeneficiary_);
// xfr excess out
uint256 excessBalance = ERC20(tokenToBeneficiary_).balanceOf(address(this)) - amountToBeneficiary_;
if (excessBalance > 0) {
tokenToBeneficiary_.safeTransfer(excessRecipient, excessBalance);
}
}
}
function _getStartTokenFromPath(bytes memory path) internal pure returns (address token) {
assembly {
token := mload(add(path, 0x14))
}
}
}
ERC20.sol 206 lines
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity >=0.8.0;
/// @notice Modern and gas efficient ERC20 + EIP-2612 implementation.
/// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/tokens/ERC20.sol)
/// @author Modified from Uniswap (https://github.com/Uniswap/uniswap-v2-core/blob/master/contracts/UniswapV2ERC20.sol)
/// @dev Do not manually set balances without updating totalSupply, as the sum of all user balances must not exceed it.
abstract contract ERC20 {
/*//////////////////////////////////////////////////////////////
EVENTS
//////////////////////////////////////////////////////////////*/
event Transfer(address indexed from, address indexed to, uint256 amount);
event Approval(address indexed owner, address indexed spender, uint256 amount);
/*//////////////////////////////////////////////////////////////
METADATA STORAGE
//////////////////////////////////////////////////////////////*/
string public name;
string public symbol;
uint8 public immutable decimals;
/*//////////////////////////////////////////////////////////////
ERC20 STORAGE
//////////////////////////////////////////////////////////////*/
uint256 public totalSupply;
mapping(address => uint256) public balanceOf;
mapping(address => mapping(address => uint256)) public allowance;
/*//////////////////////////////////////////////////////////////
EIP-2612 STORAGE
//////////////////////////////////////////////////////////////*/
uint256 internal immutable INITIAL_CHAIN_ID;
bytes32 internal immutable INITIAL_DOMAIN_SEPARATOR;
mapping(address => uint256) public nonces;
/*//////////////////////////////////////////////////////////////
CONSTRUCTOR
//////////////////////////////////////////////////////////////*/
constructor(
string memory _name,
string memory _symbol,
uint8 _decimals
) {
name = _name;
symbol = _symbol;
decimals = _decimals;
INITIAL_CHAIN_ID = block.chainid;
INITIAL_DOMAIN_SEPARATOR = computeDomainSeparator();
}
/*//////////////////////////////////////////////////////////////
ERC20 LOGIC
//////////////////////////////////////////////////////////////*/
function approve(address spender, uint256 amount) public virtual returns (bool) {
allowance[msg.sender][spender] = amount;
emit Approval(msg.sender, spender, amount);
return true;
}
function transfer(address to, uint256 amount) public virtual returns (bool) {
balanceOf[msg.sender] -= amount;
// Cannot overflow because the sum of all user
// balances can't exceed the max uint256 value.
unchecked {
balanceOf[to] += amount;
}
emit Transfer(msg.sender, to, amount);
return true;
}
function transferFrom(
address from,
address to,
uint256 amount
) public virtual returns (bool) {
uint256 allowed = allowance[from][msg.sender]; // Saves gas for limited approvals.
if (allowed != type(uint256).max) allowance[from][msg.sender] = allowed - amount;
balanceOf[from] -= amount;
// Cannot overflow because the sum of all user
// balances can't exceed the max uint256 value.
unchecked {
balanceOf[to] += amount;
}
emit Transfer(from, to, amount);
return true;
}
/*//////////////////////////////////////////////////////////////
EIP-2612 LOGIC
//////////////////////////////////////////////////////////////*/
function permit(
address owner,
address spender,
uint256 value,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) public virtual {
require(deadline >= block.timestamp, "PERMIT_DEADLINE_EXPIRED");
// Unchecked because the only math done is incrementing
// the owner's nonce which cannot realistically overflow.
unchecked {
address recoveredAddress = ecrecover(
keccak256(
abi.encodePacked(
"\x19\x01",
DOMAIN_SEPARATOR(),
keccak256(
abi.encode(
keccak256(
"Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)"
),
owner,
spender,
value,
nonces[owner]++,
deadline
)
)
)
),
v,
r,
s
);
require(recoveredAddress != address(0) && recoveredAddress == owner, "INVALID_SIGNER");
allowance[recoveredAddress][spender] = value;
}
emit Approval(owner, spender, value);
}
function DOMAIN_SEPARATOR() public view virtual returns (bytes32) {
return block.chainid == INITIAL_CHAIN_ID ? INITIAL_DOMAIN_SEPARATOR : computeDomainSeparator();
}
function computeDomainSeparator() internal view virtual returns (bytes32) {
return
keccak256(
abi.encode(
keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"),
keccak256(bytes(name)),
keccak256("1"),
block.chainid,
address(this)
)
);
}
/*//////////////////////////////////////////////////////////////
INTERNAL MINT/BURN LOGIC
//////////////////////////////////////////////////////////////*/
function _mint(address to, uint256 amount) internal virtual {
totalSupply += amount;
// Cannot overflow because the sum of all user
// balances can't exceed the max uint256 value.
unchecked {
balanceOf[to] += amount;
}
emit Transfer(address(0), to, amount);
}
function _burn(address from, uint256 amount) internal virtual {
balanceOf[from] -= amount;
// Cannot underflow because a user's balance
// will never be larger than the total supply.
unchecked {
totalSupply -= amount;
}
emit Transfer(from, address(0), amount);
}
}
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;
}
}
SwapperImpl.sol 358 lines
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.8.17;
import {ERC20} from "solmate/tokens/ERC20.sol";
import {IOracle} from "splits-oracle/interfaces/IOracle.sol";
import {PausableImpl} from "splits-utils/PausableImpl.sol";
import {QuotePair, QuoteParams, SortedQuotePair} from "splits-utils/LibQuotes.sol";
import {SafeCastLib} from "solady/utils/SafeCastLib.sol";
import {SafeTransferLib} from "solady/utils/SafeTransferLib.sol";
import {TokenUtils} from "splits-utils/TokenUtils.sol";
import {WalletImpl} from "splits-utils/WalletImpl.sol";
import {ISwapperFlashCallback} from "./interfaces/ISwapperFlashCallback.sol";
import {PairScaledOfferFactors} from "./libraries/PairScaledOfferFactors.sol";
/// @title Swapper Implementation
/// @author 0xSplits
/// @notice A contract to trustlessly & automatically convert multi-token
/// onchain revenue into a particular output token.
/// Please be aware, owner has _FULL CONTROL_ of the deployment.
/// @dev This contract uses a modular oracle. Be very careful to use a secure
/// oracle with sensible settings for the desired behavior. Insecure oracles
/// will result in catastrophic loss of funds.
/// This contract uses token = address(0) to refer to ETH.
contract SwapperImpl is WalletImpl, PausableImpl {
/// -----------------------------------------------------------------------
/// libraries
/// -----------------------------------------------------------------------
using SafeTransferLib for address;
using SafeCastLib for uint256;
using TokenUtils for address;
using PairScaledOfferFactors for mapping(address => mapping(address => uint32));
/// -----------------------------------------------------------------------
/// errors
/// -----------------------------------------------------------------------
error Invalid_AmountsToBeneficiary();
error Invalid_QuoteToken();
error InsufficientFunds_InContract();
error InsufficientFunds_FromTrader();
/// -----------------------------------------------------------------------
/// structs
/// -----------------------------------------------------------------------
struct InitParams {
address owner;
bool paused;
address beneficiary;
address tokenToBeneficiary;
IOracle oracle;
uint32 defaultScaledOfferFactor;
SetPairScaledOfferFactorParams[] pairScaledOfferFactors;
}
struct SetPairScaledOfferFactorParams {
QuotePair quotePair;
uint32 scaledOfferFactor;
}
/// -----------------------------------------------------------------------
/// events
/// -----------------------------------------------------------------------
event SetBeneficiary(address beneficiary);
event SetTokenToBeneficiary(address tokenToBeneficiary);
event SetOracle(IOracle oracle);
event SetDefaultScaledOfferFactor(uint32 defaultScaledOfferFactor);
event SetPairScaledOfferFactors(SetPairScaledOfferFactorParams[] params);
event ReceiveETH(uint256 amount);
event Payback(address indexed payer, uint256 amount);
event Flash(
address indexed beneficiary,
address indexed trader,
QuoteParams[] quoteParams,
address tokenToBeneficiary,
uint256[] amountsToBeneficiary,
uint256 excessToBeneficiary
);
/// -----------------------------------------------------------------------
/// storage
/// -----------------------------------------------------------------------
/// -----------------------------------------------------------------------
/// storage - constants & immutables
/// -----------------------------------------------------------------------
address public immutable swapperFactory;
/// @dev percentages measured in hundredths of basis points
uint32 internal constant PERCENTAGE_SCALE = 100_00_00; // = 100%
/// -----------------------------------------------------------------------
/// storage - mutables
/// -----------------------------------------------------------------------
/// slot 0 - 11 bytes free
/// OwnableImpl storage
/// address internal $owner;
/// 20 bytes
/// PausableImpl storage
/// bool internal $paused;
/// 1 byte
/// slot 1 - 0 bytes free
/// address to receive post-swap tokens
address internal $beneficiary;
/// 20 bytes
/// used to track ETH payback in flash
uint96 internal $_payback;
/// 12 bytes
/// slot 2 - 8 bytes free
/// token type to send beneficiary
/// @dev 0x0 used for ETH
address internal $tokenToBeneficiary;
/// 20 bytes
/// default oracle price scaling factor
/// @dev PERCENTAGE_SCALE = 1e6 = 100_00_00 = 100% = no discount or premium
/// 99_00_00 = 99% = 1% discount to oracle; 101_00_00 = 101% = 1% premium to oracle
/// 4 bytes
uint32 internal $defaultScaledOfferFactor;
/// slot 3 - 12 bytes free
/// price oracle for `#flash`
IOracle internal $oracle;
/// 20 bytes
/// slot 4 - 0 bytes free
/// scaledOfferFactors for specific quote pairs
/// 32 bytes
mapping(address => mapping(address => uint32)) internal $_pairScaledOfferFactors;
/// -----------------------------------------------------------------------
/// constructor & initializer
/// -----------------------------------------------------------------------
constructor() {
swapperFactory = msg.sender;
}
function initializer(InitParams calldata params_) external {
// only swapperFactory may call `initializer`
if (msg.sender != swapperFactory) revert Unauthorized();
// don't need to init wallet separately
__initPausable({owner_: params_.owner, paused_: params_.paused});
$beneficiary = params_.beneficiary;
$tokenToBeneficiary = params_.tokenToBeneficiary;
$oracle = params_.oracle;
$defaultScaledOfferFactor = params_.defaultScaledOfferFactor;
$_pairScaledOfferFactors._set(params_.pairScaledOfferFactors);
}
/// -----------------------------------------------------------------------
/// functions
/// -----------------------------------------------------------------------
/// -----------------------------------------------------------------------
/// functions - public & external
/// -----------------------------------------------------------------------
/// -----------------------------------------------------------------------
/// functions - public & external - onlyOwner
/// -----------------------------------------------------------------------
/// set beneficiary
function setBeneficiary(address beneficiary_) external onlyOwner {
$beneficiary = beneficiary_;
emit SetBeneficiary(beneficiary_);
}
/// set tokenToBeneficiary
function setTokenToBeneficiary(address tokenToBeneficiary_) external onlyOwner {
$tokenToBeneficiary = tokenToBeneficiary_;
emit SetTokenToBeneficiary(tokenToBeneficiary_);
}
/// set oracle
function setOracle(IOracle oracle_) external onlyOwner {
$oracle = oracle_;
emit SetOracle(oracle_);
}
/// set defaultScaledOfferFactor
function setDefaultScaledOfferFactor(uint32 defaultScaledOfferFactor_) external onlyOwner {
$defaultScaledOfferFactor = defaultScaledOfferFactor_;
emit SetDefaultScaledOfferFactor(defaultScaledOfferFactor_);
}
/// set pair scaled offer factors
function setPairScaledOfferFactors(SetPairScaledOfferFactorParams[] calldata params_) external onlyOwner {
$_pairScaledOfferFactors._set(params_);
emit SetPairScaledOfferFactors(params_);
}
/// -----------------------------------------------------------------------
/// functions - public & external - view
/// -----------------------------------------------------------------------
function beneficiary() external view returns (address) {
return $beneficiary;
}
function tokenToBeneficiary() external view returns (address) {
return $tokenToBeneficiary;
}
function oracle() external view returns (IOracle) {
return $oracle;
}
function defaultScaledOfferFactor() external view returns (uint32) {
return $defaultScaledOfferFactor;
}
/// get pair scaled offer factors for an array of quote pairs
function getPairScaledOfferFactors(QuotePair[] calldata quotePairs_)
external
view
returns (uint32[] memory pairScaledOfferFactors)
{
uint256 length = quotePairs_.length;
pairScaledOfferFactors = new uint32[](length);
for (uint256 i; i < length;) {
pairScaledOfferFactors[i] = $_pairScaledOfferFactors._get(quotePairs_[i]);
unchecked {
++i;
}
}
}
/// -----------------------------------------------------------------------
/// functions - public & external - permissionless
/// -----------------------------------------------------------------------
/// emit event when receiving ETH
/// @dev implemented w/i clone bytecode
/* receive() external payable { */
/* emit ReceiveETH(msg.value); */
/* } */
/// allows `#flash` to track ETH payback to `beneficiary`
/// @dev if used outside `#swapperFlashCallback`, msg.sender may lose funds.
/// Accumulates until next flash call
function payback() external payable {
$_payback += msg.value.toUint96();
emit Payback(msg.sender, msg.value);
}
/// allow third parties to withdraw tokens in return for sending `tokenToBeneficiary` to `beneficiary`
function flash(QuoteParams[] calldata quoteParams_, bytes calldata callbackData_)
external
pausable
returns (uint256)
{
address _tokenToBeneficiary = $tokenToBeneficiary;
(uint256 amountToBeneficiary, uint256[] memory amountsToBeneficiary) =
_transferToTrader(_tokenToBeneficiary, quoteParams_);
ISwapperFlashCallback(msg.sender).swapperFlashCallback({
tokenToBeneficiary: _tokenToBeneficiary,
amountToBeneficiary: amountToBeneficiary,
data: callbackData_
});
address _beneficiary = $beneficiary;
uint256 excessToBeneficiary = _transferToBeneficiary(_beneficiary, _tokenToBeneficiary, amountToBeneficiary);
emit Flash(
_beneficiary, msg.sender, quoteParams_, _tokenToBeneficiary, amountsToBeneficiary, excessToBeneficiary
);
return amountToBeneficiary + excessToBeneficiary;
}
/// -----------------------------------------------------------------------
/// functions - private & internal
/// -----------------------------------------------------------------------
function _transferToTrader(address tokenToBeneficiary_, QuoteParams[] calldata quoteParams_)
internal
returns (uint256 amountToBeneficiary, uint256[] memory amountsToBeneficiary)
{
uint256[] memory unscaledAmountsToBeneficiary = $oracle.getQuoteAmounts(quoteParams_);
uint256 length = quoteParams_.length;
if (unscaledAmountsToBeneficiary.length != length) revert Invalid_AmountsToBeneficiary();
amountsToBeneficiary = new uint256[](length);
uint256 scaledAmountToBeneficiary;
uint128 amountToTrader;
address tokenToTrader;
for (uint256 i; i < length;) {
QuoteParams calldata qp = quoteParams_[i];
if (tokenToBeneficiary_ != qp.quotePair.quote) revert Invalid_QuoteToken();
tokenToTrader = qp.quotePair.base;
amountToTrader = qp.baseAmount;
if (amountToTrader > tokenToTrader._balanceOf(address(this))) {
revert InsufficientFunds_InContract();
}
uint32 scaledOfferFactor = $_pairScaledOfferFactors._get(qp.quotePair._sort());
if (scaledOfferFactor == 0) {
scaledOfferFactor = $defaultScaledOfferFactor;
}
scaledAmountToBeneficiary = unscaledAmountsToBeneficiary[i] * scaledOfferFactor / PERCENTAGE_SCALE;
amountsToBeneficiary[i] = scaledAmountToBeneficiary;
amountToBeneficiary += scaledAmountToBeneficiary;
tokenToTrader._safeTransfer(msg.sender, amountToTrader);
unchecked {
++i;
}
}
}
function _transferToBeneficiary(address beneficiary_, address tokenToBeneficiary_, uint256 amountToBeneficiary_)
internal
returns (uint256 excessToBeneficiary)
{
if (tokenToBeneficiary_._isETH()) {
if ($_payback < amountToBeneficiary_) {
revert InsufficientFunds_FromTrader();
}
$_payback = 0;
// send ETH to `beneficiary`
uint256 ethBalance = address(this).balance;
excessToBeneficiary = ethBalance - amountToBeneficiary_;
beneficiary_.safeTransferETH(ethBalance);
} else {
tokenToBeneficiary_.safeTransferFrom(msg.sender, beneficiary_, amountToBeneficiary_);
// flush excess `tokenToBeneficiary` to `beneficiary`
excessToBeneficiary = ERC20(tokenToBeneficiary_).balanceOf(address(this));
if (excessToBeneficiary > 0) {
tokenToBeneficiary_.safeTransfer(beneficiary_, excessToBeneficiary);
}
}
}
}
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)
}
}
}
}
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;
}
}
LibQuotes.sol 42 lines
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.8.17;
import {ConvertedQuotePair, SortedConvertedQuotePair} from "./ConvertedQuotePair.sol";
using {_sort, _convert, _convertAndSort} for QuotePair global;
struct QuoteParams {
QuotePair quotePair;
uint128 baseAmount;
bytes data;
}
struct QuotePair {
address base;
address quote;
}
struct SortedQuotePair {
address token0;
address token1;
}
function _sort(QuotePair memory qp) pure returns (SortedQuotePair memory) {
return (qp.base > qp.quote)
? SortedQuotePair({token0: qp.quote, token1: qp.base})
: SortedQuotePair({token0: qp.base, token1: qp.quote});
}
function _convert(QuotePair calldata qp, function (address) internal view returns (address) convert)
view
returns (ConvertedQuotePair memory)
{
return ConvertedQuotePair({cBase: convert(qp.base), cQuote: convert(qp.quote)});
}
function _convertAndSort(QuotePair calldata qp, function (address) internal view returns (address) convert)
view
returns (SortedConvertedQuotePair memory)
{
return _convert(qp, convert)._sort();
}
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_);
}
}
SafeCastLib.sol 385 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;
/// @notice Safe integer casting library that reverts on overflow.
/// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/SafeCastLib.sol)
/// @author Modified from OpenZeppelin (https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/math/SafeCast.sol)
library SafeCastLib {
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* CUSTOM ERRORS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
error Overflow();
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* UNSIGNED INTEGER SAFE CASTING OPERATIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
function toUint8(uint256 x) internal pure returns (uint8) {
if (x >= 1 << 8) _revertOverflow();
return uint8(x);
}
function toUint16(uint256 x) internal pure returns (uint16) {
if (x >= 1 << 16) _revertOverflow();
return uint16(x);
}
function toUint24(uint256 x) internal pure returns (uint24) {
if (x >= 1 << 24) _revertOverflow();
return uint24(x);
}
function toUint32(uint256 x) internal pure returns (uint32) {
if (x >= 1 << 32) _revertOverflow();
return uint32(x);
}
function toUint40(uint256 x) internal pure returns (uint40) {
if (x >= 1 << 40) _revertOverflow();
return uint40(x);
}
function toUint48(uint256 x) internal pure returns (uint48) {
if (x >= 1 << 48) _revertOverflow();
return uint48(x);
}
function toUint56(uint256 x) internal pure returns (uint56) {
if (x >= 1 << 56) _revertOverflow();
return uint56(x);
}
function toUint64(uint256 x) internal pure returns (uint64) {
if (x >= 1 << 64) _revertOverflow();
return uint64(x);
}
function toUint72(uint256 x) internal pure returns (uint72) {
if (x >= 1 << 72) _revertOverflow();
return uint72(x);
}
function toUint80(uint256 x) internal pure returns (uint80) {
if (x >= 1 << 80) _revertOverflow();
return uint80(x);
}
function toUint88(uint256 x) internal pure returns (uint88) {
if (x >= 1 << 88) _revertOverflow();
return uint88(x);
}
function toUint96(uint256 x) internal pure returns (uint96) {
if (x >= 1 << 96) _revertOverflow();
return uint96(x);
}
function toUint104(uint256 x) internal pure returns (uint104) {
if (x >= 1 << 104) _revertOverflow();
return uint104(x);
}
function toUint112(uint256 x) internal pure returns (uint112) {
if (x >= 1 << 112) _revertOverflow();
return uint112(x);
}
function toUint120(uint256 x) internal pure returns (uint120) {
if (x >= 1 << 120) _revertOverflow();
return uint120(x);
}
function toUint128(uint256 x) internal pure returns (uint128) {
if (x >= 1 << 128) _revertOverflow();
return uint128(x);
}
function toUint136(uint256 x) internal pure returns (uint136) {
if (x >= 1 << 136) _revertOverflow();
return uint136(x);
}
function toUint144(uint256 x) internal pure returns (uint144) {
if (x >= 1 << 144) _revertOverflow();
return uint144(x);
}
function toUint152(uint256 x) internal pure returns (uint152) {
if (x >= 1 << 152) _revertOverflow();
return uint152(x);
}
function toUint160(uint256 x) internal pure returns (uint160) {
if (x >= 1 << 160) _revertOverflow();
return uint160(x);
}
function toUint168(uint256 x) internal pure returns (uint168) {
if (x >= 1 << 168) _revertOverflow();
return uint168(x);
}
function toUint176(uint256 x) internal pure returns (uint176) {
if (x >= 1 << 176) _revertOverflow();
return uint176(x);
}
function toUint184(uint256 x) internal pure returns (uint184) {
if (x >= 1 << 184) _revertOverflow();
return uint184(x);
}
function toUint192(uint256 x) internal pure returns (uint192) {
if (x >= 1 << 192) _revertOverflow();
return uint192(x);
}
function toUint200(uint256 x) internal pure returns (uint200) {
if (x >= 1 << 200) _revertOverflow();
return uint200(x);
}
function toUint208(uint256 x) internal pure returns (uint208) {
if (x >= 1 << 208) _revertOverflow();
return uint208(x);
}
function toUint216(uint256 x) internal pure returns (uint216) {
if (x >= 1 << 216) _revertOverflow();
return uint216(x);
}
function toUint224(uint256 x) internal pure returns (uint224) {
if (x >= 1 << 224) _revertOverflow();
return uint224(x);
}
function toUint232(uint256 x) internal pure returns (uint232) {
if (x >= 1 << 232) _revertOverflow();
return uint232(x);
}
function toUint240(uint256 x) internal pure returns (uint240) {
if (x >= 1 << 240) _revertOverflow();
return uint240(x);
}
function toUint248(uint256 x) internal pure returns (uint248) {
if (x >= 1 << 248) _revertOverflow();
return uint248(x);
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* SIGNED INTEGER SAFE CASTING OPERATIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
function toInt8(int256 x) internal pure returns (int8) {
int8 y = int8(x);
if (x != y) _revertOverflow();
return y;
}
function toInt16(int256 x) internal pure returns (int16) {
int16 y = int16(x);
if (x != y) _revertOverflow();
return y;
}
function toInt24(int256 x) internal pure returns (int24) {
int24 y = int24(x);
if (x != y) _revertOverflow();
return y;
}
function toInt32(int256 x) internal pure returns (int32) {
int32 y = int32(x);
if (x != y) _revertOverflow();
return y;
}
function toInt40(int256 x) internal pure returns (int40) {
int40 y = int40(x);
if (x != y) _revertOverflow();
return y;
}
function toInt48(int256 x) internal pure returns (int48) {
int48 y = int48(x);
if (x != y) _revertOverflow();
return y;
}
function toInt56(int256 x) internal pure returns (int56) {
int56 y = int56(x);
if (x != y) _revertOverflow();
return y;
}
function toInt64(int256 x) internal pure returns (int64) {
int64 y = int64(x);
if (x != y) _revertOverflow();
return y;
}
function toInt72(int256 x) internal pure returns (int72) {
int72 y = int72(x);
if (x != y) _revertOverflow();
return y;
}
function toInt80(int256 x) internal pure returns (int80) {
int80 y = int80(x);
if (x != y) _revertOverflow();
return y;
}
function toInt88(int256 x) internal pure returns (int88) {
int88 y = int88(x);
if (x != y) _revertOverflow();
return y;
}
function toInt96(int256 x) internal pure returns (int96) {
int96 y = int96(x);
if (x != y) _revertOverflow();
return y;
}
function toInt104(int256 x) internal pure returns (int104) {
int104 y = int104(x);
if (x != y) _revertOverflow();
return y;
}
function toInt112(int256 x) internal pure returns (int112) {
int112 y = int112(x);
if (x != y) _revertOverflow();
return y;
}
function toInt120(int256 x) internal pure returns (int120) {
int120 y = int120(x);
if (x != y) _revertOverflow();
return y;
}
function toInt128(int256 x) internal pure returns (int128) {
int128 y = int128(x);
if (x != y) _revertOverflow();
return y;
}
function toInt136(int256 x) internal pure returns (int136) {
int136 y = int136(x);
if (x != y) _revertOverflow();
return y;
}
function toInt144(int256 x) internal pure returns (int144) {
int144 y = int144(x);
if (x != y) _revertOverflow();
return y;
}
function toInt152(int256 x) internal pure returns (int152) {
int152 y = int152(x);
if (x != y) _revertOverflow();
return y;
}
function toInt160(int256 x) internal pure returns (int160) {
int160 y = int160(x);
if (x != y) _revertOverflow();
return y;
}
function toInt168(int256 x) internal pure returns (int168) {
int168 y = int168(x);
if (x != y) _revertOverflow();
return y;
}
function toInt176(int256 x) internal pure returns (int176) {
int176 y = int176(x);
if (x != y) _revertOverflow();
return y;
}
function toInt184(int256 x) internal pure returns (int184) {
int184 y = int184(x);
if (x != y) _revertOverflow();
return y;
}
function toInt192(int256 x) internal pure returns (int192) {
int192 y = int192(x);
if (x != y) _revertOverflow();
return y;
}
function toInt200(int256 x) internal pure returns (int200) {
int200 y = int200(x);
if (x != y) _revertOverflow();
return y;
}
function toInt208(int256 x) internal pure returns (int208) {
int208 y = int208(x);
if (x != y) _revertOverflow();
return y;
}
function toInt216(int256 x) internal pure returns (int216) {
int216 y = int216(x);
if (x != y) _revertOverflow();
return y;
}
function toInt224(int256 x) internal pure returns (int224) {
int224 y = int224(x);
if (x != y) _revertOverflow();
return y;
}
function toInt232(int256 x) internal pure returns (int232) {
int232 y = int232(x);
if (x != y) _revertOverflow();
return y;
}
function toInt240(int256 x) internal pure returns (int240) {
int240 y = int240(x);
if (x != y) _revertOverflow();
return y;
}
function toInt248(int256 x) internal pure returns (int248) {
int248 y = int248(x);
if (x != y) _revertOverflow();
return y;
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* UNSIGNED TO SIGNED SAFE CASTING OPERATIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
function toInt256(uint256 x) internal pure returns (int256) {
if (x >= 1 << 255) _revertOverflow();
return int256(x);
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* PRIVATE HELPERS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
function _revertOverflow() private pure {
/// @solidity memory-safe-assembly
assembly {
// Store the function selector of `Overflow()`.
mstore(0x00, 0x35278d12)
// Revert with (offset, size).
revert(0x1c, 0x04)
}
}
}
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;
}
}
AddressUtils.sol 14 lines
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.8.17;
address constant ADDRESS_ZERO = address(0);
library AddressUtils {
function _isEmpty(address addr) internal pure returns (bool) {
return (addr == ADDRESS_ZERO);
}
function _isNotEmpty(address addr) internal pure returns (bool) {
return (addr != ADDRESS_ZERO);
}
}
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;
}
}
SafeTransferLib.sol 353 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)) {
// For better gas estimation.
if iszero(gt(gas(), 1000000)) { revert(0, 0) }
}
}
}
}
/// @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)) {
// For better gas estimation.
if iszero(gt(gas(), 1000000)) { revert(0, 0) }
}
}
}
}
/// @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.
mstore(0x60, amount) // Store the `amount` argument.
mstore(0x40, to) // Store the `to` argument.
mstore(0x2c, shl(96, from)) // Store the `from` argument.
// Store the function selector of `transferFrom(address,address,uint256)`.
mstore(0x0c, 0x23b872dd000000000000000000000000)
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(0x40, to) // Store the `to` argument.
mstore(0x2c, shl(96, from)) // Store the `from` argument.
// Store the function selector of `balanceOf(address)`.
mstore(0x0c, 0x70a08231000000000000000000000000)
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)
// The `amount` argument is already written to the memory word at 0x6c.
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(0x14, to) // Store the `to` argument.
mstore(0x34, amount) // Store the `amount` argument.
// Store the function selector of `transfer(address,uint256)`.
mstore(0x00, 0xa9059cbb000000000000000000000000)
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, 0x10, 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.
mstore(0x34, 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, 0x34, 0x20)
)
) {
// Store the function selector of `TransferFailed()`.
mstore(0x00, 0x90b8ec18)
// Revert with (offset, size).
revert(0x1c, 0x04)
}
mstore(0x14, to) // Store the `to` argument.
// The `amount` argument is already written to the memory word at 0x34.
amount := mload(0x34)
// Store the function selector of `transfer(address,uint256)`.
mstore(0x00, 0xa9059cbb000000000000000000000000)
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, 0x10, 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.
mstore(0x34, 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(0x14, to) // Store the `to` argument.
mstore(0x34, amount) // Store the `amount` argument.
// Store the function selector of `approve(address,uint256)`.
mstore(0x00, 0x095ea7b3000000000000000000000000)
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, 0x10, 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.
mstore(0x34, 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(0x14, account) // Store the `account` argument.
// Store the function selector of `balanceOf(address)`.
mstore(0x00, 0x70a08231000000000000000000000000)
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, 0x10, 0x24, 0x20, 0x20)
)
)
}
}
}
ISwapperFlashCallback.sol 20 lines
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.8.17;
/// @title Swapper Flash Callback
/// @author 0xSplits
/// @notice Callback for `Swapper#flash`
/// @dev any contract that calls `Swapper#flash` must implement this interface.
/// Inspired by IUniswapV3FlashCallback
interface ISwapperFlashCallback {
/// Called to `msg.sender` in `Swapper#flash` after transferring `quoteParams`.
/// @dev In the implementation you must complete the flash swap.
/// If `tokenToBeneficiary` is ETH, you must deposit `amountToBeneficiary` via `Swapper#payback`.
/// If `tokenToBeneficiary` is an ERC20, you must use approve Swapper to transfer `amountToBeneficiary`.
/// The caller of this method will use token = address(0) to refer to ETH.
/// @param tokenToBeneficiary The token due to the `beneficiary` by the end of `#flash`
/// @param amountToBeneficiary The amount of `tokenToBeneficiary` due to the `beneficiary` by the end of `#flash`
/// @param data Any `data` passed through by `msg.sender` of `Swapper#flash`
function swapperFlashCallback(address tokenToBeneficiary, uint256 amountToBeneficiary, bytes calldata data)
external;
}
PairScaledOfferFactors.sol 52 lines
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.8.17;
import {QuotePair, SortedQuotePair} from "splits-utils/LibQuotes.sol";
import {SwapperImpl} from "../SwapperImpl.sol";
/// @title PairScaledOfferFactors Library
/// @author 0xSplits
/// @notice Setters & getters for quote pairs' scaledOfferFactors
library PairScaledOfferFactors {
/// set pairs' scaled offer factors
function _set(
mapping(address => mapping(address => uint32)) storage self,
SwapperImpl.SetPairScaledOfferFactorParams[] calldata params_
) internal {
uint256 length = params_.length;
for (uint256 i; i < length;) {
_set(self, params_[i]);
unchecked {
++i;
}
}
}
/// set pair's scaled offer factor
function _set(
mapping(address => mapping(address => uint32)) storage self,
SwapperImpl.SetPairScaledOfferFactorParams calldata params_
) internal {
SortedQuotePair memory sqp = params_.quotePair._sort();
self[sqp.token0][sqp.token1] = params_.scaledOfferFactor;
}
/// get pair's scaled offer factor
function _get(mapping(address => mapping(address => uint32)) storage self, QuotePair calldata quotePair_)
internal
view
returns (uint32)
{
return _get(self, quotePair_._sort());
}
/// get pair's scaled offer factor
function _get(mapping(address => mapping(address => uint32)) storage self, SortedQuotePair memory sqp_)
internal
view
returns (uint32)
{
return self[sqp_.token0][sqp_.token1];
}
}
ConvertedQuotePair.sol 20 lines
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.8.17;
using {_sort} for ConvertedQuotePair global;
struct ConvertedQuotePair {
address cBase;
address cQuote;
}
struct SortedConvertedQuotePair {
address cToken0;
address cToken1;
}
function _sort(ConvertedQuotePair memory cqp) pure returns (SortedConvertedQuotePair memory) {
return (cqp.cBase > cqp.cQuote)
? SortedConvertedQuotePair({cToken0: cqp.cQuote, cToken1: cqp.cBase})
: SortedConvertedQuotePair({cToken0: cqp.cBase, cToken1: cqp.cQuote});
}
IOracle.sol 10 lines
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.8.17;
import {QuoteParams} from "splits-utils/LibQuotes.sol";
/// @title Oracle Interface
/// @author 0xSplits
interface IOracle {
function getQuoteAmounts(QuoteParams[] calldata quoteParams_) external view returns (uint256[] memory);
}
OracleParams.sol 31 lines
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.8.17;
import {AddressUtils} from "splits-utils/AddressUtils.sol";
import {IOracle} from "../interfaces/IOracle.sol";
import {IOracleFactory} from "../interfaces/IOracleFactory.sol";
using {_parseIntoOracle} for OracleParams global;
using AddressUtils for address;
struct OracleParams {
IOracle oracle;
CreateOracleParams createOracleParams;
}
struct CreateOracleParams {
IOracleFactory factory;
bytes data;
}
function _parseIntoOracle(OracleParams calldata oracleParams_) returns (IOracle) {
if (address(oracleParams_.oracle)._isNotEmpty()) {
return oracleParams_.oracle;
} else {
// if oracle not provided, create one with provided params
CreateOracleParams calldata createOracleParams = oracleParams_.createOracleParams;
return createOracleParams.factory.createOracle(createOracleParams.data);
}
}
IOracleFactory.sol 9 lines
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.8.17;
import {IOracle} from "./IOracle.sol";
/// @title Oracle factory interface
interface IOracleFactory {
function createOracle(bytes calldata data_) external returns (IOracle);
}
IWETH9.sol 13 lines
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.8.17;
/// @title Interface for WETH9
interface IWETH9 {
/// @notice Deposit ether to get wrapped ether
function deposit() external payable;
/// @notice Withdraw wrapped ether to get ether
function withdraw(uint256) external;
function balanceOf(address) external view returns (uint256);
}
ISwapRouter.sol 67 lines
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.7.5;
pragma abicoder v2;
import '@uniswap/v3-core/contracts/interfaces/callback/IUniswapV3SwapCallback.sol';
/// @title Router token swapping functionality
/// @notice Functions for swapping tokens via Uniswap V3
interface ISwapRouter is IUniswapV3SwapCallback {
struct ExactInputSingleParams {
address tokenIn;
address tokenOut;
uint24 fee;
address recipient;
uint256 deadline;
uint256 amountIn;
uint256 amountOutMinimum;
uint160 sqrtPriceLimitX96;
}
/// @notice Swaps `amountIn` of one token for as much as possible of another token
/// @param params The parameters necessary for the swap, encoded as `ExactInputSingleParams` in calldata
/// @return amountOut The amount of the received token
function exactInputSingle(ExactInputSingleParams calldata params) external payable returns (uint256 amountOut);
struct ExactInputParams {
bytes path;
address recipient;
uint256 deadline;
uint256 amountIn;
uint256 amountOutMinimum;
}
/// @notice Swaps `amountIn` of one token for as much as possible of another along the specified path
/// @param params The parameters necessary for the multi-hop swap, encoded as `ExactInputParams` in calldata
/// @return amountOut The amount of the received token
function exactInput(ExactInputParams calldata params) external payable returns (uint256 amountOut);
struct ExactOutputSingleParams {
address tokenIn;
address tokenOut;
uint24 fee;
address recipient;
uint256 deadline;
uint256 amountOut;
uint256 amountInMaximum;
uint160 sqrtPriceLimitX96;
}
/// @notice Swaps as little as possible of one token for `amountOut` of another token
/// @param params The parameters necessary for the swap, encoded as `ExactOutputSingleParams` in calldata
/// @return amountIn The amount of the input token
function exactOutputSingle(ExactOutputSingleParams calldata params) external payable returns (uint256 amountIn);
struct ExactOutputParams {
bytes path;
address recipient;
uint256 deadline;
uint256 amountOut;
uint256 amountInMaximum;
}
/// @notice Swaps as little as possible of one token for `amountOut` of another along the specified path (reversed)
/// @param params The parameters necessary for the multi-hop swap, encoded as `ExactOutputParams` in calldata
/// @return amountIn The amount of the input token
function exactOutput(ExactOutputParams calldata params) external payable returns (uint256 amountIn);
}
IUniswapV3SwapCallback.sol 21 lines
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.5.0;
/// @title Callback for IUniswapV3PoolActions#swap
/// @notice Any contract that calls IUniswapV3PoolActions#swap must implement this interface
interface IUniswapV3SwapCallback {
/// @notice Called to `msg.sender` after executing a swap via IUniswapV3Pool#swap.
/// @dev In the implementation you must pay the pool tokens owed for the swap.
/// The caller of this method must be checked to be a UniswapV3Pool deployed by the canonical UniswapV3Factory.
/// amount0Delta and amount1Delta can both be 0 if no tokens were swapped.
/// @param amount0Delta The amount of token0 that was sent (negative) or must be received (positive) by the pool by
/// the end of the swap. If positive, the callback must send that amount of token0 to the pool.
/// @param amount1Delta The amount of token1 that was sent (negative) or must be received (positive) by the pool by
/// the end of the swap. If positive, the callback must send that amount of token1 to the pool.
/// @param data Any data passed through by the caller via the IUniswapV3PoolActions#swap call
function uniswapV3SwapCallback(
int256 amount0Delta,
int256 amount1Delta,
bytes calldata data
) external;
}
Read Contract
swapRouter 0xc31c9c07 → address
swapperFactory 0xd122d54a → address
weth9 0x50879c1c → address
Write Contract 2 functions
These functions modify contract state and require a wallet transaction to execute.
initFlash 0x98662bea
address swapper
tuple params_
swapperFlashCallback 0x2602cbb7
address tokenToBeneficiary_
uint256 amountToBeneficiary_
bytes data_
Recent Transactions
No transactions found for this address