Skip to content

Instantly share code, notes, and snippets.

@djokicx
Created May 27, 2022 21:26
Show Gist options
  • Save djokicx/863fdae2f34e1335a381541486fe8c27 to your computer and use it in GitHub Desktop.
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=
// 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