编译器
0.8.19+commit.7dd6d404
文件 1 的 4:Address.sol
pragma solidity ^0.8.1;
library Address {
function isContract(address account) internal view returns (bool) {
return account.code.length > 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 functionCallWithValue(target, data, 0, "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");
(bool success, bytes memory returndata) = target.call{value: value}(data);
return verifyCallResultFromTarget(target, 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) {
(bool success, bytes memory returndata) = target.staticcall(data);
return verifyCallResultFromTarget(target, 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) {
(bool success, bytes memory returndata) = target.delegatecall(data);
return verifyCallResultFromTarget(target, success, returndata, errorMessage);
}
function verifyCallResultFromTarget(
address target,
bool success,
bytes memory returndata,
string memory errorMessage
) internal view returns (bytes memory) {
if (success) {
if (returndata.length == 0) {
require(isContract(target), "Address: call to non-contract");
}
return returndata;
} else {
_revert(returndata, errorMessage);
}
}
function verifyCallResult(
bool success,
bytes memory returndata,
string memory errorMessage
) internal pure returns (bytes memory) {
if (success) {
return returndata;
} else {
_revert(returndata, errorMessage);
}
}
function _revert(bytes memory returndata, string memory errorMessage) private pure {
if (returndata.length > 0) {
assembly {
let returndata_size := mload(returndata)
revert(add(32, returndata), returndata_size)
}
} else {
revert(errorMessage);
}
}
}
文件 2 的 4:DutchAuction.sol
pragma solidity 0.8.19;
import {Address} from "@openzeppelin/utils/Address.sol";
import {MerkleProof} from "@openzeppelin/utils/cryptography/MerkleProof.sol";
import {IERC20} from "@openzeppelin/interfaces/IERC20.sol";
struct AuctionDetails {
uint256 startTime;
uint256 endTime;
uint256 totalTokens;
uint256 startPrice;
uint256 minimumPrice;
}
error NoCommitmentRegistered();
error PleaseSendETH();
error AuctionHasNotStarted();
error AuctionHasFinalized();
error AuctionHasNotFinalized();
error ClaimTokensInstead();
error NotInvited();
contract DutchAuction {
using Address for address payable;
event FinalizedAuction(uint256 timestamp);
event ClaimedPRTC(address indexed who, uint256 amount);
event CommittedETH(address indexed who, uint256 amount);
event RefundETH(address indexed who, uint256 amount);
uint256 private constant WAD = 1e18;
uint256 private constant INVITE_LIST_PERIOD = 2 days;
IERC20 public immutable prtc;
address public immutable beneficiary;
bytes32 public immutable merkleRoot;
AuctionDetails public auctionDetails;
uint256 public totalCommitments;
bool public isAuctionFinalized;
mapping(address user => uint256 amountCommitted) public commitments;
constructor(
IERC20 _prtc,
address _beneficiary,
bytes32 _merkleRoot,
AuctionDetails memory _auctionDetails
) {
prtc = _prtc;
beneficiary = _beneficiary;
auctionDetails = _auctionDetails;
merkleRoot = _merkleRoot;
}
function claimTokens() external {
if (!auctionSuccessful()) revert AuctionHasNotFinalized();
if (commitments[msg.sender] == 0) revert NoCommitmentRegistered();
uint256 amountForUser =
commitments[msg.sender] * auctionDetails.totalTokens / totalCommitments;
delete commitments[msg.sender];
prtc.transfer(msg.sender, amountForUser);
emit ClaimedPRTC(msg.sender, amountForUser);
}
function claimETH() external {
if (!isAuctionFinalized) revert AuctionHasNotFinalized();
if (commitments[msg.sender] == 0) revert NoCommitmentRegistered();
if (auctionSuccessful()) revert ClaimTokensInstead();
uint256 amount = commitments[msg.sender];
delete commitments[msg.sender];
payable(msg.sender).sendValue(amount);
emit RefundETH(msg.sender, amount);
}
function finalizeAuction() external {
if (isAuctionFinalized) revert AuctionHasFinalized();
if (!(block.timestamp > auctionDetails.endTime || auctionSuccessful())) {
revert AuctionHasNotFinalized();
}
isAuctionFinalized = true;
if (auctionSuccessful()) {
payable(beneficiary).sendValue(address(this).balance);
} else {
prtc.transfer(beneficiary, auctionDetails.totalTokens);
}
emit FinalizedAuction(block.timestamp);
}
function commitETH(bytes32[] calldata _mp) external payable {
if (block.timestamp < auctionDetails.startTime) revert AuctionHasNotStarted();
if (isAuctionFinalized || auctionSuccessful() || block.timestamp > auctionDetails.endTime) {
revert AuctionHasFinalized();
}
if (msg.value == 0) revert PleaseSendETH();
if (!_verifyMerkleProof(_mp)) revert NotInvited();
uint256 commitment = _calculateCommitment(msg.value);
uint256 maybeRefund = msg.value - commitment;
commitments[msg.sender] += commitment;
totalCommitments += commitment;
emit CommittedETH(msg.sender, commitment);
if (maybeRefund != 0) {
payable(msg.sender).sendValue(maybeRefund);
emit RefundETH(msg.sender, maybeRefund);
}
}
function auctionSuccessful() public view returns (bool) {
return tokenPrice() >= clearingPrice();
}
function clearingPrice() public view returns (uint256) {
uint256 _tokenPrice = tokenPrice();
uint256 _currentPrice = priceFunction();
return _tokenPrice > _currentPrice ? _tokenPrice : _currentPrice;
}
function tokenPrice() public view returns (uint256) {
return totalCommitments * WAD / auctionDetails.totalTokens;
}
function priceFunction() public view returns (uint256) {
if (block.timestamp <= auctionDetails.startTime) return auctionDetails.startPrice;
if (block.timestamp >= auctionDetails.endTime) return auctionDetails.minimumPrice;
uint256 auctionDuration = auctionDetails.endTime - auctionDetails.startTime;
uint256 timeSinceAuctionStart = block.timestamp - auctionDetails.startTime;
uint256 deltaPrice = auctionDetails.startPrice - auctionDetails.minimumPrice;
return auctionDetails.startPrice - (timeSinceAuctionStart * deltaPrice / auctionDuration);
}
function _calculateCommitment(uint256 _amount) internal view returns (uint256) {
uint256 maxCommitment = auctionDetails.totalTokens * clearingPrice() / WAD;
if (totalCommitments + _amount <= maxCommitment) return _amount;
return maxCommitment - totalCommitments;
}
function _verifyMerkleProof(bytes32[] calldata mp) internal view returns (bool) {
if (block.timestamp >= auctionDetails.startTime + INVITE_LIST_PERIOD) return true;
return MerkleProof.verifyCalldata(mp, merkleRoot, keccak256(abi.encodePacked(msg.sender)));
}
}
文件 3 的 4:IERC20.sol
pragma solidity ^0.8.0;
import "../token/ERC20/IERC20.sol";
文件 4 的 4:MerkleProof.sol
pragma solidity ^0.8.0;
library MerkleProof {
function verify(bytes32[] memory proof, bytes32 root, bytes32 leaf) internal pure returns (bool) {
return processProof(proof, leaf) == root;
}
function verifyCalldata(bytes32[] calldata proof, bytes32 root, bytes32 leaf) internal pure returns (bool) {
return processProofCalldata(proof, leaf) == root;
}
function processProof(bytes32[] memory proof, bytes32 leaf) internal pure returns (bytes32) {
bytes32 computedHash = leaf;
for (uint256 i = 0; i < proof.length; i++) {
computedHash = _hashPair(computedHash, proof[i]);
}
return computedHash;
}
function processProofCalldata(bytes32[] calldata proof, bytes32 leaf) internal pure returns (bytes32) {
bytes32 computedHash = leaf;
for (uint256 i = 0; i < proof.length; i++) {
computedHash = _hashPair(computedHash, proof[i]);
}
return computedHash;
}
function multiProofVerify(
bytes32[] memory proof,
bool[] memory proofFlags,
bytes32 root,
bytes32[] memory leaves
) internal pure returns (bool) {
return processMultiProof(proof, proofFlags, leaves) == root;
}
function multiProofVerifyCalldata(
bytes32[] calldata proof,
bool[] calldata proofFlags,
bytes32 root,
bytes32[] memory leaves
) internal pure returns (bool) {
return processMultiProofCalldata(proof, proofFlags, leaves) == root;
}
function processMultiProof(
bytes32[] memory proof,
bool[] memory proofFlags,
bytes32[] memory leaves
) internal pure returns (bytes32 merkleRoot) {
uint256 leavesLen = leaves.length;
uint256 totalHashes = proofFlags.length;
require(leavesLen + proof.length - 1 == totalHashes, "MerkleProof: invalid multiproof");
bytes32[] memory hashes = new bytes32[](totalHashes);
uint256 leafPos = 0;
uint256 hashPos = 0;
uint256 proofPos = 0;
for (uint256 i = 0; i < totalHashes; i++) {
bytes32 a = leafPos < leavesLen ? leaves[leafPos++] : hashes[hashPos++];
bytes32 b = proofFlags[i]
? (leafPos < leavesLen ? leaves[leafPos++] : hashes[hashPos++])
: proof[proofPos++];
hashes[i] = _hashPair(a, b);
}
if (totalHashes > 0) {
unchecked {
return hashes[totalHashes - 1];
}
} else if (leavesLen > 0) {
return leaves[0];
} else {
return proof[0];
}
}
function processMultiProofCalldata(
bytes32[] calldata proof,
bool[] calldata proofFlags,
bytes32[] memory leaves
) internal pure returns (bytes32 merkleRoot) {
uint256 leavesLen = leaves.length;
uint256 totalHashes = proofFlags.length;
require(leavesLen + proof.length - 1 == totalHashes, "MerkleProof: invalid multiproof");
bytes32[] memory hashes = new bytes32[](totalHashes);
uint256 leafPos = 0;
uint256 hashPos = 0;
uint256 proofPos = 0;
for (uint256 i = 0; i < totalHashes; i++) {
bytes32 a = leafPos < leavesLen ? leaves[leafPos++] : hashes[hashPos++];
bytes32 b = proofFlags[i]
? (leafPos < leavesLen ? leaves[leafPos++] : hashes[hashPos++])
: proof[proofPos++];
hashes[i] = _hashPair(a, b);
}
if (totalHashes > 0) {
unchecked {
return hashes[totalHashes - 1];
}
} else if (leavesLen > 0) {
return leaves[0];
} else {
return proof[0];
}
}
function _hashPair(bytes32 a, bytes32 b) private pure returns (bytes32) {
return a < b ? _efficientHash(a, b) : _efficientHash(b, a);
}
function _efficientHash(bytes32 a, bytes32 b) private pure returns (bytes32 value) {
assembly {
mstore(0x00, a)
mstore(0x20, b)
value := keccak256(0x00, 0x40)
}
}
}
{
"compilationTarget": {
"src/DutchAuction.sol": "DutchAuction"
},
"evmVersion": "paris",
"libraries": {},
"metadata": {
"bytecodeHash": "ipfs"
},
"optimizer": {
"enabled": true,
"runs": 200
},
"remappings": [
":@openzeppelin/=lib/openzeppelin-contracts/contracts/",
":ds-test/=lib/forge-std/lib/ds-test/src/",
":erc4626-tests/=lib/openzeppelin-contracts/lib/erc4626-tests/",
":forge-std/=lib/forge-std/src/",
":openzeppelin-contracts/=lib/openzeppelin-contracts/",
":openzeppelin/=lib/openzeppelin-contracts/contracts/"
]
}
[{"inputs":[{"internalType":"contract IERC20","name":"_prtc","type":"address"},{"internalType":"address","name":"_beneficiary","type":"address"},{"internalType":"bytes32","name":"_merkleRoot","type":"bytes32"},{"components":[{"internalType":"uint256","name":"startTime","type":"uint256"},{"internalType":"uint256","name":"endTime","type":"uint256"},{"internalType":"uint256","name":"totalTokens","type":"uint256"},{"internalType":"uint256","name":"startPrice","type":"uint256"},{"internalType":"uint256","name":"minimumPrice","type":"uint256"}],"internalType":"struct AuctionDetails","name":"_auctionDetails","type":"tuple"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"AuctionHasFinalized","type":"error"},{"inputs":[],"name":"AuctionHasNotFinalized","type":"error"},{"inputs":[],"name":"AuctionHasNotStarted","type":"error"},{"inputs":[],"name":"ClaimTokensInstead","type":"error"},{"inputs":[],"name":"NoCommitmentRegistered","type":"error"},{"inputs":[],"name":"NotInvited","type":"error"},{"inputs":[],"name":"PleaseSendETH","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"who","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"ClaimedPRTC","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"who","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"CommittedETH","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"timestamp","type":"uint256"}],"name":"FinalizedAuction","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"who","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"RefundETH","type":"event"},{"inputs":[],"name":"auctionDetails","outputs":[{"internalType":"uint256","name":"startTime","type":"uint256"},{"internalType":"uint256","name":"endTime","type":"uint256"},{"internalType":"uint256","name":"totalTokens","type":"uint256"},{"internalType":"uint256","name":"startPrice","type":"uint256"},{"internalType":"uint256","name":"minimumPrice","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"auctionSuccessful","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"beneficiary","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"claimETH","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"claimTokens","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"clearingPrice","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32[]","name":"_mp","type":"bytes32[]"}],"name":"commitETH","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"}],"name":"commitments","outputs":[{"internalType":"uint256","name":"amountCommitted","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"finalizeAuction","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"isAuctionFinalized","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"merkleRoot","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"priceFunction","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"prtc","outputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"tokenPrice","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalCommitments","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"}]