More Info
Private Name Tags
ContractCreator
Latest 25 internal transactions (View All)
Advanced mode:
Parent Transaction Hash | Block | From | To | |||
---|---|---|---|---|---|---|
19355356 | 240 days ago | 0.11049932 ETH | ||||
19355356 | 240 days ago | 2.07738723 ETH | ||||
19354976 | 240 days ago | 0.01641622 ETH | ||||
19354976 | 240 days ago | 0.30862495 ETH | ||||
19353577 | 240 days ago | 0.03332653 ETH | ||||
19353577 | 240 days ago | 0.6265389 ETH | ||||
19352288 | 240 days ago | 0.0340032 ETH | ||||
19352288 | 240 days ago | 0.63926017 ETH | ||||
19352015 | 240 days ago | 0.05231803 ETH | ||||
19352015 | 240 days ago | 0.98357909 ETH | ||||
19348930 | 241 days ago | 0.01780173 ETH | ||||
19348930 | 241 days ago | 0.33467267 ETH | ||||
19348365 | 241 days ago | 0.03616183 ETH | ||||
19348365 | 241 days ago | 0.6798424 ETH | ||||
19347168 | 241 days ago | 0.0369273 ETH | ||||
19347168 | 241 days ago | 0.69423328 ETH | ||||
19346123 | 241 days ago | 0.01875885 ETH | ||||
19346123 | 241 days ago | 0.35266649 ETH | ||||
19324698 | 244 days ago | 0.09181632 ETH | ||||
19324698 | 244 days ago | 1.94650605 ETH | ||||
19296559 | 248 days ago | 0.09179178 ETH | ||||
19296559 | 248 days ago | 1.72568546 ETH | ||||
19274787 | 251 days ago | 0.01894954 ETH | ||||
19274787 | 251 days ago | 0.35625139 ETH | ||||
19273311 | 251 days ago | 0.07786342 ETH |
Loading...
Loading
Minimal Proxy Contract for 0x8c5c3a33393631e90b63e9ae5abc2f4003ff0468
Contract Name:
FloorPairNFTAndETH
Compiler Version
v0.8.17+commit.8df45f5f
Optimization Enabled:
Yes with 1000 runs
Other Settings:
default evmVersion
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: UNLICENSED pragma solidity ^0.8.17; import {FloorPairNFT} from "./FloorPairNFT.sol"; import {FloorPairETH} from "./FloorPairETH.sol"; contract FloorPairNFTAndETH is FloorPairNFT, FloorPairETH { }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.8.0) (token/ERC721/IERC721.sol) pragma solidity ^0.8.0; import "../../utils/introspection/IERC165.sol"; /** * @dev Required interface of an ERC721 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 ERC721 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 ERC721 * 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 caller. * * 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); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (utils/introspection/IERC165.sol) pragma solidity ^0.8.0; /** * @dev Interface of the ERC165 standard, as defined in the * https://eips.ethereum.org/EIPS/eip-165[EIP]. * * 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[EIP 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); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.8.0) (utils/structs/EnumerableSet.sol) // This file was procedurally generated from scripts/generate/templates/EnumerableSet.js. pragma solidity ^0.8.0; /** * @dev Library for managing * https://en.wikipedia.org/wiki/Set_(abstract_data_type)[sets] of primitive * types. * * Sets have the following properties: * * - Elements are added, removed, and checked for existence in constant time * (O(1)). * - Elements are enumerated in O(n). No guarantees are made on the ordering. * * ``` * contract Example { * // Add the library methods * using EnumerableSet for EnumerableSet.AddressSet; * * // Declare a set state variable * EnumerableSet.AddressSet private mySet; * } * ``` * * As of v3.3.0, sets of type `bytes32` (`Bytes32Set`), `address` (`AddressSet`) * and `uint256` (`UintSet`) are supported. * * [WARNING] * ==== * Trying to delete such a structure from storage will likely result in data corruption, rendering the structure * unusable. * See https://github.com/ethereum/solidity/pull/11843[ethereum/solidity#11843] for more info. * * In order to clean an EnumerableSet, you can either remove all elements one by one or create a fresh instance using an * array of EnumerableSet. * ==== */ library EnumerableSet { // To implement this library for multiple types with as little code // repetition as possible, we write it in terms of a generic Set type with // bytes32 values. // The Set implementation uses private functions, and user-facing // implementations (such as AddressSet) are just wrappers around the // underlying Set. // This means that we can only create new EnumerableSets for types that fit // in bytes32. struct Set { // Storage of set values bytes32[] _values; // Position of the value in the `values` array, plus 1 because index 0 // means a value is not in the set. mapping(bytes32 => uint256) _indexes; } /** * @dev Add a value to a set. O(1). * * Returns true if the value was added to the set, that is if it was not * already present. */ function _add(Set storage set, bytes32 value) private returns (bool) { if (!_contains(set, value)) { set._values.push(value); // The value is stored at length-1, but we add 1 to all indexes // and use 0 as a sentinel value set._indexes[value] = set._values.length; return true; } else { return false; } } /** * @dev Removes a value from a set. O(1). * * Returns true if the value was removed from the set, that is if it was * present. */ function _remove(Set storage set, bytes32 value) private returns (bool) { // We read and store the value's index to prevent multiple reads from the same storage slot uint256 valueIndex = set._indexes[value]; if (valueIndex != 0) { // Equivalent to contains(set, value) // To delete an element from the _values array in O(1), we swap the element to delete with the last one in // the array, and then remove the last element (sometimes called as 'swap and pop'). // This modifies the order of the array, as noted in {at}. uint256 toDeleteIndex = valueIndex - 1; uint256 lastIndex = set._values.length - 1; if (lastIndex != toDeleteIndex) { bytes32 lastValue = set._values[lastIndex]; // Move the last value to the index where the value to delete is set._values[toDeleteIndex] = lastValue; // Update the index for the moved value set._indexes[lastValue] = valueIndex; // Replace lastValue's index to valueIndex } // Delete the slot where the moved value was stored set._values.pop(); // Delete the index for the deleted slot delete set._indexes[value]; return true; } else { return false; } } /** * @dev Returns true if the value is in the set. O(1). */ function _contains(Set storage set, bytes32 value) private view returns (bool) { return set._indexes[value] != 0; } /** * @dev Returns the number of values on the set. O(1). */ function _length(Set storage set) private view returns (uint256) { return set._values.length; } /** * @dev Returns the value stored at position `index` in the set. O(1). * * Note that there are no guarantees on the ordering of values inside the * array, and it may change when more values are added or removed. * * Requirements: * * - `index` must be strictly less than {length}. */ function _at(Set storage set, uint256 index) private view returns (bytes32) { return set._values[index]; } /** * @dev Return the entire set in an array * * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that * this function has an unbounded cost, and using it as part of a state-changing function may render the function * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block. */ function _values(Set storage set) private view returns (bytes32[] memory) { return set._values; } // Bytes32Set struct Bytes32Set { Set _inner; } /** * @dev Add a value to a set. O(1). * * Returns true if the value was added to the set, that is if it was not * already present. */ function add(Bytes32Set storage set, bytes32 value) internal returns (bool) { return _add(set._inner, value); } /** * @dev Removes a value from a set. O(1). * * Returns true if the value was removed from the set, that is if it was * present. */ function remove(Bytes32Set storage set, bytes32 value) internal returns (bool) { return _remove(set._inner, value); } /** * @dev Returns true if the value is in the set. O(1). */ function contains(Bytes32Set storage set, bytes32 value) internal view returns (bool) { return _contains(set._inner, value); } /** * @dev Returns the number of values in the set. O(1). */ function length(Bytes32Set storage set) internal view returns (uint256) { return _length(set._inner); } /** * @dev Returns the value stored at position `index` in the set. O(1). * * Note that there are no guarantees on the ordering of values inside the * array, and it may change when more values are added or removed. * * Requirements: * * - `index` must be strictly less than {length}. */ function at(Bytes32Set storage set, uint256 index) internal view returns (bytes32) { return _at(set._inner, index); } /** * @dev Return the entire set in an array * * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that * this function has an unbounded cost, and using it as part of a state-changing function may render the function * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block. */ function values(Bytes32Set storage set) internal view returns (bytes32[] memory) { bytes32[] memory store = _values(set._inner); bytes32[] memory result; /// @solidity memory-safe-assembly assembly { result := store } return result; } // AddressSet struct AddressSet { Set _inner; } /** * @dev Add a value to a set. O(1). * * Returns true if the value was added to the set, that is if it was not * already present. */ function add(AddressSet storage set, address value) internal returns (bool) { return _add(set._inner, bytes32(uint256(uint160(value)))); } /** * @dev Removes a value from a set. O(1). * * Returns true if the value was removed from the set, that is if it was * present. */ function remove(AddressSet storage set, address value) internal returns (bool) { return _remove(set._inner, bytes32(uint256(uint160(value)))); } /** * @dev Returns true if the value is in the set. O(1). */ function contains(AddressSet storage set, address value) internal view returns (bool) { return _contains(set._inner, bytes32(uint256(uint160(value)))); } /** * @dev Returns the number of values in the set. O(1). */ function length(AddressSet storage set) internal view returns (uint256) { return _length(set._inner); } /** * @dev Returns the value stored at position `index` in the set. O(1). * * Note that there are no guarantees on the ordering of values inside the * array, and it may change when more values are added or removed. * * Requirements: * * - `index` must be strictly less than {length}. */ function at(AddressSet storage set, uint256 index) internal view returns (address) { return address(uint160(uint256(_at(set._inner, index)))); } /** * @dev Return the entire set in an array * * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that * this function has an unbounded cost, and using it as part of a state-changing function may render the function * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block. */ function values(AddressSet storage set) internal view returns (address[] memory) { bytes32[] memory store = _values(set._inner); address[] memory result; /// @solidity memory-safe-assembly assembly { result := store } return result; } // UintSet struct UintSet { Set _inner; } /** * @dev Add a value to a set. O(1). * * Returns true if the value was added to the set, that is if it was not * already present. */ function add(UintSet storage set, uint256 value) internal returns (bool) { return _add(set._inner, bytes32(value)); } /** * @dev Removes a value from a set. O(1). * * Returns true if the value was removed from the set, that is if it was * present. */ function remove(UintSet storage set, uint256 value) internal returns (bool) { return _remove(set._inner, bytes32(value)); } /** * @dev Returns true if the value is in the set. O(1). */ function contains(UintSet storage set, uint256 value) internal view returns (bool) { return _contains(set._inner, bytes32(value)); } /** * @dev Returns the number of values in the set. O(1). */ function length(UintSet storage set) internal view returns (uint256) { return _length(set._inner); } /** * @dev Returns the value stored at position `index` in the set. O(1). * * Note that there are no guarantees on the ordering of values inside the * array, and it may change when more values are added or removed. * * Requirements: * * - `index` must be strictly less than {length}. */ function at(UintSet storage set, uint256 index) internal view returns (uint256) { return uint256(_at(set._inner, index)); } /** * @dev Return the entire set in an array * * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that * this function has an unbounded cost, and using it as part of a state-changing function may render the function * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block. */ function values(UintSet storage set) internal view returns (uint256[] memory) { bytes32[] memory store = _values(set._inner); uint256[] memory result; /// @solidity memory-safe-assembly assembly { result := store } return result; } }
// SPDX-License-Identifier: UNLICENSED pragma solidity ^0.8.17; import "./interface/ICurve.sol"; import "./interface/IRouter.sol"; import "@openzeppelin/contracts/token/ERC721/IERC721.sol"; import "@openzeppelin/contracts/utils/structs/EnumerableSet.sol"; import "solmate/src/utils/SafeTransferLib.sol"; import "solmate/src/tokens/ERC20.sol"; abstract contract FloorPair { using EnumerableSet for EnumerableSet.UintSet; bool private initialized; //Router contract IRouter public router; uint8 public curveType; IERC721 public nft; ERC20 public erc20; uint256 public floorTokenId; // The current token reserve uint256 public tokenReserve; // nft list EnumerableSet.UintSet internal nftIdSet; //virtual token reserve uint256 public virtualTokenReserve; //virtual nft reserve uint256 public virtualNftReserve; // bonding curve delta uint128 public delta; // trade pool feeMultiplier uint96 public feeMultiplier; // trade pool accumulative fee uint256 public feeAccumulation; event NFTWithdraw(uint256 count); event NFTDeposit(uint256 count); event TokenWithdraw(uint256 amount); event TokenDeposit(uint256 amount); event SwapNFTToToken(address to, uint256[] nftIds, uint256 tradeFee, uint256 protocolFee, uint256 outputAmount); event SwapTokenTONFT(address to, uint256[] nftIds, uint256 tradeFee, uint256 protocolFee, uint256 inputAmount); event WithdrawFee(uint256 amount); modifier onlyRouter() { require(address(router) == msg.sender, "Invalid operator"); _; } function initialize( IRouter _router, uint8 _bondingCurve, IERC721 _nft, uint256 _floorTokenId, uint96 _feeMultiplier, uint128 _delta, uint256 _tokenReserve, uint256 _nftReserve ) external { require(!initialized, "pair is already initialized"); router = _router; curveType = _bondingCurve; nft = _nft; floorTokenId = _floorTokenId; feeMultiplier = _feeMultiplier; delta = _delta; virtualTokenReserve = _tokenReserve * _delta - _tokenReserve; virtualNftReserve = _nftReserve * _delta + 1 - _nftReserve; initialized = true; } function setFeeMultiplier(uint96 _feeMultiplier) external onlyRouter { feeMultiplier = _feeMultiplier; } /** * Swap */ function swapNFTsForToken(address to, uint256[] calldata nftIds) external virtual onlyRouter returns (uint256 outputAmount) { // Call bonding curve for pricing information uint256 protocolFee; uint256 tradeFee; (tradeFee, protocolFee, outputAmount) = _calculateSellInfoAndUpdatePoolParams( nftIds.length ); //get nfts from escrow _getSwapNFT(nftIds); //Send eth to recipient _sendTokenOutput(to, outputAmount); //Pay protocol fee _payProtocolFee(protocolFee); emit SwapNFTToToken(to, nftIds, tradeFee, protocolFee, outputAmount); } function swapTokenForSpecificNFTs(address to, uint256[] calldata nftIds) external virtual payable onlyRouter returns (uint256 inputAmount) { return _swapTokenForNFTs(to, nftIds); } function swapTokenForAnyNFTs(address to, uint256 count) external virtual payable onlyRouter returns (uint256 inputAmount) { return (_swapTokenForNFTs(to, getSwapAnyNFTIds(count))); } function _swapTokenForNFTs(address to, uint256[] memory nftIds) internal returns (uint256 inputAmount) { // Call bonding curve for pricing information uint256 protocolFee; uint256 tradeFee; (tradeFee, protocolFee, inputAmount) = _calculateBuyInfoAndUpdatePoolParams( nftIds.length ); //get token from escrow _getSwapToken(inputAmount); //Send nft to recipient _sendSpecificNFTsToRecipient(to, nftIds); //Pay protocol fee _payProtocolFee(protocolFee); emit SwapTokenTONFT(to, nftIds, tradeFee, protocolFee, inputAmount); } /** @notice Calculates the amount needed to be sent by the pair for a sell and adjusts spot price or delta if necessary @param numNFTs The amount of NFTs to send to the the pair @return tradeFee The amount of tokens to send as trade fee @return protocolFee The amount of tokens to send as protocol fee @return outputValue The amount of tokens total tokens receive */ function _calculateSellInfoAndUpdatePoolParams(uint256 numNFTs) internal returns ( uint256 tradeFee, uint256 protocolFee, uint256 outputValue ) { (outputValue, tradeFee, protocolFee) = calculateSellInfo(numNFTs); uint256 actualOutput = outputValue + tradeFee + protocolFee; //update tokenReserve tokenReserve = tokenReserve + tradeFee - actualOutput; //update virtualTokenReserve virtualTokenReserve = virtualTokenReserve + tradeFee - actualOutput; //update virtualNftReserve virtualNftReserve += numNFTs; //update feeAccumulation feeAccumulation += tradeFee; } /** @notice Calculates the amount needed to be sent into the pair for a buy and adjusts spot price or delta if necessary @param numNFTs The amount of NFTs to purchase from the pair @return tradeFee The amount of tokens to send as trade fee @return protocolFee The amount of tokens to send as protocol fee @return inputValue The amount of tokens total tokens receive */ function _calculateBuyInfoAndUpdatePoolParams(uint256 numNFTs) internal returns ( uint256 tradeFee, uint256 protocolFee, uint256 inputValue ) { (inputValue, tradeFee, protocolFee) = calculateBuyInfo(numNFTs); uint256 actualInput = inputValue - tradeFee - protocolFee; //update tokenReserve tokenReserve = tokenReserve + actualInput + tradeFee; //update virtualTokenReserve virtualTokenReserve = virtualTokenReserve + actualInput + tradeFee; //update virtualNftReserve virtualNftReserve -= numNFTs; //update feeAccumulation feeAccumulation += tradeFee; } function calculateSellInfo(uint256 numNFTs) public view returns(uint256 outputValue, uint256 tradeFee, uint256 protocolFee) { (outputValue, tradeFee, protocolFee) = ICurve( router.getCurveBonding(curveType) ).getSellInfo( virtualNftReserve, virtualTokenReserve, getHeldNFTCount(), tokenReserve, numNFTs, feeMultiplier, router.getProtocolFeeMultiplier() ); } function calculateBuyInfo(uint256 numNFTs) public view returns(uint256 inputValue, uint256 tradeFee, uint256 protocolFee) { (inputValue, tradeFee, protocolFee) = ICurve( router.getCurveBonding(curveType) ).getBuyInfo( virtualNftReserve, virtualTokenReserve, getHeldNFTCount(), tokenReserve, numNFTs, feeMultiplier, router.getProtocolFeeMultiplier() ); } function getParam() public view returns ( uint256 _floorTokenId, address _tokenAddress, address _nftAddress, uint256 _tokenReserve, uint256[] memory _nftIdSet, uint256 _virtualTokenReserve, uint256 _virtualNftReserve, uint128 _delta, uint96 _feeMultiplier, uint256 _feeAccumulation) { _floorTokenId = floorTokenId; _tokenAddress = address(erc20); _nftAddress = address(nft); _tokenReserve = tokenReserve; _nftIdSet = nftIdSet.values(); _virtualTokenReserve = virtualTokenReserve; _virtualNftReserve = virtualNftReserve; _delta = delta; _feeMultiplier = feeMultiplier; _feeAccumulation = feeAccumulation; } function getSwapAnyNFTIds(uint256 count) public view returns (uint256[] memory outputNft) { require(count <= nftIdSet.length(), "insufficient nft"); outputNft = new uint256[](count); for (uint i = 0; i < count; i++) { outputNft[i] = nftIdSet.at(i); } } /*////////////////////////////////////////////////////////////// nft functions //////////////////////////////////////////////////////////////*/ function getAllHeldNFTIds() public view virtual returns (uint256[] memory); function getHeldNFTCount() public view virtual returns (uint256); function _sendSpecificNFTsToRecipient(address to, uint256[] memory nftIds) internal virtual; function _getSwapNFT(uint256[] calldata nftIds) internal virtual; function depositNFT(uint256[] calldata nftIds) public virtual; /*////////////////////////////////////////////////////////////// token functions //////////////////////////////////////////////////////////////*/ function _sendTokenOutput(address to, uint256 outputAmount) internal virtual; function _payProtocolFee(uint256 protocolFee) internal virtual; function _getSwapToken(uint256 amount) internal virtual; function depositToken(uint256 amount) public payable virtual; function approveRouter(uint256[] calldata nftIds) external virtual; /*////////////////////////////////////////////////////////////// Owner functions //////////////////////////////////////////////////////////////*/ function withdrawToken(address to, uint256 amount) external virtual; function withdrawERC721(address to, uint256[] calldata nftIds, uint256 virtualCount) external virtual; function withdrawFee(address to) external virtual; }
// SPDX-License-Identifier: UNLICENSED pragma solidity ^0.8.17; import {FloorPair} from "./FloorPair.sol"; import {SafeTransferLib} from "solmate/src/utils/SafeTransferLib.sol"; abstract contract FloorPairETH is FloorPair { using SafeTransferLib for address payable; // @inheritdoc FloorPair function _sendTokenOutput(address to, uint256 outputAmount) internal override { if (outputAmount > 0) { payable(to).safeTransferETH(outputAmount); } } // @inheritdoc FloorPair function _payProtocolFee(uint256 protocolFee) internal override { if (protocolFee > 0) { payable(router.getProtocolFeeRecipient()).safeTransferETH(protocolFee); } } function _getSwapToken(uint256 inputAmount) internal override { require(inputAmount == msg.value, "invalid amount"); } /// @inheritdoc FloorPair function depositToken(uint256 amount) public override payable onlyRouter { require(amount == msg.value, "invalid amount"); tokenReserve += msg.value; virtualTokenReserve += msg.value; emit TokenDeposit(msg.value); } /// @inheritdoc FloorPair function withdrawToken(address owner, uint256 amount) external override onlyRouter { require(amount <= virtualTokenReserve, "invalid amount, too large"); uint256 claim = amount < tokenReserve ? amount : tokenReserve; payable(owner).safeTransferETH(claim); tokenReserve -= claim; virtualTokenReserve -= amount; // emit event since ETH is the pair token emit TokenWithdraw(amount); } /// @inheritdoc FloorPair function withdrawFee(address owner) external override onlyRouter { uint256 claim = feeAccumulation > tokenReserve ? tokenReserve : feeAccumulation; if (claim > 0) { payable(owner).safeTransferETH(claim); feeAccumulation -= claim; tokenReserve -= claim; virtualTokenReserve -= claim; } emit WithdrawFee(claim); } receive() external payable { } }
// SPDX-License-Identifier: UNLICENSED pragma solidity ^0.8.17; import "./FloorPair.sol"; import "@openzeppelin/contracts/utils/structs/EnumerableSet.sol"; abstract contract FloorPairNFT is FloorPair { using EnumerableSet for EnumerableSet.UintSet; function getAllHeldNFTIds() public view override returns (uint256[] memory) { return nftIdSet.values(); } function getHeldNFTCount() public view override returns (uint256) { return nftIdSet.length(); } function _getSwapNFT(uint256[] calldata nftIds) internal override { uint256 len = nftIds.length; for (uint256 i; i < len; ) { require(nft.ownerOf(nftIds[i]) == address(this), "not received the NFT"); nftIdSet.add(nftIds[i]); unchecked { ++i; } } } function depositNFT(uint256[] calldata nftIds) public override onlyRouter { uint256 len = nftIds.length; for (uint256 i; i < len; ) { require(nft.ownerOf(nftIds[i]) == address(this), "not received the NFT"); nftIdSet.add(nftIds[i]); unchecked { ++i; } } virtualNftReserve += len; emit NFTDeposit(len); } function _sendSpecificNFTsToRecipient( address to, uint256[] memory nftIds ) internal virtual override { uint256 len = nftIds.length; for (uint256 i; i < len; ) { nftIdSet.remove(nftIds[i]); nft.safeTransferFrom(address(this), to, nftIds[i]); unchecked { ++i; } } } function withdrawERC721(address to, uint256[] calldata nftIds, uint256 virtualCount) external override onlyRouter { uint256 len = nftIds.length; require(len + virtualCount <= virtualNftReserve, "invalid amount, too large"); for (uint256 i; i < len; ) { nftIdSet.remove(nftIds[i]); nft.safeTransferFrom(address(this), to, nftIds[i]); unchecked { ++i; } } virtualNftReserve -= len + virtualCount; emit NFTWithdraw(len); } function approveRouter(uint256[] calldata nftIds) external override onlyRouter { uint256 len = nftIds.length; for (uint256 i; i < len; ) { nft.approve(address(router), nftIds[i]); unchecked { ++i; } } } function onERC721Received( address, address, uint256, bytes memory ) public virtual returns (bytes4) { return this.onERC721Received.selector; } }
// SPDX-License-Identifier: UNLICENSED pragma solidity ^0.8.17; interface ICurve { /** Given the current state of the pool, computes how much the user should pay to purchase these NFT from the pool, the new spot price, and other values. @param virtualNftReserve virtual nft reserve @param virtualTokenReserve virtual token reserve @param nftReserve actual nft reserve @param tokenReserve actual token reserve @param numItems The number of NFTs the user is buying from the pair @param feeMultiplier Determines how much fee the LP takes from this trade, 18 decimals @param protocolFeeMultiplier Determines how much fee the protocol takes from this trade, 18 decimals @return inputValue The amount that the user should pay @return tradeFee The amount of tradeFee @return protocolFee The amount of fee to send to the protocol */ function getBuyInfo( uint256 virtualNftReserve, uint256 virtualTokenReserve, uint256 nftReserve, uint256 tokenReserve, uint256 numItems, uint256 feeMultiplier, uint256 protocolFeeMultiplier ) external view returns ( uint256 inputValue, uint256 tradeFee, uint256 protocolFee ); /** Given the current state of the pair and the trade, computes how much the user should receive when selling NFTs to the pair, the new spot price, and other values. @param virtualNftReserve virtual nft reserve @param virtualTokenReserve virtual token reserve @param nftReserve actual nft reserve @param tokenReserve actual token reserve @param numItems The number of NFTs the user is selling to the pair @param feeMultiplier Determines how much fee the LP takes from this trade @param protocolFeeMultiplier Determines how much fee the protocol takes from this trade @return outputValue The amount that the user will receive @return tradeFee The amount of tradeFee @return protocolFee The amount of fee to send to the protocol */ function getSellInfo( uint256 virtualTokenReserve, uint256 virtualNftReserve, uint256 nftReserve, uint256 tokenReserve, uint256 numItems, uint256 feeMultiplier, uint256 protocolFeeMultiplier ) external view returns ( uint256 outputValue, uint256 tradeFee, uint256 protocolFee ); }
// SPDX-License-Identifier: UNLICENSED pragma solidity ^0.8.17; interface IRouter { function getProtocolFeeMultiplier() external view returns (uint256); function getProtocolFeeRecipient() external view returns (address); function getCurveBonding(uint8 curveType) external view returns (address); }
// 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); } }
// SPDX-License-Identifier: AGPL-3.0-only pragma solidity >=0.8.0; import {ERC20} from "../tokens/ERC20.sol"; /// @notice Safe ETH and ERC20 transfer library that gracefully handles missing return values. /// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/SafeTransferLib.sol) /// @dev Use with caution! Some functions in this library knowingly create dirty bits at the destination of the free memory pointer. /// @dev Note that none of the functions in this library check that a token has code at all! That responsibility is delegated to the caller. library SafeTransferLib { /*////////////////////////////////////////////////////////////// ETH OPERATIONS //////////////////////////////////////////////////////////////*/ function safeTransferETH(address to, uint256 amount) internal { bool success; assembly { // Transfer the ETH and store if it succeeded or not. success := call(gas(), to, amount, 0, 0, 0, 0) } require(success, "ETH_TRANSFER_FAILED"); } /*////////////////////////////////////////////////////////////// ERC20 OPERATIONS //////////////////////////////////////////////////////////////*/ function safeTransferFrom( ERC20 token, address from, address to, uint256 amount ) internal { bool success; assembly { // Get a pointer to some free memory. let freeMemoryPointer := mload(0x40) // Write the abi-encoded calldata into memory, beginning with the function selector. mstore(freeMemoryPointer, 0x23b872dd00000000000000000000000000000000000000000000000000000000) mstore(add(freeMemoryPointer, 4), from) // Append the "from" argument. mstore(add(freeMemoryPointer, 36), to) // Append the "to" argument. mstore(add(freeMemoryPointer, 68), amount) // Append the "amount" argument. success := and( // 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(and(eq(mload(0), 1), gt(returndatasize(), 31)), iszero(returndatasize())), // We use 100 because the length of our calldata totals up like so: 4 + 32 * 3. // We use 0 and 32 to copy up to 32 bytes of return data into the scratch space. // Counterintuitively, this call must be positioned second to the or() call in the // surrounding and() call or else returndatasize() will be zero during the computation. call(gas(), token, 0, freeMemoryPointer, 100, 0, 32) ) } require(success, "TRANSFER_FROM_FAILED"); } function safeTransfer( ERC20 token, address to, uint256 amount ) internal { bool success; assembly { // Get a pointer to some free memory. let freeMemoryPointer := mload(0x40) // Write the abi-encoded calldata into memory, beginning with the function selector. mstore(freeMemoryPointer, 0xa9059cbb00000000000000000000000000000000000000000000000000000000) mstore(add(freeMemoryPointer, 4), to) // Append the "to" argument. mstore(add(freeMemoryPointer, 36), amount) // Append the "amount" argument. success := and( // 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(and(eq(mload(0), 1), gt(returndatasize(), 31)), iszero(returndatasize())), // We use 68 because the length of our calldata totals up like so: 4 + 32 * 2. // We use 0 and 32 to copy up to 32 bytes of return data into the scratch space. // Counterintuitively, this call must be positioned second to the or() call in the // surrounding and() call or else returndatasize() will be zero during the computation. call(gas(), token, 0, freeMemoryPointer, 68, 0, 32) ) } require(success, "TRANSFER_FAILED"); } function safeApprove( ERC20 token, address to, uint256 amount ) internal { bool success; assembly { // Get a pointer to some free memory. let freeMemoryPointer := mload(0x40) // Write the abi-encoded calldata into memory, beginning with the function selector. mstore(freeMemoryPointer, 0x095ea7b300000000000000000000000000000000000000000000000000000000) mstore(add(freeMemoryPointer, 4), to) // Append the "to" argument. mstore(add(freeMemoryPointer, 36), amount) // Append the "amount" argument. success := and( // 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(and(eq(mload(0), 1), gt(returndatasize(), 31)), iszero(returndatasize())), // We use 68 because the length of our calldata totals up like so: 4 + 32 * 2. // We use 0 and 32 to copy up to 32 bytes of return data into the scratch space. // Counterintuitively, this call must be positioned second to the or() call in the // surrounding and() call or else returndatasize() will be zero during the computation. call(gas(), token, 0, freeMemoryPointer, 68, 0, 32) ) } require(success, "APPROVE_FAILED"); } }
{ "optimizer": { "enabled": true, "runs": 1000 }, "viaIR": true, "outputSelection": { "*": { "*": [ "evm.bytecode", "evm.deployedBytecode", "devdoc", "userdoc", "metadata", "abi" ] } }, "libraries": {} }
[{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"count","type":"uint256"}],"name":"NFTDeposit","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"count","type":"uint256"}],"name":"NFTWithdraw","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256[]","name":"nftIds","type":"uint256[]"},{"indexed":false,"internalType":"uint256","name":"tradeFee","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"protocolFee","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"outputAmount","type":"uint256"}],"name":"SwapNFTToToken","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256[]","name":"nftIds","type":"uint256[]"},{"indexed":false,"internalType":"uint256","name":"tradeFee","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"protocolFee","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"inputAmount","type":"uint256"}],"name":"SwapTokenTONFT","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"TokenDeposit","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"TokenWithdraw","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"WithdrawFee","type":"event"},{"inputs":[{"internalType":"uint256[]","name":"nftIds","type":"uint256[]"}],"name":"approveRouter","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"numNFTs","type":"uint256"}],"name":"calculateBuyInfo","outputs":[{"internalType":"uint256","name":"inputValue","type":"uint256"},{"internalType":"uint256","name":"tradeFee","type":"uint256"},{"internalType":"uint256","name":"protocolFee","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"numNFTs","type":"uint256"}],"name":"calculateSellInfo","outputs":[{"internalType":"uint256","name":"outputValue","type":"uint256"},{"internalType":"uint256","name":"tradeFee","type":"uint256"},{"internalType":"uint256","name":"protocolFee","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"curveType","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"delta","outputs":[{"internalType":"uint128","name":"","type":"uint128"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"nftIds","type":"uint256[]"}],"name":"depositNFT","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"depositToken","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"erc20","outputs":[{"internalType":"contract ERC20","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"feeAccumulation","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"feeMultiplier","outputs":[{"internalType":"uint96","name":"","type":"uint96"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"floorTokenId","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getAllHeldNFTIds","outputs":[{"internalType":"uint256[]","name":"","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getHeldNFTCount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getParam","outputs":[{"internalType":"uint256","name":"_floorTokenId","type":"uint256"},{"internalType":"address","name":"_tokenAddress","type":"address"},{"internalType":"address","name":"_nftAddress","type":"address"},{"internalType":"uint256","name":"_tokenReserve","type":"uint256"},{"internalType":"uint256[]","name":"_nftIdSet","type":"uint256[]"},{"internalType":"uint256","name":"_virtualTokenReserve","type":"uint256"},{"internalType":"uint256","name":"_virtualNftReserve","type":"uint256"},{"internalType":"uint128","name":"_delta","type":"uint128"},{"internalType":"uint96","name":"_feeMultiplier","type":"uint96"},{"internalType":"uint256","name":"_feeAccumulation","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"count","type":"uint256"}],"name":"getSwapAnyNFTIds","outputs":[{"internalType":"uint256[]","name":"outputNft","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract IRouter","name":"_router","type":"address"},{"internalType":"uint8","name":"_bondingCurve","type":"uint8"},{"internalType":"contract IERC721","name":"_nft","type":"address"},{"internalType":"uint256","name":"_floorTokenId","type":"uint256"},{"internalType":"uint96","name":"_feeMultiplier","type":"uint96"},{"internalType":"uint128","name":"_delta","type":"uint128"},{"internalType":"uint256","name":"_tokenReserve","type":"uint256"},{"internalType":"uint256","name":"_nftReserve","type":"uint256"}],"name":"initialize","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"nft","outputs":[{"internalType":"contract IERC721","name":"","type":"address"}],"stateMutability":"view","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":[],"name":"router","outputs":[{"internalType":"contract IRouter","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint96","name":"_feeMultiplier","type":"uint96"}],"name":"setFeeMultiplier","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256[]","name":"nftIds","type":"uint256[]"}],"name":"swapNFTsForToken","outputs":[{"internalType":"uint256","name":"outputAmount","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"count","type":"uint256"}],"name":"swapTokenForAnyNFTs","outputs":[{"internalType":"uint256","name":"inputAmount","type":"uint256"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256[]","name":"nftIds","type":"uint256[]"}],"name":"swapTokenForSpecificNFTs","outputs":[{"internalType":"uint256","name":"inputAmount","type":"uint256"}],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"tokenReserve","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"virtualNftReserve","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"virtualTokenReserve","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256[]","name":"nftIds","type":"uint256[]"},{"internalType":"uint256","name":"virtualCount","type":"uint256"}],"name":"withdrawERC721","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"withdrawFee","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"withdrawToken","outputs":[],"stateMutability":"nonpayable","type":"function"},{"stateMutability":"payable","type":"receive"}]
Loading...
Loading
Loading...
Loading
Multichain Portfolio | 30 Chains
Chain | Token | Portfolio % | Price | Amount | Value |
---|---|---|---|---|---|
ETH | 100.00% | $2,631.74 | 0.1203 | $316.55 |
Loading...
Loading
[ Download: CSV Export ]
A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.