Created
May 27, 2022 21:26
-
-
Save djokicx/863fdae2f34e1335a381541486fe8c27 to your computer and use it in GitHub Desktop.
Created using remix-ide: Realtime Ethereum Contract Compiler and Runtime. Load this file by pasting this gists URL or ID at https://remix.ethereum.org/#version=soljson-v0.8.14+commit.80d49f37.js&optimize=false&runs=200&gist=
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Dutch auction | |
// Seller sets the price at the start of the auction and price goes down over time | |
// When the buyer decides that the price is low enough, he buys and the auction ends | |
pragma solidity ^0.8.13; | |
// SPDX-License-Identifier: MIT | |
pragma solidity ^0.8.6; | |
interface IERC165 { | |
function supportsInterface(bytes4 interfaceID) external view returns (bool); | |
} | |
interface IERC721 is IERC165 { | |
function balanceOf(address owner) external view returns (uint balance); | |
function ownerOf(uint tokenId) external view returns (address owner); | |
function safeTransferFrom( | |
address from, | |
address to, | |
uint tokenId | |
) external; | |
function safeTransferFrom( | |
address from, | |
address to, | |
uint tokenId, | |
bytes calldata data | |
) external; | |
function transferFrom( | |
address from, | |
address to, | |
uint tokenId | |
) external; | |
function approve(address to, uint tokenId) external; | |
function getApproved(uint tokenId) external view returns (address operator); | |
function setApprovalForAll(address operator, bool _approved) external; | |
function isApprovedForAll(address owner, address operator) | |
external | |
view | |
returns (bool); | |
} | |
interface IERC721Receiver { | |
function onERC721Received( | |
address operator, | |
address from, | |
uint tokenId, | |
bytes calldata data | |
) external returns (bytes4); | |
} | |
contract ERC721 is IERC721 { | |
using Address for address; | |
event Transfer(address indexed from, address indexed to, uint indexed tokenId); | |
event Approval( | |
address indexed owner, | |
address indexed approved, | |
uint indexed tokenId | |
); | |
event ApprovalForAll( | |
address indexed owner, | |
address indexed operator, | |
bool approved | |
); | |
// Mapping from token ID to owner address | |
mapping(uint => address) private _owners; | |
// Mapping owner address to token count | |
mapping(address => uint) private _balances; | |
// Mapping from token ID to approved address | |
mapping(uint => address) private _tokenApprovals; | |
// Mapping from owner to operator approvals | |
mapping(address => mapping(address => bool)) private _operatorApprovals; | |
function supportsInterface(bytes4 interfaceId) | |
external | |
pure | |
override | |
returns (bool) | |
{ | |
return | |
interfaceId == type(IERC721).interfaceId || | |
interfaceId == type(IERC165).interfaceId; | |
} | |
function balanceOf(address owner) external view override returns (uint) { | |
require(owner != address(0), "owner = zero address"); | |
return _balances[owner]; | |
} | |
function ownerOf(uint tokenId) public view override returns (address owner) { | |
owner = _owners[tokenId]; | |
require(owner != address(0), "token doesn't exist"); | |
} | |
function isApprovedForAll(address owner, address operator) | |
external | |
view | |
override | |
returns (bool) | |
{ | |
return _operatorApprovals[owner][operator]; | |
} | |
function setApprovalForAll(address operator, bool approved) external override { | |
_operatorApprovals[msg.sender][operator] = approved; | |
emit ApprovalForAll(msg.sender, operator, approved); | |
} | |
function getApproved(uint tokenId) external view override returns (address) { | |
require(_owners[tokenId] != address(0), "token doesn't exist"); | |
return _tokenApprovals[tokenId]; | |
} | |
function _approve( | |
address owner, | |
address to, | |
uint tokenId | |
) private { | |
_tokenApprovals[tokenId] = to; | |
emit Approval(owner, to, tokenId); | |
} | |
function approve(address to, uint tokenId) external override { | |
address owner = _owners[tokenId]; | |
require( | |
msg.sender == owner || _operatorApprovals[owner][msg.sender], | |
"not owner nor approved for all" | |
); | |
_approve(owner, to, tokenId); | |
} | |
function _isApprovedOrOwner( | |
address owner, | |
address spender, | |
uint tokenId | |
) private view returns (bool) { | |
return (spender == owner || | |
_tokenApprovals[tokenId] == spender || | |
_operatorApprovals[owner][spender]); | |
} | |
function _transfer( | |
address owner, | |
address from, | |
address to, | |
uint tokenId | |
) private { | |
require(from == owner, "not owner"); | |
require(to != address(0), "transfer to the zero address"); | |
_approve(owner, address(0), tokenId); | |
_balances[from] -= 1; | |
_balances[to] += 1; | |
_owners[tokenId] = to; | |
emit Transfer(from, to, tokenId); | |
} | |
function transferFrom( | |
address from, | |
address to, | |
uint tokenId | |
) external override { | |
address owner = ownerOf(tokenId); | |
require( | |
_isApprovedOrOwner(owner, msg.sender, tokenId), | |
"not owner nor approved" | |
); | |
_transfer(owner, from, to, tokenId); | |
} | |
function _checkOnERC721Received( | |
address from, | |
address to, | |
uint tokenId, | |
bytes memory _data | |
) private returns (bool) { | |
if (to.isContract()) { | |
return | |
IERC721Receiver(to).onERC721Received( | |
msg.sender, | |
from, | |
tokenId, | |
_data | |
) == IERC721Receiver.onERC721Received.selector; | |
} else { | |
return true; | |
} | |
} | |
function _safeTransfer( | |
address owner, | |
address from, | |
address to, | |
uint tokenId, | |
bytes memory _data | |
) private { | |
_transfer(owner, from, to, tokenId); | |
require(_checkOnERC721Received(from, to, tokenId, _data), "not ERC721Receiver"); | |
} | |
function safeTransferFrom( | |
address from, | |
address to, | |
uint tokenId, | |
bytes memory _data | |
) public override { | |
address owner = ownerOf(tokenId); | |
require( | |
_isApprovedOrOwner(owner, msg.sender, tokenId), | |
"not owner nor approved" | |
); | |
_safeTransfer(owner, from, to, tokenId, _data); | |
} | |
function safeTransferFrom( | |
address from, | |
address to, | |
uint tokenId | |
) external override { | |
safeTransferFrom(from, to, tokenId, ""); | |
} | |
function mint(address to, uint tokenId) external { | |
require(to != address(0), "mint to zero address"); | |
require(_owners[tokenId] == address(0), "token already minted"); | |
_balances[to] += 1; | |
_owners[tokenId] = to; | |
emit Transfer(address(0), to, tokenId); | |
} | |
function burn(uint tokenId) external { | |
address owner = ownerOf(tokenId); | |
_approve(owner, address(0), tokenId); | |
_balances[owner] -= 1; | |
delete _owners[tokenId]; | |
emit Transfer(owner, address(0), tokenId); | |
} | |
} | |
library Address { | |
function isContract(address account) internal view returns (bool) { | |
uint size; | |
assembly { | |
size := extcodesize(account) | |
} | |
return size > 0; | |
} | |
} | |
contract DutchAuction { | |
uint private constant DURATION = 7 days; | |
IERC721 public immutable nft; | |
uint public immutable nftId; | |
address payable public immutable seller; | |
uint public immutable startingPrice; | |
uint public immutable startAt; | |
uint public immutable expiresAt; | |
uint public immutable discountRate; | |
constructor( | |
uint _startingPrice, uint _discountRate, address _nft, uint _nftId | |
) { | |
seller = payable(msg.sender); | |
startingPrice = _startingPrice; | |
discountRate = _discountRate; | |
startAt = block.timestamp; | |
expiresAt = block.timestamp + DURATION; | |
require(_startingPrice >= _discountRate * DURATION, "Starting price is less than discount"); | |
nft = IERC721(_nft); | |
nftId = _nftId; | |
} | |
function getPrice() public view returns (uint) { | |
return startingPrice - (discountRate * (block.timestamp - startAt)); | |
} | |
function buy() external payable { | |
require(block.timestamp < expiresAt, "Auction expired"); | |
require(msg.value > getPrice(), "Not enough eth sent"); | |
nft.transferFrom(seller, msg.sender, nftId); | |
uint refund = msg.value - getPrice(); | |
if(refund > 0) { | |
payable(msg.sender).transfer(refund); | |
} | |
selfdestruct(seller); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment