-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit 944277d
Showing
17 changed files
with
41,902 additions
and
0 deletions.
There are no files selected for viewing
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
ETHERSCAN_API_KEY=ABC123ABC123ABC123ABC123ABC123ABC1 | ||
ROPSTEN_URL=https://eth-ropsten.alchemyapi.io/v2/<YOUR ALCHEMY KEY> | ||
PRIVATE_KEY=0xabc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc1 |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
node_modules | ||
artifacts | ||
cache | ||
coverage |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
module.exports = { | ||
env: { | ||
browser: false, | ||
es2021: true, | ||
mocha: true, | ||
node: true, | ||
}, | ||
plugins: ["@typescript-eslint"], | ||
extends: [ | ||
"standard", | ||
"plugin:prettier/recommended", | ||
"plugin:node/recommended", | ||
], | ||
parser: "@typescript-eslint/parser", | ||
parserOptions: { | ||
ecmaVersion: 12, | ||
}, | ||
rules: { | ||
"node/no-unsupported-features/es-syntax": [ | ||
"error", | ||
{ ignores: ["modules"] }, | ||
], | ||
}, | ||
}; |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
node_modules | ||
.env | ||
coverage | ||
coverage.json | ||
typechain | ||
|
||
#Hardhat files | ||
cache | ||
artifacts |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
hardhat.config.ts | ||
scripts | ||
test |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
node_modules | ||
artifacts | ||
cache | ||
coverage* | ||
gasReporterOutput.json |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
{} |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
{ | ||
"extends": "solhint:recommended", | ||
"rules": { | ||
"compiler-version": ["error", "^0.8.0"], | ||
"func-visibility": ["warn", { "ignoreConstructors": true }] | ||
} | ||
} |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
node_modules |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
ISC License | ||
|
||
Copyright (c) 2022 AlmostFancy | ||
|
||
Permission to use, copy, modify, and/or distribute this software for any | ||
purpose with or without fee is hereby granted, provided that the above | ||
copyright notice and this permission notice appear in all copies. | ||
|
||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH | ||
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY | ||
AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, | ||
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM | ||
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR | ||
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR | ||
PERFORMANCE OF THIS SOFTWARE. |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,59 @@ | ||
[![License: ISC](https://img.shields.io/badge/License-ISC-blue.svg)](https://github.com/almostfancy/true-dutch/LICENSE.txt) | ||
[![NPM](https://img.shields.io/npm/v/@almost-fancy/true-dutch)](https://www.npmjs.com/package/@almost-fancy/true-dutch) | ||
|
||
## Installation | ||
|
||
```sh | ||
npm install -D @almost-fancy/true-dutch | ||
``` | ||
|
||
## Usage | ||
|
||
To get started, you can use the library as follows: | ||
|
||
```solidity | ||
pragma solidity ^0.8.4; | ||
import "@almost-fancy/true-dutch/contracts/TrueDutchAuction.sol"; | ||
contract AlmostFancy is TrueDutchAuction, ERC721A { | ||
constructor(address payable _beneficiary) | ||
ERC721A("AlmostFancy", "AF") | ||
TrueDutchAuction( | ||
DutchAuctionConfig({ | ||
saleStartTime: dutchSaleTime, | ||
startPriceWei: 0.99 ether, | ||
endPriceWei: 0.09 ether, | ||
duration: 9 hours, | ||
dropInterval: 15 minutes, | ||
maxBidsPerAddress: 0, | ||
available: 1111, | ||
maxPerTx: 3 | ||
}), | ||
_beneficiary | ||
) | ||
{} | ||
function placeBid(uint256 quantity) external payable { | ||
_placeAuctionBid(msg.sender, quantity); | ||
} | ||
function _handleBidPlaced( | ||
address whom, | ||
uint256 quantity, | ||
uint256 priceToPay | ||
) internal override { | ||
uint256 cost = priceToPay * quantity; | ||
_safeMint(whom, quantity); // call ERC721A#_safeMint to actually get the asset to the caller | ||
} | ||
} | ||
``` | ||
|
||
## License | ||
|
||
Distributed under the ISC License. See `LICENSE.txt` for more information. | ||
|
||
## Contact Us | ||
|
||
- Erik (lead dev) - [@erosemberg\_](https://twitter.com/erosemberg_) |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
// SPDX-License-Identifier: MIT | ||
// TrueDutchAuction contracts, written by AlmostFancy | ||
pragma solidity ^0.8.4; | ||
|
||
interface ITrueDutchAuction { | ||
struct AuctionBid { | ||
uint256 quantity; | ||
uint256 bid; | ||
uint256 timestamp; | ||
} | ||
|
||
event RefundPaid( | ||
address indexed to, | ||
AuctionBid[] bids, | ||
uint256 dutch, | ||
uint256 refund | ||
); | ||
|
||
event BidPlaced( | ||
address indexed from, | ||
uint256 quantity, | ||
uint256 currentPrice | ||
); | ||
|
||
function getBidsFrom(address bidder) | ||
external | ||
view | ||
returns (AuctionBid[] memory); | ||
|
||
function getDutchPrice() external view returns (uint256); | ||
} |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,183 @@ | ||
// SPDX-License-Identifier: MIT | ||
// TrueDutchAuction contracts, written by AlmostFancy | ||
pragma solidity ^0.8.4; | ||
|
||
import "./ITrueDutchAuction.sol"; | ||
import "@openzeppelin/contracts/security/ReentrancyGuard.sol"; | ||
|
||
abstract contract TrueDutchAuction is ITrueDutchAuction, ReentrancyGuard { | ||
struct DutchAuctionConfig { | ||
uint256 saleStartTime; | ||
uint256 startPriceWei; | ||
uint256 endPriceWei; | ||
uint256 duration; | ||
uint256 dropInterval; | ||
uint256 maxBidsPerAddress; // @dev if this is set to 0, there is no limit | ||
uint256 available; // @dev total amount of tokens that can be sold during dutch | ||
uint256 maxPerTx; // @dev max amount of bids per transaction | ||
} | ||
|
||
constructor(DutchAuctionConfig memory _config, address payable _beneficiary) { | ||
dutchConfig = _config; | ||
dropsPerStep = | ||
(_config.startPriceWei - _config.endPriceWei) / | ||
(_config.duration / _config.dropInterval); | ||
beneficiary = _beneficiary; | ||
} | ||
|
||
address payable public beneficiary; | ||
|
||
uint256 private totalSales; // @dev track amount of sales during dutch auction | ||
DutchAuctionConfig public dutchConfig; | ||
uint256 private dropsPerStep; | ||
|
||
uint256 internal lastDutchPrice; | ||
|
||
bool public refundsEnabled = false; | ||
mapping(address => bool) private claimedRefunds; | ||
mapping(address => AuctionBid[]) public auctionBids; | ||
bool private auctionProfitsWithdrawn = false; | ||
|
||
function _setBeneficiary(address payable _beneficiary) internal { | ||
beneficiary = _beneficiary; | ||
} | ||
|
||
function _setDutchConfig(DutchAuctionConfig memory _config) internal { | ||
dutchConfig = _config; | ||
dropsPerStep = | ||
(_config.startPriceWei - _config.endPriceWei) / | ||
(_config.duration / _config.dropInterval); | ||
} | ||
|
||
// @dev this method is called by _placeAuctionBid() after the sanity checks have | ||
// been completed. | ||
// this is where safeMint() and similar functions should be called | ||
function _handleBidPlaced(address whom, uint256 quantity, uint256 priceToPay) internal virtual; | ||
|
||
function _placeAuctionBid(address who, uint256 quantity) internal virtual { | ||
DutchAuctionConfig memory saleConfig = dutchConfig; | ||
// solhint-disable-next-line reason-string | ||
require( | ||
// solhint-disable-next-line not-rely-on-time | ||
saleConfig.saleStartTime != 0 && block.timestamp >= saleConfig.saleStartTime, | ||
"TrueDutchAuction: Dutch auction has not started yet!" | ||
); | ||
// solhint-disable-next-line reason-string | ||
require(quantity <= saleConfig.maxPerTx, "TrueDutchAuction: Exceeds max per tx"); | ||
uint256 dutchPrice = getDutchPrice(); | ||
// solhint-disable-next-line reason-string | ||
require(msg.value >= quantity * dutchPrice, "TrueDutchAuction: Not enough ETH sent"); | ||
if (saleConfig.maxBidsPerAddress != 0) { | ||
// solhint-disable-next-line reason-string | ||
require( | ||
auctionBids[who].length + quantity <= | ||
saleConfig.maxBidsPerAddress, | ||
"TrueDutchAuction: That amount will exceed the max allowed bids per address" | ||
); | ||
} | ||
// solhint-disable-next-line reason-string | ||
require( | ||
totalSales + quantity <= saleConfig.available, | ||
"TrueDutchAuction: Dutch Auction has already sold out!" | ||
); | ||
|
||
if (totalSales + quantity == saleConfig.available) { | ||
lastDutchPrice = dutchPrice; | ||
} | ||
|
||
totalSales += quantity; | ||
_handleBidPlaced(who, quantity, dutchPrice); // @dev check _handleBidPlaced() notes | ||
// store auction data for refunds later on | ||
auctionBids[who].push( | ||
AuctionBid({ | ||
quantity: quantity, | ||
bid: dutchPrice, | ||
// solhint-disable-next-line not-rely-on-time | ||
timestamp: block.timestamp | ||
}) | ||
); | ||
emit BidPlaced(who, quantity, dutchPrice); | ||
} | ||
|
||
// @dev returns the current dutch price by calculating the steps | ||
function getDutchPrice() public view override returns (uint256) { | ||
DutchAuctionConfig memory saleConfig = dutchConfig; | ||
// solhint-disable-next-line not-rely-on-time | ||
if (block.timestamp < saleConfig.saleStartTime) { | ||
return saleConfig.startPriceWei; | ||
} | ||
|
||
if ( | ||
// solhint-disable-next-line not-rely-on-time | ||
block.timestamp - saleConfig.saleStartTime >= saleConfig.duration | ||
) { | ||
return saleConfig.endPriceWei; | ||
} else { | ||
// solhint-disable-next-line not-rely-on-time | ||
uint256 steps = (block.timestamp - saleConfig.saleStartTime) / | ||
saleConfig.dropInterval; | ||
return saleConfig.startPriceWei - (steps * dropsPerStep); | ||
} | ||
} | ||
|
||
function _toggleRefunds(bool state) internal { | ||
refundsEnabled = state; | ||
} | ||
|
||
function claimRefund() external nonReentrant { | ||
// solhint-disable-next-line reason-string | ||
require(refundsEnabled, "TrueDutchAuction: Refunds are not enabled!"); | ||
// solhint-disable-next-line reason-string | ||
require( | ||
!claimedRefunds[msg.sender], | ||
"TrueDutchAuction: You have already claimed your refund!" | ||
); | ||
AuctionBid[] memory bids = auctionBids[msg.sender]; | ||
// solhint-disable-next-line reason-string | ||
require( | ||
bids.length > 0, | ||
"TrueDutchAuction: You are not eligible for a refund!" | ||
); | ||
|
||
uint256 refund = 0; | ||
for (uint256 i = 0; i < bids.length; i++) { | ||
AuctionBid memory bid = bids[i]; | ||
refund += (bid.bid * bid.quantity) - lastDutchPrice; // should be safe from underflows | ||
} | ||
claimedRefunds[msg.sender] = true; | ||
// solhint-disable-next-line reason-string | ||
require(refund > 0, "TrueDutchAuction: You are not eligible for a refund!"); | ||
// solhint-disable-next-line avoid-low-level-calls | ||
(bool success, ) = msg.sender.call{value: refund}(""); | ||
require(success, "TrueDutchAuction: Refund failed."); | ||
emit RefundPaid(msg.sender, bids, lastDutchPrice, refund); | ||
} | ||
|
||
// @dev withdraws profits made from the dutch auction, multiplies | ||
// the total amount of sales against the set dutch auction price | ||
// can only be called once. | ||
function _withdrawDutchProfits() internal nonReentrant { | ||
// solhint-disable-next-line reason-string | ||
require(!auctionProfitsWithdrawn, "TrueDutchAuction: Dutch Auction profits have already been paid out"); | ||
// solhint-disable-next-line reason-string | ||
require(lastDutchPrice > 0, "TrueDutchAuction: The Dutch Auction has not ended yet"); | ||
uint256 profits = totalSales * lastDutchPrice; | ||
|
||
// solhint-disable-next-line avoid-low-level-calls | ||
(bool success, ) = beneficiary.call{value: profits}(""); | ||
// solhint-disable-next-line reason-string | ||
require(success, "TrueDutchAuction: Withdraw failed."); | ||
auctionProfitsWithdrawn = true; | ||
} | ||
|
||
// @dev this is meant to be used as a last resort or for dev purposes | ||
// this contract's authors are not made responsible for misuse of this | ||
// method | ||
function _forceDutchFloor(uint256 floor) internal { | ||
lastDutchPrice = floor; | ||
} | ||
|
||
function getBidsFrom(address bidder) external view override returns(AuctionBid[] memory) { | ||
return auctionBids[bidder]; | ||
} | ||
} |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
import * as dotenv from "dotenv"; | ||
|
||
import { HardhatUserConfig } from "hardhat/config"; | ||
import "@nomiclabs/hardhat-waffle"; | ||
import "@typechain/hardhat"; | ||
import "hardhat-gas-reporter"; | ||
import "solidity-coverage"; | ||
|
||
dotenv.config(); | ||
|
||
const config: HardhatUserConfig = { | ||
solidity: { | ||
version: "0.8.4", | ||
settings: { | ||
optimizer: { | ||
enabled: true, | ||
runs: 500, | ||
}, | ||
}, | ||
}, | ||
gasReporter: { | ||
enabled: process.env.REPORT_GAS !== undefined, | ||
currency: "USD", | ||
}, | ||
}; | ||
|
||
export default config; |
Oops, something went wrong.