编译器
0.8.12+commit.f00d7308
文件 1 的 8:Address.sol
pragma solidity ^0.8.0;
library Address {
function isContract(address account) internal view returns (bool) {
uint256 size;
assembly {
size := extcodesize(account)
}
return size > 0;
}
function sendValue(address payable recipient, uint256 amount) internal {
require(address(this).balance >= amount, "Address: insufficient balance");
(bool success, ) = recipient.call{value: amount}("");
require(success, "Address: unable to send value, recipient may have reverted");
}
function functionCall(address target, bytes memory data) internal returns (bytes memory) {
return functionCall(target, data, "Address: low-level call failed");
}
function functionCall(
address target,
bytes memory data,
string memory errorMessage
) internal returns (bytes memory) {
return functionCallWithValue(target, data, 0, errorMessage);
}
function functionCallWithValue(
address target,
bytes memory data,
uint256 value
) internal returns (bytes memory) {
return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
}
function functionCallWithValue(
address target,
bytes memory data,
uint256 value,
string memory errorMessage
) internal returns (bytes memory) {
require(address(this).balance >= value, "Address: insufficient balance for call");
require(isContract(target), "Address: call to non-contract");
(bool success, bytes memory returndata) = target.call{value: value}(data);
return verifyCallResult(success, returndata, errorMessage);
}
function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
return functionStaticCall(target, data, "Address: low-level static call failed");
}
function functionStaticCall(
address target,
bytes memory data,
string memory errorMessage
) internal view returns (bytes memory) {
require(isContract(target), "Address: static call to non-contract");
(bool success, bytes memory returndata) = target.staticcall(data);
return verifyCallResult(success, returndata, errorMessage);
}
function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
return functionDelegateCall(target, data, "Address: low-level delegate call failed");
}
function functionDelegateCall(
address target,
bytes memory data,
string memory errorMessage
) internal returns (bytes memory) {
require(isContract(target), "Address: delegate call to non-contract");
(bool success, bytes memory returndata) = target.delegatecall(data);
return verifyCallResult(success, returndata, errorMessage);
}
function verifyCallResult(
bool success,
bytes memory returndata,
string memory errorMessage
) internal pure returns (bytes memory) {
if (success) {
return returndata;
} else {
if (returndata.length > 0) {
assembly {
let returndata_size := mload(returndata)
revert(add(32, returndata), returndata_size)
}
} else {
revert(errorMessage);
}
}
}
}
文件 2 的 8:Auction.sol
pragma solidity 0.8.12;
import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import "@openzeppelin/contracts/security/Pausable.sol";
import "@openzeppelin/contracts/utils/cryptography/MerkleProof.sol";
contract Auction is Ownable, Pausable {
using SafeERC20 for IERC20;
uint256 public immutable minimumUnitPrice;
uint256 public immutable minimumBidIncrement;
uint256 public immutable unitPriceStepSize;
uint256 public immutable minimumQuantity;
uint256 public immutable maximumQuantity;
uint256 public immutable numberOfAuctions;
uint256 public immutable itemsPerAuction;
address payable public immutable beneficiaryAddress;
uint256 public auctionLengthInHours = 24;
uint256 constant randomEnd = 7;
uint256 public constant auctionEndThresholdHrs = 3;
uint256 public constant endWindows = 18;
uint256 public auctionStart;
bytes32 public refundMerkleRoot;
AuctionStatus private _auctionStatus;
uint256 private _bidIndex;
event AuctionStarted();
event AuctionEnded();
event BidPlaced(
bytes32 indexed bidHash,
uint256 indexed auctionIndex,
address indexed bidder,
uint256 bidIndex,
uint256 unitPrice,
uint256 quantity,
uint256 balance
);
event RefundIssued(address indexed refundRecipient, uint256 refundAmount);
struct Bid {
uint128 unitPrice;
uint128 quantity;
}
struct AuctionStatus {
bool started;
bool ended;
}
mapping(bytes32 => Bid) private _bids;
mapping(address => bool) private _excessRefunded;
mapping(uint256 => bool) private _windowChecked;
constructor(
address payable beneficiaryAddress_,
uint256 minimumUnitPrice_,
uint256 minimumBidIncrement_,
uint256 unitPriceStepSize_,
uint256 maximumQuantity_,
uint256 numberOfAuctions_,
uint256 itemsPerAuction_
) {
beneficiaryAddress = beneficiaryAddress_;
minimumUnitPrice = minimumUnitPrice_;
minimumBidIncrement = minimumBidIncrement_;
unitPriceStepSize = unitPriceStepSize_;
minimumQuantity = 1;
maximumQuantity = maximumQuantity_;
numberOfAuctions = numberOfAuctions_;
itemsPerAuction = itemsPerAuction_;
pause();
}
modifier whenRefundsActive() {
require(refundMerkleRoot != 0, "Refund merkle root not set");
_;
}
modifier whenAuctionActive() {
require(!_auctionStatus.ended, "Auction has already ended");
require(_auctionStatus.started, "Auction hasn't started yet");
_;
}
modifier whenPreAuction() {
require(!_auctionStatus.ended, "Auction has already ended");
require(!_auctionStatus.started, "Auction has already started");
_;
}
modifier whenAuctionEnded() {
require(_auctionStatus.ended, "Auction hasn't ended yet");
require(_auctionStatus.started, "Auction hasn't started yet");
_;
}
function auctionStatus() public view returns (AuctionStatus memory) {
return _auctionStatus;
}
function pause() public onlyOwner {
_pause();
}
function unpause() public onlyOwner {
_unpause();
}
function startAuction() external onlyOwner whenPreAuction {
_auctionStatus.started = true;
auctionStart = block.timestamp;
if (paused()) {
unpause();
}
emit AuctionStarted();
}
function getAuctionEnd() internal view returns (uint256) {
return auctionStart + (auctionLengthInHours * 1 hours);
}
function endAuction() external whenAuctionActive {
require(
block.timestamp >= getAuctionEnd(),
"Auction can't be stopped until due"
);
_endAuction();
}
function _endAuction() internal whenAuctionActive {
_auctionStatus.ended = true;
if (!paused()) {
_pause();
}
emit AuctionEnded();
}
function numberOfBidsPlaced() external view returns (uint256) {
return _bidIndex;
}
function getBid(uint256 auctionIndex_, address bidder_)
external
view
returns (Bid memory)
{
return _bids[_bidHash(auctionIndex_, bidder_)];
}
function _bidHash(uint256 auctionIndex_, address bidder_)
internal
pure
returns (bytes32)
{
return keccak256(abi.encode(auctionIndex_, bidder_));
}
function _refundHash(uint256 refundAmount_, address bidder_)
internal
pure
returns (bytes32)
{
return keccak256(abi.encode(refundAmount_, bidder_));
}
function placeBid(
uint256 auctionIndex_,
uint256 quantity_,
uint256 unitPrice_
) external payable whenNotPaused whenAuctionActive {
if (msg.value > 0 && msg.value < minimumBidIncrement) {
revert("Bid lower than minimum bid increment.");
}
require(auctionIndex_ < numberOfAuctions, "Invalid auctionIndex");
bytes32 bidHash = _bidHash(auctionIndex_, msg.sender);
uint256 initialUnitPrice = _bids[bidHash].unitPrice;
uint256 initialQuantity = _bids[bidHash].quantity;
uint256 initialBalance = initialUnitPrice * initialQuantity;
uint256 finalUnitPrice = unitPrice_;
uint256 finalQuantity = quantity_;
uint256 finalBalance = initialBalance + msg.value;
require(
finalUnitPrice % unitPriceStepSize == 0,
"Unit price step too small"
);
require(finalQuantity >= minimumQuantity, "Quantity too low");
require(finalQuantity <= maximumQuantity, "Quantity too high");
require(finalBalance >= initialBalance, "Balance can't be lowered");
require(finalUnitPrice >= initialUnitPrice, "Unit price can't be lowered");
require(
finalQuantity * finalUnitPrice == finalBalance,
"Quantity * Unit Price != Total Value"
);
require(finalUnitPrice >= minimumUnitPrice, "Bid unit price too low");
if (
initialUnitPrice == finalUnitPrice && initialQuantity == finalQuantity
) {
revert("This bid doesn't change anything");
}
_bids[bidHash].unitPrice = uint128(finalUnitPrice);
_bids[bidHash].quantity = uint128(finalQuantity);
emit BidPlaced(
bidHash,
auctionIndex_,
msg.sender,
_bidIndex,
finalUnitPrice,
finalQuantity,
finalBalance
);
_bidIndex += 1;
_checkAuctionEnd();
}
function withdrawContractBalance() external onlyOwner {
(bool success, ) = beneficiaryAddress.call{value: address(this).balance}(
""
);
require(success, "Transfer failed");
}
function transferERC20Token(IERC20 token, uint256 amount) external onlyOwner {
token.safeTransfer(owner(), amount);
}
receive() external payable {
require(msg.value > 0, "No ether was sent");
require(
msg.sender == beneficiaryAddress || msg.sender == owner(),
"Only owner or beneficiary can fund contract"
);
}
function setRefundMerkleRoot(bytes32 refundMerkleRoot_)
external
onlyOwner
whenAuctionEnded
{
refundMerkleRoot = refundMerkleRoot_;
}
function claimRefund(uint256 refundAmount_, bytes32[] calldata proof_)
external
whenNotPaused
whenAuctionEnded
whenRefundsActive
{
require(!_excessRefunded[msg.sender], "Refund already issued");
bytes32 leaf = _refundHash(refundAmount_, msg.sender);
require(
MerkleProof.verify(proof_, refundMerkleRoot, leaf),
"Refund proof invalid"
);
uint256 totalBalance;
for (
uint256 auctionIndex = 0;
auctionIndex < numberOfAuctions;
auctionIndex++
) {
bytes32 bidHash = _bidHash(auctionIndex, msg.sender);
totalBalance += _bids[bidHash].unitPrice * _bids[bidHash].quantity;
}
require(refundAmount_ <= totalBalance, "Refund request exceeds balance");
_excessRefunded[msg.sender] = true;
(bool success, ) = msg.sender.call{value: refundAmount_}("");
require(success, "Refund failed");
emit RefundIssued(msg.sender, refundAmount_);
}
function _checkAuctionEnd() internal {
if (block.timestamp >= getAuctionEnd()) {
_endAuction();
} else {
uint256 auctionEndThreshold = getAuctionEnd() -
(auctionEndThresholdHrs * 1 hours);
if (block.timestamp >= auctionEndThreshold) {
uint256 windowSize = (auctionEndThresholdHrs * 1 hours) / endWindows;
uint256 windowIndex = (block.timestamp - auctionEndThreshold) /
windowSize;
if (!_windowChecked[windowIndex]) {
_windowChecked[windowIndex] = true;
if (_getRandomNumber() % endWindows == randomEnd) {
_endAuction();
}
}
}
}
}
function _getRandomNumber() internal view returns (uint256) {
return
uint256(keccak256(abi.encode(_bidIndex, blockhash(block.number - 1))));
}
}
文件 3 的 8:Context.sol
pragma solidity ^0.8.0;
abstract contract Context {
function _msgSender() internal view virtual returns (address) {
return msg.sender;
}
function _msgData() internal view virtual returns (bytes calldata) {
return msg.data;
}
}
文件 4 的 8:IERC20.sol
pragma solidity ^0.8.0;
interface IERC20 {
function totalSupply() external view returns (uint256);
function balanceOf(address account) external view returns (uint256);
function transfer(address recipient, uint256 amount) external returns (bool);
function allowance(address owner, address spender) external view returns (uint256);
function approve(address spender, uint256 amount) external returns (bool);
function transferFrom(
address sender,
address recipient,
uint256 amount
) external returns (bool);
event Transfer(address indexed from, address indexed to, uint256 value);
event Approval(address indexed owner, address indexed spender, uint256 value);
}
文件 5 的 8:MerkleProof.sol
pragma solidity ^0.8.0;
library MerkleProof {
function verify(
bytes32[] memory proof,
bytes32 root,
bytes32 leaf
) internal pure returns (bool) {
bytes32 computedHash = leaf;
for (uint256 i = 0; i < proof.length; i++) {
bytes32 proofElement = proof[i];
if (computedHash <= proofElement) {
computedHash = keccak256(abi.encodePacked(computedHash, proofElement));
} else {
computedHash = keccak256(abi.encodePacked(proofElement, computedHash));
}
}
return computedHash == root;
}
}
文件 6 的 8:Ownable.sol
pragma solidity ^0.8.0;
import "../utils/Context.sol";
abstract contract Ownable is Context {
address private _owner;
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
constructor() {
_setOwner(_msgSender());
}
function owner() public view virtual returns (address) {
return _owner;
}
modifier onlyOwner() {
require(owner() == _msgSender(), "Ownable: caller is not the owner");
_;
}
function renounceOwnership() public virtual onlyOwner {
_setOwner(address(0));
}
function transferOwnership(address newOwner) public virtual onlyOwner {
require(newOwner != address(0), "Ownable: new owner is the zero address");
_setOwner(newOwner);
}
function _setOwner(address newOwner) private {
address oldOwner = _owner;
_owner = newOwner;
emit OwnershipTransferred(oldOwner, newOwner);
}
}
文件 7 的 8:Pausable.sol
pragma solidity ^0.8.0;
import "../utils/Context.sol";
abstract contract Pausable is Context {
event Paused(address account);
event Unpaused(address account);
bool private _paused;
constructor() {
_paused = false;
}
function paused() public view virtual returns (bool) {
return _paused;
}
modifier whenNotPaused() {
require(!paused(), "Pausable: paused");
_;
}
modifier whenPaused() {
require(paused(), "Pausable: not paused");
_;
}
function _pause() internal virtual whenNotPaused {
_paused = true;
emit Paused(_msgSender());
}
function _unpause() internal virtual whenPaused {
_paused = false;
emit Unpaused(_msgSender());
}
}
文件 8 的 8:SafeERC20.sol
pragma solidity ^0.8.0;
import "../IERC20.sol";
import "../../../utils/Address.sol";
library SafeERC20 {
using Address for address;
function safeTransfer(
IERC20 token,
address to,
uint256 value
) internal {
_callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));
}
function safeTransferFrom(
IERC20 token,
address from,
address to,
uint256 value
) internal {
_callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));
}
function safeApprove(
IERC20 token,
address spender,
uint256 value
) internal {
require(
(value == 0) || (token.allowance(address(this), spender) == 0),
"SafeERC20: approve from non-zero to non-zero allowance"
);
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));
}
function safeIncreaseAllowance(
IERC20 token,
address spender,
uint256 value
) internal {
uint256 newAllowance = token.allowance(address(this), spender) + value;
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
}
function safeDecreaseAllowance(
IERC20 token,
address spender,
uint256 value
) internal {
unchecked {
uint256 oldAllowance = token.allowance(address(this), spender);
require(oldAllowance >= value, "SafeERC20: decreased allowance below zero");
uint256 newAllowance = oldAllowance - value;
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
}
}
function _callOptionalReturn(IERC20 token, bytes memory data) private {
bytes memory returndata = address(token).functionCall(data, "SafeERC20: low-level call failed");
if (returndata.length > 0) {
require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed");
}
}
}
{
"compilationTarget": {
"contracts/Auction.sol": "Auction"
},
"evmVersion": "london",
"libraries": {},
"metadata": {
"bytecodeHash": "ipfs"
},
"optimizer": {
"enabled": false,
"runs": 200
},
"remappings": []
}
[{"inputs":[{"internalType":"address payable","name":"beneficiaryAddress_","type":"address"},{"internalType":"uint256","name":"minimumUnitPrice_","type":"uint256"},{"internalType":"uint256","name":"minimumBidIncrement_","type":"uint256"},{"internalType":"uint256","name":"unitPriceStepSize_","type":"uint256"},{"internalType":"uint256","name":"maximumQuantity_","type":"uint256"},{"internalType":"uint256","name":"numberOfAuctions_","type":"uint256"},{"internalType":"uint256","name":"itemsPerAuction_","type":"uint256"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[],"name":"AuctionEnded","type":"event"},{"anonymous":false,"inputs":[],"name":"AuctionStarted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"bidHash","type":"bytes32"},{"indexed":true,"internalType":"uint256","name":"auctionIndex","type":"uint256"},{"indexed":true,"internalType":"address","name":"bidder","type":"address"},{"indexed":false,"internalType":"uint256","name":"bidIndex","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"unitPrice","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"quantity","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"balance","type":"uint256"}],"name":"BidPlaced","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"account","type":"address"}],"name":"Paused","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"refundRecipient","type":"address"},{"indexed":false,"internalType":"uint256","name":"refundAmount","type":"uint256"}],"name":"RefundIssued","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"account","type":"address"}],"name":"Unpaused","type":"event"},{"inputs":[],"name":"auctionEndThresholdHrs","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"auctionLengthInHours","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"auctionStart","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"auctionStatus","outputs":[{"components":[{"internalType":"bool","name":"started","type":"bool"},{"internalType":"bool","name":"ended","type":"bool"}],"internalType":"struct Auction.AuctionStatus","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"beneficiaryAddress","outputs":[{"internalType":"address payable","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"refundAmount_","type":"uint256"},{"internalType":"bytes32[]","name":"proof_","type":"bytes32[]"}],"name":"claimRefund","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"endAuction","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"endWindows","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"auctionIndex_","type":"uint256"},{"internalType":"address","name":"bidder_","type":"address"}],"name":"getBid","outputs":[{"components":[{"internalType":"uint128","name":"unitPrice","type":"uint128"},{"internalType":"uint128","name":"quantity","type":"uint128"}],"internalType":"struct Auction.Bid","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"itemsPerAuction","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"maximumQuantity","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"minimumBidIncrement","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"minimumQuantity","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"minimumUnitPrice","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"numberOfAuctions","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"numberOfBidsPlaced","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pause","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"paused","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"auctionIndex_","type":"uint256"},{"internalType":"uint256","name":"quantity_","type":"uint256"},{"internalType":"uint256","name":"unitPrice_","type":"uint256"}],"name":"placeBid","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"refundMerkleRoot","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"refundMerkleRoot_","type":"bytes32"}],"name":"setRefundMerkleRoot","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"startAuction","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract IERC20","name":"token","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transferERC20Token","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"unitPriceStepSize","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"unpause","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"withdrawContractBalance","outputs":[],"stateMutability":"nonpayable","type":"function"},{"stateMutability":"payable","type":"receive"}]