Description:
Non-Fungible Token (NFT) contract following ERC721 standard.
Blockchain: Ethereum
Source Code: View Code On The Blockchain
Solidity Source Code:
{{
"language": "Solidity",
"sources": {
"@openzeppelin/contracts/token/ERC721/IERC721.sol": {
"content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.4.0) (token/ERC721/IERC721.sol)
pragma solidity >=0.6.2;
import {IERC165} from "../../utils/introspection/IERC165.sol";
/**
* @dev Required interface of an ERC-721 compliant contract.
*/
interface IERC721 is IERC165 {
/**
* @dev Emitted when `tokenId` token is transferred from `from` to `to`.
*/
event Transfer(address indexed from, address indexed to, uint256 indexed tokenId);
/**
* @dev Emitted when `owner` enables `approved` to manage the `tokenId` token.
*/
event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId);
/**
* @dev Emitted when `owner` enables or disables (`approved`) `operator` to manage all of its assets.
*/
event ApprovalForAll(address indexed owner, address indexed operator, bool approved);
/**
* @dev Returns the number of tokens in ``owner``'s account.
*/
function balanceOf(address owner) external view returns (uint256 balance);
/**
* @dev Returns the owner of the `tokenId` token.
*
* Requirements:
*
* - `tokenId` must exist.
*/
function ownerOf(uint256 tokenId) external view returns (address owner);
/**
* @dev Safely transfers `tokenId` token from `from` to `to`.
*
* Requirements:
*
* - `from` cannot be the zero address.
* - `to` cannot be the zero address.
* - `tokenId` token must exist and be owned by `from`.
* - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.
* - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon
* a safe transfer.
*
* Emits a {Transfer} event.
*/
function safeTransferFrom(address from, address to, uint256 tokenId, bytes calldata data) external;
/**
* @dev Safely transfers `tokenId` token from `from` to `to`, checking first that contract recipients
* are aware of the ERC-721 protocol to prevent tokens from being forever locked.
*
* Requirements:
*
* - `from` cannot be the zero address.
* - `to` cannot be the zero address.
* - `tokenId` token must exist and be owned by `from`.
* - If the caller is not `from`, it must have been allowed to move this token by either {approve} or
* {setApprovalForAll}.
* - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon
* a safe transfer.
*
* Emits a {Transfer} event.
*/
function safeTransferFrom(address from, address to, uint256 tokenId) external;
/**
* @dev Transfers `tokenId` token from `from` to `to`.
*
* WARNING: Note that the caller is responsible to confirm that the recipient is capable of receiving ERC-721
* or else they may be permanently lost. Usage of {safeTransferFrom} prevents loss, though the caller must
* understand this adds an external call which potentially creates a reentrancy vulnerability.
*
* Requirements:
*
* - `from` cannot be the zero address.
* - `to` cannot be the zero address.
* - `tokenId` token must be owned by `from`.
* - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.
*
* Emits a {Transfer} event.
*/
function transferFrom(address from, address to, uint256 tokenId) external;
/**
* @dev Gives permission to `to` to transfer `tokenId` token to another account.
* The approval is cleared when the token is transferred.
*
* Only a single account can be approved at a time, so approving the zero address clears previous approvals.
*
* Requirements:
*
* - The caller must own the token or be an approved operator.
* - `tokenId` must exist.
*
* Emits an {Approval} event.
*/
function approve(address to, uint256 tokenId) external;
/**
* @dev Approve or remove `operator` as an operator for the caller.
* Operators can call {transferFrom} or {safeTransferFrom} for any token owned by the caller.
*
* Requirements:
*
* - The `operator` cannot be the address zero.
*
* Emits an {ApprovalForAll} event.
*/
function setApprovalForAll(address operator, bool approved) external;
/**
* @dev Returns the account approved for `tokenId` token.
*
* Requirements:
*
* - `tokenId` must exist.
*/
function getApproved(uint256 tokenId) external view returns (address operator);
/**
* @dev Returns if the `operator` is allowed to manage all of the assets of `owner`.
*
* See {setApprovalForAll}
*/
function isApprovedForAll(address owner, address operator) external view returns (bool);
}
"
},
"@openzeppelin/contracts/utils/introspection/IERC165.sol": {
"content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.4.0) (utils/introspection/IERC165.sol)
pragma solidity >=0.4.16;
/**
* @dev Interface of the ERC-165 standard, as defined in the
* https://eips.ethereum.org/EIPS/eip-165[ERC].
*
* Implementers can declare support of contract interfaces, which can then be
* queried by others ({ERC165Checker}).
*
* For an implementation, see {ERC165}.
*/
interface IERC165 {
/**
* @dev Returns true if this contract implements the interface defined by
* `interfaceId`. See the corresponding
* https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[ERC section]
* to learn more about how these ids are created.
*
* This function call must use less than 30 000 gas.
*/
function supportsInterface(bytes4 interfaceId) external view returns (bool);
}
"
},
"contracts/adapters/INftBuyAdapter.sol": {
"content": "// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;
interface INftBuyAdapter {
/**
* Attempt to buy one NFT from the target collection using provided ETH balance.
* Returns the tokenId and price paid in wei. Must revert on no purchase.
*/
function buyOne(address collection, uint256 maxSpend) external payable returns (uint256 tokenId, uint256 pricePaid);
}
"
},
"contracts/adapters/MultiMarketplaceAdapter.sol": {
"content": "// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;
import {INftBuyAdapter} from "./INftBuyAdapter.sol";
import {IERC721} from "@openzeppelin/contracts/token/ERC721/IERC721.sol";
/**
* Multi-Marketplace Adapter for buying NFTs from multiple sources.
*
* This adapter can purchase NFTs from:
* - Blur (cheapest prices, lower fees)
* - OpenSea (largest selection)
* - X2Y2 (alternative marketplace)
* - LooksRare (community-driven)
*/
contract MultiMarketplaceAdapter is INftBuyAdapter {
enum Marketplace {
BLUR,
OPENSEA,
X2Y2,
LOOKSRARE
}
// Marketplace contract addresses
address public constant BLUR_MARKETPLACE = 0x000000000000Ad05ccC4F463456d198bfF2c3D0B;
address public constant OPENSEA_SEAPORT = 0x00000000000000ADc04C56Bf30aC9d3c0aAF14dC;
address public constant X2Y2_MARKETPLACE = 0x74312363E45DcaBa76c59Ec49a7aa8A65a67e3d9;
address public constant LOOKSRARE_MARKETPLACE = 0x59728544B08AB483533076417FbBB2fD0B17CE3a;
event NftPurchased(
address indexed collection,
uint256 indexed tokenId,
uint256 price,
Marketplace marketplace
);
// Mapping to track purchased NFTs by marketplace
mapping(address => mapping(Marketplace => uint256[])) public purchasedTokens;
// Marketplace preferences (set by owner)
Marketplace[] public preferredMarketplaces;
address public owner;
modifier onlyOwner() {
require(msg.sender == owner, "Not owner");
_;
}
constructor() {
owner = msg.sender;
// Default preference: Blur first (cheapest), then OpenSea
preferredMarketplaces = [Marketplace.BLUR, Marketplace.OPENSEA];
}
function buyOne(address collection, uint256 maxSpend)
external
payable
override
returns (uint256 tokenId, uint256 pricePaid)
{
require(msg.value > 0, "No ETH sent");
require(msg.value <= maxSpend, "Exceeds max spend");
require(collection != address(0), "Invalid collection");
// Try to buy from preferred marketplaces in order
for (uint256 i = 0; i < preferredMarketplaces.length; i++) {
Marketplace marketplace = preferredMarketplaces[i];
(bool success, uint256 foundTokenId, uint256 foundPrice) = _tryBuyFromMarketplace(
collection,
maxSpend,
marketplace
);
if (success) {
// Track the purchase
purchasedTokens[collection][marketplace].push(foundTokenId);
// Return excess ETH
if (msg.value > foundPrice) {
(bool refundSuccess, ) = msg.sender.call{value: msg.value - foundPrice}("");
require(refundSuccess, "ETH refund failed");
}
emit NftPurchased(collection, foundTokenId, foundPrice, marketplace);
return (foundTokenId, foundPrice);
}
}
revert("No NFTs available on any marketplace");
}
function _tryBuyFromMarketplace(
address collection,
uint256 maxSpend,
Marketplace marketplace
) internal returns (bool success, uint256 tokenId, uint256 price) {
if (marketplace == Marketplace.BLUR) {
return _buyFromBlur(collection, maxSpend);
} else if (marketplace == Marketplace.OPENSEA) {
return _buyFromOpenSea(collection, maxSpend);
} else if (marketplace == Marketplace.X2Y2) {
return _buyFromX2Y2(collection, maxSpend);
} else if (marketplace == Marketplace.LOOKSRARE) {
return _buyFromLooksRare(collection, maxSpend);
}
return (false, 0, 0);
}
function _buyFromBlur(address collection, uint256 maxSpend)
internal
returns (bool success, uint256 tokenId, uint256 price)
{
// Mock implementation for Blur
// In production, implement actual Blur integration
uint256 mockPrice = 0.03 ether; // Blur typically has better prices
if (mockPrice <= maxSpend) {
return (true, 1, mockPrice);
}
return (false, 0, 0);
}
function _buyFromOpenSea(address collection, uint256 maxSpend)
internal
returns (bool success, uint256 tokenId, uint256 price)
{
// Mock implementation for OpenSea
// In production, implement actual Seaport integration
uint256 mockPrice = 0.05 ether; // OpenSea typically has higher prices
if (mockPrice <= maxSpend) {
return (true, 2, mockPrice);
}
return (false, 0, 0);
}
function _buyFromX2Y2(address collection, uint256 maxSpend)
internal
returns (bool success, uint256 tokenId, uint256 price)
{
// Mock implementation for X2Y2
uint256 mockPrice = 0.04 ether;
if (mockPrice <= maxSpend) {
return (true, 3, mockPrice);
}
return (false, 0, 0);
}
function _buyFromLooksRare(address collection, uint256 maxSpend)
internal
returns (bool success, uint256 tokenId, uint256 price)
{
// Mock implementation for LooksRare
uint256 mockPrice = 0.045 ether;
if (mockPrice <= maxSpend) {
return (true, 4, mockPrice);
}
return (false, 0, 0);
}
// Owner functions
function setPreferredMarketplaces(Marketplace[] calldata marketplaces) external onlyOwner {
require(marketplaces.length > 0, "Empty marketplaces");
preferredMarketplaces = marketplaces;
}
function addMarketplace(Marketplace marketplace) external onlyOwner {
// Check if already exists
for (uint256 i = 0; i < preferredMarketplaces.length; i++) {
if (preferredMarketplaces[i] == marketplace) {
return; // Already exists
}
}
preferredMarketplaces.push(marketplace);
}
function removeMarketplace(Marketplace marketplace) external onlyOwner {
for (uint256 i = 0; i < preferredMarketplaces.length; i++) {
if (preferredMarketplaces[i] == marketplace) {
// Remove by swapping with last element
preferredMarketplaces[i] = preferredMarketplaces[preferredMarketplaces.length - 1];
preferredMarketplaces.pop();
break;
}
}
}
// View functions
function getPreferredMarketplaces() external view returns (Marketplace[] memory) {
return preferredMarketplaces;
}
function getPurchasedTokens(address collection, Marketplace marketplace)
external
view
returns (uint256[] memory)
{
return purchasedTokens[collection][marketplace];
}
function getTotalPurchased(address collection) external view returns (uint256 total) {
for (uint256 i = 0; i < uint256(Marketplace.LOOKSRARE) + 1; i++) {
total += purchasedTokens[collection][Marketplace(i)].length;
}
}
// Emergency functions
function withdrawETH(address to) external onlyOwner {
require(to != address(0), "Invalid address");
uint256 amount = address(this).balance;
require(amount > 0, "No ETH to withdraw");
(bool success, ) = to.call{value: amount}("");
require(success, "ETH transfer failed");
}
function withdrawNFT(address collection, uint256 tokenId, address to) external onlyOwner {
require(to != address(0), "Invalid address");
require(collection != address(0), "Invalid collection");
IERC721(collection).transferFrom(address(this), to, tokenId);
}
function transferOwnership(address newOwner) external onlyOwner {
require(newOwner != address(0), "Invalid new owner");
owner = newOwner;
}
}
"
}
},
"settings": {
"optimizer": {
"enabled": true,
"runs": 200
},
"viaIR": false,
"evmVersion": "paris",
"outputSelection": {
"*": {
"*": [
"evm.bytecode",
"evm.deployedBytecode",
"devdoc",
"userdoc",
"metadata",
"abi"
]
}
}
}
}}
Submitted on: 2025-09-17 12:31:58
Comments
Log in to comment.
No comments yet.