编译器
0.8.17+commit.8df45f5f
文件 1 的 11:AutoSniper.sol
pragma solidity ^0.8.17;
import "./helpers/SniperStructs.sol";
import "./helpers/IWETH.sol";
import "./helpers/IPunk.sol";
import "openzeppelin/contracts/access/Ownable.sol";
import "openzeppelin/contracts/token/ERC721/IERC721.sol";
import "openzeppelin/contracts/token/ERC20/IERC20.sol";
import "openzeppelin/contracts/token/ERC1155/IERC1155.sol";
error InsufficientBalance();
error FailedToWithdraw();
error MaxTipExceeded();
error MarketplaceNotAllowed();
error TokenContractNotAllowed();
error OrderFailed();
error TipBelowMinimum();
error CallerNotFulfiller();
contract AutoSniper is Ownable {
event SnipeSuccessful(address nftContractAddress, uint256 tokenId, address sniper);
address private FULFILLER_ADDRESS;
uint256 public minimumTip = 0.005 ether;
mapping(address => bool) public allowedMarketplaces;
mapping(address => uint256) public sniperBalances;
mapping(address => SniperGuardrails) public sniperGuardrails;
constructor(address _fulfiller) {
FULFILLER_ADDRESS = _fulfiller;
}
function fulfillOrder(SniperOrder calldata order, uint256 wethAmount) external onlyFulfiller {
if (wethAmount > 0) _swapWeth(wethAmount, order.to);
_checkSniperGuardrails(order.tokenAddress, order.marketplace, order.tip, order.to);
uint256 totalValue = order.value + order.tip;
if (!allowedMarketplaces[order.marketplace]) revert MarketplaceNotAllowed();
if (sniperBalances[order.to] < totalValue) revert InsufficientBalance();
uint256 balanceBefore = address(this).balance;
(bool transferred, ) = payable(FULFILLER_ADDRESS).call{value: order.tip}("");
if (!transferred) revert FailedToWithdraw();
(bool success,) = order.marketplace.call{value: order.value}(order.data);
if (!success) revert OrderFailed();
uint256 balanceAfter = address(this).balance;
uint256 spent = balanceBefore - balanceAfter;
unchecked { sniperBalances[order.to] -= spent; }
_transferNftToSniper(order.tokenType, order.tokenAddress, order.tokenId, order.to);
emit SnipeSuccessful(order.tokenAddress, order.tokenId, order.to);
}
function deposit(address sniper) public payable {
sniperBalances[sniper] += msg.value;
}
function depositSelf() external payable {
deposit(msg.sender);
}
function withdraw(uint256 amount) external {
if (sniperBalances[msg.sender] < amount) revert InsufficientBalance();
unchecked { sniperBalances[msg.sender] -= amount; }
(bool success, ) = payable(msg.sender).call{value: amount}("");
if (!success) revert FailedToWithdraw();
}
function setUserAllowedMarketplaces(bool guardEnabled, bool marketplaceAllowed, address[] calldata marketplaces) external {
sniperGuardrails[msg.sender].marketplaceGuardEnabled = guardEnabled;
for (uint256 i = 0; i < marketplaces.length;) {
sniperGuardrails[msg.sender].allowedMarketplaces[marketplaces[i]] = marketplaceAllowed;
unchecked { ++i; }
}
}
function setUserMaxTip(uint256 maxTipInWei) external {
if (maxTipInWei < minimumTip && maxTipInWei != 0) revert TipBelowMinimum();
sniperGuardrails[msg.sender].maxTip = maxTipInWei;
}
function setUserAllowedNfts(bool guardEnabled, bool nftAllowed, address[] calldata nfts) external {
sniperGuardrails[msg.sender].nftContractGuardEnabled = guardEnabled;
for (uint256 i = 0; i < nfts.length;) {
sniperGuardrails[msg.sender].allowedNftContracts[nfts[i]] = nftAllowed;
unchecked { ++i; }
}
}
function configureMarket(address marketplace, bool status) external onlyOwner {
allowedMarketplaces[marketplace] = status;
}
function setFulfillerAddress(address _fulfiller) external onlyOwner {
FULFILLER_ADDRESS = _fulfiller;
}
function setMinimumTip(uint256 tip) external onlyOwner {
minimumTip = tip;
}
function marketplaceApprovedBySniper(address sniper, address marketplace) external view returns (bool) {
return sniperGuardrails[sniper].allowedMarketplaces[marketplace];
}
function nftContractApprovedBySniper(address sniper, address nftContract) external view returns (bool) {
return sniperGuardrails[sniper].allowedNftContracts[nftContract];
}
function _swapWeth(uint256 wethAmount, address sniper) private onlyFulfiller {
IWETH weth = IWETH(0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2);
weth.transferFrom(sniper, address(this), wethAmount);
weth.withdraw(wethAmount);
unchecked { sniperBalances[sniper] += wethAmount; }
}
function _transferNftToSniper(ItemType tokenType, address tokenAddress, uint256 tokenId, address sniper) private {
if (tokenType == ItemType.ERC721) {
IERC721(tokenAddress).transferFrom(address(this), sniper, tokenId);
} else if (tokenType == ItemType.ERC1155) {
IERC1155(tokenAddress).safeTransferFrom(address(this), sniper, tokenId, 1, "");
} else if (tokenType == ItemType.CRYPTOPUNKS) {
IPunk(tokenAddress).transferPunk(sniper, tokenId);
}
}
function _checkSniperGuardrails(address tokenAddress, address marketplace, uint256 tip, address sniper) private view {
SniperGuardrails storage guardrails = sniperGuardrails[sniper];
if (guardrails.maxTip > 0 && tip > guardrails.maxTip) revert MaxTipExceeded();
if (guardrails.marketplaceGuardEnabled && !guardrails.allowedMarketplaces[marketplace]) revert MarketplaceNotAllowed();
if (guardrails.nftContractGuardEnabled && !guardrails.allowedNftContracts[tokenAddress]) revert TokenContractNotAllowed();
}
function onERC1155Received(
address,
address,
uint256,
uint256,
bytes calldata
) public virtual returns (bytes4) {
return this.onERC1155Received.selector;
}
function onERC1155BatchReceived(
address,
address,
uint256[] calldata,
uint256[] calldata,
bytes calldata
) public virtual returns (bytes4) {
return this.onERC1155BatchReceived.selector;
}
function onERC721Received(
address,
address,
uint256,
bytes calldata
) external virtual returns (bytes4) {
return 0x150b7a02;
}
function onERC721Received(
address,
uint256,
bytes calldata
) external virtual returns (bytes4) {
return 0xf0b9e5ba;
}
function supportsInterface(bytes4 interfaceId)
external
virtual
view
returns (bool)
{
return interfaceId == this.supportsInterface.selector;
}
receive() external payable {}
function rescueERC20(address asset, address recipient) onlyOwner external {
IERC20 token = IERC20(asset);
token.transfer(recipient, token.balanceOf(address(this)));
}
function rescueERC721(address asset, uint256[] calldata ids, address recipient) onlyOwner external {
for (uint256 i = 0; i < ids.length; i++) {
IERC721(asset).transferFrom(address(this), recipient, ids[i]);
}
}
function rescueERC1155(address asset, uint256[] calldata ids, uint256[] calldata amounts, address recipient) onlyOwner external {
for (uint256 i = 0; i < ids.length; i++) {
IERC1155(asset).safeTransferFrom(address(this), recipient, ids[i], amounts[i], "");
}
}
modifier onlyFulfiller() {
if (msg.sender != FULFILLER_ADDRESS) revert CallerNotFulfiller();
_;
}
}
文件 2 的 11:Context.sol
pragma solidity ^0.8.0;
abstract contract Context {
function _msgSender() internal view virtual returns (address) {
return msg.sender;
}
function _msgData() internal view virtual returns (bytes calldata) {
return msg.data;
}
}
文件 3 的 11:IERC1155.sol
pragma solidity ^0.8.0;
import "../../utils/introspection/IERC165.sol";
interface IERC1155 is IERC165 {
event TransferSingle(address indexed operator, address indexed from, address indexed to, uint256 id, uint256 value);
event TransferBatch(
address indexed operator,
address indexed from,
address indexed to,
uint256[] ids,
uint256[] values
);
event ApprovalForAll(address indexed account, address indexed operator, bool approved);
event URI(string value, uint256 indexed id);
function balanceOf(address account, uint256 id) external view returns (uint256);
function balanceOfBatch(address[] calldata accounts, uint256[] calldata ids)
external
view
returns (uint256[] memory);
function setApprovalForAll(address operator, bool approved) external;
function isApprovedForAll(address account, address operator) external view returns (bool);
function safeTransferFrom(
address from,
address to,
uint256 id,
uint256 amount,
bytes calldata data
) external;
function safeBatchTransferFrom(
address from,
address to,
uint256[] calldata ids,
uint256[] calldata amounts,
bytes calldata data
) external;
}
文件 4 的 11:IERC165.sol
pragma solidity ^0.8.0;
interface IERC165 {
function supportsInterface(bytes4 interfaceId) external view returns (bool);
}
文件 5 的 11:IERC20.sol
pragma solidity ^0.8.0;
interface IERC20 {
event Transfer(address indexed from, address indexed to, uint256 value);
event Approval(address indexed owner, address indexed spender, uint256 value);
function totalSupply() external view returns (uint256);
function balanceOf(address account) external view returns (uint256);
function transfer(address to, uint256 amount) external returns (bool);
function allowance(address owner, address spender) external view returns (uint256);
function approve(address spender, uint256 amount) external returns (bool);
function transferFrom(
address from,
address to,
uint256 amount
) external returns (bool);
}
文件 6 的 11:IERC721.sol
pragma solidity ^0.8.0;
import "../../utils/introspection/IERC165.sol";
interface IERC721 is IERC165 {
event Transfer(address indexed from, address indexed to, uint256 indexed tokenId);
event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId);
event ApprovalForAll(address indexed owner, address indexed operator, bool approved);
function balanceOf(address owner) external view returns (uint256 balance);
function ownerOf(uint256 tokenId) external view returns (address owner);
function safeTransferFrom(
address from,
address to,
uint256 tokenId,
bytes calldata data
) external;
function safeTransferFrom(
address from,
address to,
uint256 tokenId
) external;
function transferFrom(
address from,
address to,
uint256 tokenId
) external;
function approve(address to, uint256 tokenId) external;
function setApprovalForAll(address operator, bool _approved) external;
function getApproved(uint256 tokenId) external view returns (address operator);
function isApprovedForAll(address owner, address operator) external view returns (bool);
}
文件 7 的 11:IPunk.sol
pragma solidity 0.8.17;
interface IPunk {
function transferPunk(address to, uint punkIndex) external;
}
文件 8 的 11:IWETH.sol
pragma solidity 0.8.17;
interface IWETH {
function transferFrom(address src, address dst, uint wad) external;
function deposit() external payable;
function withdraw(uint wad) external;
function balanceOf(address user) external view returns (uint256);
function approve(address guy, uint wad) external returns (bool);
}
文件 9 的 11:Ownable.sol
pragma solidity ^0.8.0;
import "../utils/Context.sol";
abstract contract Ownable is Context {
address private _owner;
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
constructor() {
_transferOwnership(_msgSender());
}
modifier onlyOwner() {
_checkOwner();
_;
}
function owner() public view virtual returns (address) {
return _owner;
}
function _checkOwner() internal view virtual {
require(owner() == _msgSender(), "Ownable: caller is not the owner");
}
function renounceOwnership() public virtual onlyOwner {
_transferOwnership(address(0));
}
function transferOwnership(address newOwner) public virtual onlyOwner {
require(newOwner != address(0), "Ownable: new owner is the zero address");
_transferOwnership(newOwner);
}
function _transferOwnership(address newOwner) internal virtual {
address oldOwner = _owner;
_owner = newOwner;
emit OwnershipTransferred(oldOwner, newOwner);
}
}
文件 10 的 11:SniperEnums.sol
pragma solidity 0.8.17;
enum ItemType {
NATIVE,
ERC20,
ERC721,
ERC1155,
ERC721_WITH_CRITERIA,
ERC1155_WITH_CRITERIA,
CRYPTOPUNKS
}
文件 11 的 11:SniperStructs.sol
pragma solidity 0.8.17;
import "./SniperEnums.sol";
struct SniperOrder {
address to;
address marketplace;
uint256 value;
uint256 tip;
ItemType tokenType;
bytes data;
address tokenAddress;
uint256 tokenId;
}
struct SniperGuardrails {
bool marketplaceGuardEnabled;
bool nftContractGuardEnabled;
mapping(address => bool) allowedMarketplaces;
mapping(address => bool) allowedNftContracts;
uint256 maxTip;
}
{
"compilationTarget": {
"src/AutoSniper.sol": "AutoSniper"
},
"evmVersion": "london",
"libraries": {},
"metadata": {
"bytecodeHash": "ipfs"
},
"optimizer": {
"enabled": true,
"runs": 200
},
"remappings": [
":ERC721A/=lib/ERC721A/",
":ds-test/=lib/forge-std/lib/ds-test/src/",
":forge-std/=lib/forge-std/src/",
":openzeppelin/=lib/openzeppelin-contracts/",
":solmate/=lib/solmate/"
]
}
[{"inputs":[{"internalType":"address","name":"_fulfiller","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"CallerNotFulfiller","type":"error"},{"inputs":[],"name":"FailedToWithdraw","type":"error"},{"inputs":[],"name":"InsufficientBalance","type":"error"},{"inputs":[],"name":"MarketplaceNotAllowed","type":"error"},{"inputs":[],"name":"MaxTipExceeded","type":"error"},{"inputs":[],"name":"OrderFailed","type":"error"},{"inputs":[],"name":"TipBelowMinimum","type":"error"},{"inputs":[],"name":"TokenContractNotAllowed","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"nftContractAddress","type":"address"},{"indexed":false,"internalType":"uint256","name":"tokenId","type":"uint256"},{"indexed":false,"internalType":"address","name":"sniper","type":"address"}],"name":"SnipeSuccessful","type":"event"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"allowedMarketplaces","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"marketplace","type":"address"},{"internalType":"bool","name":"status","type":"bool"}],"name":"configureMarket","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"sniper","type":"address"}],"name":"deposit","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"depositSelf","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"to","type":"address"},{"internalType":"address","name":"marketplace","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"},{"internalType":"uint256","name":"tip","type":"uint256"},{"internalType":"enum ItemType","name":"tokenType","type":"uint8"},{"internalType":"bytes","name":"data","type":"bytes"},{"internalType":"address","name":"tokenAddress","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"internalType":"struct SniperOrder","name":"order","type":"tuple"},{"internalType":"uint256","name":"wethAmount","type":"uint256"}],"name":"fulfillOrder","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"sniper","type":"address"},{"internalType":"address","name":"marketplace","type":"address"}],"name":"marketplaceApprovedBySniper","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"minimumTip","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"sniper","type":"address"},{"internalType":"address","name":"nftContract","type":"address"}],"name":"nftContractApprovedBySniper","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256[]","name":"","type":"uint256[]"},{"internalType":"uint256[]","name":"","type":"uint256[]"},{"internalType":"bytes","name":"","type":"bytes"}],"name":"onERC1155BatchReceived","outputs":[{"internalType":"bytes4","name":"","type":"bytes4"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bytes","name":"","type":"bytes"}],"name":"onERC1155Received","outputs":[{"internalType":"bytes4","name":"","type":"bytes4"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bytes","name":"","type":"bytes"}],"name":"onERC721Received","outputs":[{"internalType":"bytes4","name":"","type":"bytes4"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bytes","name":"","type":"bytes"}],"name":"onERC721Received","outputs":[{"internalType":"bytes4","name":"","type":"bytes4"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"asset","type":"address"},{"internalType":"uint256[]","name":"ids","type":"uint256[]"},{"internalType":"uint256[]","name":"amounts","type":"uint256[]"},{"internalType":"address","name":"recipient","type":"address"}],"name":"rescueERC1155","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"asset","type":"address"},{"internalType":"address","name":"recipient","type":"address"}],"name":"rescueERC20","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"asset","type":"address"},{"internalType":"uint256[]","name":"ids","type":"uint256[]"},{"internalType":"address","name":"recipient","type":"address"}],"name":"rescueERC721","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_fulfiller","type":"address"}],"name":"setFulfillerAddress","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"tip","type":"uint256"}],"name":"setMinimumTip","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bool","name":"guardEnabled","type":"bool"},{"internalType":"bool","name":"marketplaceAllowed","type":"bool"},{"internalType":"address[]","name":"marketplaces","type":"address[]"}],"name":"setUserAllowedMarketplaces","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bool","name":"guardEnabled","type":"bool"},{"internalType":"bool","name":"nftAllowed","type":"bool"},{"internalType":"address[]","name":"nfts","type":"address[]"}],"name":"setUserAllowedNfts","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"maxTipInWei","type":"uint256"}],"name":"setUserMaxTip","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"sniperBalances","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"sniperGuardrails","outputs":[{"internalType":"bool","name":"marketplaceGuardEnabled","type":"bool"},{"internalType":"bool","name":"nftContractGuardEnabled","type":"bool"},{"internalType":"uint256","name":"maxTip","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes4","name":"interfaceId","type":"bytes4"}],"name":"supportsInterface","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"withdraw","outputs":[],"stateMutability":"nonpayable","type":"function"},{"stateMutability":"payable","type":"receive"}]