编译器
0.8.22+commit.4fc1097e
文件 1 的 19:Address.sol
pragma solidity ^0.8.20;
library Address {
error AddressInsufficientBalance(address account);
error AddressEmptyCode(address target);
error FailedInnerCall();
function sendValue(address payable recipient, uint256 amount) internal {
if (address(this).balance < amount) {
revert AddressInsufficientBalance(address(this));
}
(bool success, ) = recipient.call{value: amount}("");
if (!success) {
revert FailedInnerCall();
}
}
function functionCall(address target, bytes memory data) internal returns (bytes memory) {
return functionCallWithValue(target, data, 0);
}
function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
if (address(this).balance < value) {
revert AddressInsufficientBalance(address(this));
}
(bool success, bytes memory returndata) = target.call{value: value}(data);
return verifyCallResultFromTarget(target, success, returndata);
}
function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
(bool success, bytes memory returndata) = target.staticcall(data);
return verifyCallResultFromTarget(target, success, returndata);
}
function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
(bool success, bytes memory returndata) = target.delegatecall(data);
return verifyCallResultFromTarget(target, success, returndata);
}
function verifyCallResultFromTarget(
address target,
bool success,
bytes memory returndata
) internal view returns (bytes memory) {
if (!success) {
_revert(returndata);
} else {
if (returndata.length == 0 && target.code.length == 0) {
revert AddressEmptyCode(target);
}
return returndata;
}
}
function verifyCallResult(bool success, bytes memory returndata) internal pure returns (bytes memory) {
if (!success) {
_revert(returndata);
} else {
return returndata;
}
}
function _revert(bytes memory returndata) private pure {
if (returndata.length > 0) {
assembly {
let returndata_size := mload(returndata)
revert(add(32, returndata), returndata_size)
}
} else {
revert FailedInnerCall();
}
}
}
文件 2 的 19:AggregatorV3Interface.sol
pragma solidity ^0.8.0;
interface AggregatorV3Interface {
function decimals() external view returns (uint8);
function description() external view returns (string memory);
function version() external view returns (uint256);
function getRoundData(uint80 _roundId)
external
view
returns (
uint80 roundId,
int256 answer,
uint256 startedAt,
uint256 updatedAt,
uint80 answeredInRound
);
function latestRoundData()
external
view
returns (
uint80 roundId,
int256 answer,
uint256 startedAt,
uint256 updatedAt,
uint80 answeredInRound
);
}
文件 3 的 19:Common.sol
pragma solidity 0.8.22;
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
error ZeroAddress();
error ZeroLengthArray();
error IdenticalValue();
error ArrayLengthMismatch();
IERC20 constant ETH = IERC20(0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE);
文件 4 的 19:Context.sol
pragma solidity ^0.8.20;
abstract contract Context {
function _msgSender() internal view virtual returns (address) {
return msg.sender;
}
function _msgData() internal view virtual returns (bytes calldata) {
return msg.data;
}
}
文件 5 的 19:ECDSA.sol
pragma solidity ^0.8.20;
library ECDSA {
enum RecoverError {
NoError,
InvalidSignature,
InvalidSignatureLength,
InvalidSignatureS
}
error ECDSAInvalidSignature();
error ECDSAInvalidSignatureLength(uint256 length);
error ECDSAInvalidSignatureS(bytes32 s);
function tryRecover(bytes32 hash, bytes memory signature) internal pure returns (address, RecoverError, bytes32) {
if (signature.length == 65) {
bytes32 r;
bytes32 s;
uint8 v;
assembly {
r := mload(add(signature, 0x20))
s := mload(add(signature, 0x40))
v := byte(0, mload(add(signature, 0x60)))
}
return tryRecover(hash, v, r, s);
} else {
return (address(0), RecoverError.InvalidSignatureLength, bytes32(signature.length));
}
}
function recover(bytes32 hash, bytes memory signature) internal pure returns (address) {
(address recovered, RecoverError error, bytes32 errorArg) = tryRecover(hash, signature);
_throwError(error, errorArg);
return recovered;
}
function tryRecover(bytes32 hash, bytes32 r, bytes32 vs) internal pure returns (address, RecoverError, bytes32) {
unchecked {
bytes32 s = vs & bytes32(0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff);
uint8 v = uint8((uint256(vs) >> 255) + 27);
return tryRecover(hash, v, r, s);
}
}
function recover(bytes32 hash, bytes32 r, bytes32 vs) internal pure returns (address) {
(address recovered, RecoverError error, bytes32 errorArg) = tryRecover(hash, r, vs);
_throwError(error, errorArg);
return recovered;
}
function tryRecover(
bytes32 hash,
uint8 v,
bytes32 r,
bytes32 s
) internal pure returns (address, RecoverError, bytes32) {
if (uint256(s) > 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0) {
return (address(0), RecoverError.InvalidSignatureS, s);
}
address signer = ecrecover(hash, v, r, s);
if (signer == address(0)) {
return (address(0), RecoverError.InvalidSignature, bytes32(0));
}
return (signer, RecoverError.NoError, bytes32(0));
}
function recover(bytes32 hash, uint8 v, bytes32 r, bytes32 s) internal pure returns (address) {
(address recovered, RecoverError error, bytes32 errorArg) = tryRecover(hash, v, r, s);
_throwError(error, errorArg);
return recovered;
}
function _throwError(RecoverError error, bytes32 errorArg) private pure {
if (error == RecoverError.NoError) {
return;
} else if (error == RecoverError.InvalidSignature) {
revert ECDSAInvalidSignature();
} else if (error == RecoverError.InvalidSignatureLength) {
revert ECDSAInvalidSignatureLength(uint256(errorArg));
} else if (error == RecoverError.InvalidSignatureS) {
revert ECDSAInvalidSignatureS(errorArg);
}
}
}
文件 6 的 19:IERC20.sol
pragma solidity ^0.8.20;
interface IERC20 {
event Transfer(address indexed from, address indexed to, uint256 value);
event Approval(address indexed owner, address indexed spender, uint256 value);
function totalSupply() external view returns (uint256);
function balanceOf(address account) external view returns (uint256);
function transfer(address to, uint256 value) external returns (bool);
function allowance(address owner, address spender) external view returns (uint256);
function approve(address spender, uint256 value) external returns (bool);
function transferFrom(address from, address to, uint256 value) external returns (bool);
}
文件 7 的 19:IERC20Permit.sol
pragma solidity ^0.8.20;
interface IERC20Permit {
function permit(
address owner,
address spender,
uint256 value,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) external;
function nonces(address owner) external view returns (uint256);
function DOMAIN_SEPARATOR() external view returns (bytes32);
}
文件 8 的 19:IPreSaleDop.sol
pragma solidity 0.8.22;
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {IRounds} from "./IRounds.sol";
interface IPreSaleDop is IRounds {
function purchaseWithClaim(
IERC20 token,
uint256 tokenPrice,
uint8 referenceNormalizationFactor,
uint256 amount,
uint256 minAmountDop,
address recipient,
uint32 round
) external payable;
function verifyPurchaseWithClaim(
address recipient,
uint32 round,
uint256 deadline,
uint256[] calldata tokenPrices,
uint8[] calldata normalizationFactors,
IERC20[] calldata tokens,
uint256[] calldata amounts,
uint8 v,
bytes32 r,
bytes32 s
) external;
}
文件 9 的 19:IRounds.sol
pragma solidity 0.8.22;
interface IRounds {
function rounds(
uint32 round
) external view returns (uint256 startTime, uint256 endTime, uint256 price);
}
文件 10 的 19:Math.sol
pragma solidity ^0.8.20;
library Math {
error MathOverflowedMulDiv();
enum Rounding {
Floor,
Ceil,
Trunc,
Expand
}
function tryAdd(uint256 a, uint256 b) internal pure returns (bool, uint256) {
unchecked {
uint256 c = a + b;
if (c < a) return (false, 0);
return (true, c);
}
}
function trySub(uint256 a, uint256 b) internal pure returns (bool, uint256) {
unchecked {
if (b > a) return (false, 0);
return (true, a - b);
}
}
function tryMul(uint256 a, uint256 b) internal pure returns (bool, uint256) {
unchecked {
if (a == 0) return (true, 0);
uint256 c = a * b;
if (c / a != b) return (false, 0);
return (true, c);
}
}
function tryDiv(uint256 a, uint256 b) internal pure returns (bool, uint256) {
unchecked {
if (b == 0) return (false, 0);
return (true, a / b);
}
}
function tryMod(uint256 a, uint256 b) internal pure returns (bool, uint256) {
unchecked {
if (b == 0) return (false, 0);
return (true, a % b);
}
}
function max(uint256 a, uint256 b) internal pure returns (uint256) {
return a > b ? a : b;
}
function min(uint256 a, uint256 b) internal pure returns (uint256) {
return a < b ? a : b;
}
function average(uint256 a, uint256 b) internal pure returns (uint256) {
return (a & b) + (a ^ b) / 2;
}
function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {
if (b == 0) {
return a / b;
}
return a == 0 ? 0 : (a - 1) / b + 1;
}
function mulDiv(uint256 x, uint256 y, uint256 denominator) internal pure returns (uint256 result) {
unchecked {
uint256 prod0 = x * y;
uint256 prod1;
assembly {
let mm := mulmod(x, y, not(0))
prod1 := sub(sub(mm, prod0), lt(mm, prod0))
}
if (prod1 == 0) {
return prod0 / denominator;
}
if (denominator <= prod1) {
revert MathOverflowedMulDiv();
}
uint256 remainder;
assembly {
remainder := mulmod(x, y, denominator)
prod1 := sub(prod1, gt(remainder, prod0))
prod0 := sub(prod0, remainder)
}
uint256 twos = denominator & (0 - denominator);
assembly {
denominator := div(denominator, twos)
prod0 := div(prod0, twos)
twos := add(div(sub(0, twos), twos), 1)
}
prod0 |= prod1 * twos;
uint256 inverse = (3 * denominator) ^ 2;
inverse *= 2 - denominator * inverse;
inverse *= 2 - denominator * inverse;
inverse *= 2 - denominator * inverse;
inverse *= 2 - denominator * inverse;
inverse *= 2 - denominator * inverse;
inverse *= 2 - denominator * inverse;
result = prod0 * inverse;
return result;
}
}
function mulDiv(uint256 x, uint256 y, uint256 denominator, Rounding rounding) internal pure returns (uint256) {
uint256 result = mulDiv(x, y, denominator);
if (unsignedRoundsUp(rounding) && mulmod(x, y, denominator) > 0) {
result += 1;
}
return result;
}
function sqrt(uint256 a) internal pure returns (uint256) {
if (a == 0) {
return 0;
}
uint256 result = 1 << (log2(a) >> 1);
unchecked {
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
return min(result, a / result);
}
}
function sqrt(uint256 a, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = sqrt(a);
return result + (unsignedRoundsUp(rounding) && result * result < a ? 1 : 0);
}
}
function log2(uint256 value) internal pure returns (uint256) {
uint256 result = 0;
unchecked {
if (value >> 128 > 0) {
value >>= 128;
result += 128;
}
if (value >> 64 > 0) {
value >>= 64;
result += 64;
}
if (value >> 32 > 0) {
value >>= 32;
result += 32;
}
if (value >> 16 > 0) {
value >>= 16;
result += 16;
}
if (value >> 8 > 0) {
value >>= 8;
result += 8;
}
if (value >> 4 > 0) {
value >>= 4;
result += 4;
}
if (value >> 2 > 0) {
value >>= 2;
result += 2;
}
if (value >> 1 > 0) {
result += 1;
}
}
return result;
}
function log2(uint256 value, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = log2(value);
return result + (unsignedRoundsUp(rounding) && 1 << result < value ? 1 : 0);
}
}
function log10(uint256 value) internal pure returns (uint256) {
uint256 result = 0;
unchecked {
if (value >= 10 ** 64) {
value /= 10 ** 64;
result += 64;
}
if (value >= 10 ** 32) {
value /= 10 ** 32;
result += 32;
}
if (value >= 10 ** 16) {
value /= 10 ** 16;
result += 16;
}
if (value >= 10 ** 8) {
value /= 10 ** 8;
result += 8;
}
if (value >= 10 ** 4) {
value /= 10 ** 4;
result += 4;
}
if (value >= 10 ** 2) {
value /= 10 ** 2;
result += 2;
}
if (value >= 10 ** 1) {
result += 1;
}
}
return result;
}
function log10(uint256 value, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = log10(value);
return result + (unsignedRoundsUp(rounding) && 10 ** result < value ? 1 : 0);
}
}
function log256(uint256 value) internal pure returns (uint256) {
uint256 result = 0;
unchecked {
if (value >> 128 > 0) {
value >>= 128;
result += 16;
}
if (value >> 64 > 0) {
value >>= 64;
result += 8;
}
if (value >> 32 > 0) {
value >>= 32;
result += 4;
}
if (value >> 16 > 0) {
value >>= 16;
result += 2;
}
if (value >> 8 > 0) {
result += 1;
}
}
return result;
}
function log256(uint256 value, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = log256(value);
return result + (unsignedRoundsUp(rounding) && 1 << (result << 3) < value ? 1 : 0);
}
}
function unsignedRoundsUp(Rounding rounding) internal pure returns (bool) {
return uint8(rounding) % 2 == 1;
}
}
文件 11 的 19:MessageHashUtils.sol
pragma solidity ^0.8.20;
import {Strings} from "../Strings.sol";
library MessageHashUtils {
function toEthSignedMessageHash(bytes32 messageHash) internal pure returns (bytes32 digest) {
assembly {
mstore(0x00, "\x19Ethereum Signed Message:\n32")
mstore(0x1c, messageHash)
digest := keccak256(0x00, 0x3c)
}
}
function toEthSignedMessageHash(bytes memory message) internal pure returns (bytes32) {
return
keccak256(bytes.concat("\x19Ethereum Signed Message:\n", bytes(Strings.toString(message.length)), message));
}
function toDataWithIntendedValidatorHash(address validator, bytes memory data) internal pure returns (bytes32) {
return keccak256(abi.encodePacked(hex"19_00", validator, data));
}
function toTypedDataHash(bytes32 domainSeparator, bytes32 structHash) internal pure returns (bytes32 digest) {
assembly {
let ptr := mload(0x40)
mstore(ptr, hex"19_01")
mstore(add(ptr, 0x02), domainSeparator)
mstore(add(ptr, 0x22), structHash)
digest := keccak256(ptr, 0x42)
}
}
}
文件 12 的 19:Ownable.sol
pragma solidity ^0.8.20;
import {Context} from "../utils/Context.sol";
abstract contract Ownable is Context {
address private _owner;
error OwnableUnauthorizedAccount(address account);
error OwnableInvalidOwner(address owner);
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
constructor(address initialOwner) {
if (initialOwner == address(0)) {
revert OwnableInvalidOwner(address(0));
}
_transferOwnership(initialOwner);
}
modifier onlyOwner() {
_checkOwner();
_;
}
function owner() public view virtual returns (address) {
return _owner;
}
function _checkOwner() internal view virtual {
if (owner() != _msgSender()) {
revert OwnableUnauthorizedAccount(_msgSender());
}
}
function renounceOwnership() public virtual onlyOwner {
_transferOwnership(address(0));
}
function transferOwnership(address newOwner) public virtual onlyOwner {
if (newOwner == address(0)) {
revert OwnableInvalidOwner(address(0));
}
_transferOwnership(newOwner);
}
function _transferOwnership(address newOwner) internal virtual {
address oldOwner = _owner;
_owner = newOwner;
emit OwnershipTransferred(oldOwner, newOwner);
}
}
文件 13 的 19:PreSaleDop.sol
pragma solidity 0.8.22;
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import {ECDSA} from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol";
import {MessageHashUtils} from "@openzeppelin/contracts/utils/cryptography/MessageHashUtils.sol";
import {Address} from "@openzeppelin/contracts/utils/Address.sol";
import {ReentrancyGuard} from "@openzeppelin/contracts/utils/ReentrancyGuard.sol";
import {Rounds, Ownable} from "./Rounds.sol";
import {IPreSaleDop} from "./IPreSaleDop.sol";
import "./Common.sol";
contract PreSaleDop is IPreSaleDop, Rounds, ReentrancyGuard {
using SafeERC20 for IERC20;
using Address for address payable;
error Blacklisted();
error BuyNotEnable();
error DeadlineExpired();
error InvalidSignature();
error UnexpectedPriceDifference();
error ZeroValue();
error PriceNotFound();
error OnlyClaims();
error InvalidInvestment();
error CodeSyncIssue();
bool public buyEnable = true;
address public signerWallet;
address public claimsContract;
address public fundsWallet;
uint256[] public nftPricing;
mapping(address => mapping(uint32 => uint256)) public claims;
mapping(address => bool) public blacklistAddress;
mapping(address => mapping(uint32 => ClaimNFT[])) public claimNFT;
struct ClaimNFT {
uint256[] nftAmounts;
uint256 roundPrice;
}
struct TokenInfo {
uint256 latestPrice;
uint8 normalizationFactorForToken;
uint8 normalizationFactorForNFT;
}
event InvestedWithETH(
address indexed by,
string code,
uint256 amountInvestedEth,
uint32 indexed round,
uint256 indexed roundPrice,
uint256 dopPurchased
);
event InvestedWithToken(
IERC20 indexed token,
uint256 tokenPrice,
address indexed by,
string code,
uint256 amountInvested,
uint256 dopPurchased,
uint32 indexed round
);
event InvestedWithETHForNFT(
address indexed by,
string code,
uint256 amountInEth,
uint256 ethPrice,
uint32 indexed round,
uint256 roundPrice,
uint256[] nftAmounts
);
event InvestedWithTokenForNFT(
IERC20 indexed token,
uint256 tokenPrice,
address indexed by,
string code,
uint256 amountInvested,
uint32 indexed round,
uint256 roundPrice,
uint256[] nftAmounts
);
event InvestedWithClaimAmount(
address indexed by,
uint256 amount,
IERC20 token,
uint32 indexed round,
uint256 indexed tokenPrice,
uint256 dopPurchased
);
event SignerUpdated(address oldSigner, address newSigner);
event FundsWalletUpdated(address oldAddress, address newAddress);
event BlacklistUpdated(address which, bool accessNow);
event BuyEnableUpdated(bool oldAccess, bool newAccess);
event PricingUpdated(uint256 oldPrice, uint256 newPrice);
modifier checkAddressZero(address which) {
if (which == address(0)) {
revert ZeroAddress();
}
_;
}
modifier canBuy() {
if (!buyEnable) {
revert BuyNotEnable();
}
_;
}
constructor(
address fundsWalletAddress,
address signerAddress,
address claimsContractAddress,
address owner,
uint32 lastRound,
uint256[] memory nftPrices
) Rounds(lastRound) Ownable(owner) {
if (
fundsWalletAddress == address(0) ||
signerAddress == address(0) ||
claimsContractAddress == address(0) ||
owner == address(0)
) {
revert ZeroAddress();
}
fundsWallet = fundsWalletAddress;
signerWallet = signerAddress;
claimsContract = claimsContractAddress;
if (nftPrices.length == 0) {
revert ZeroLengthArray();
}
for (uint256 i = 0; i < nftPrices.length; ++i) {
_checkValue(nftPrices[i]);
}
nftPricing = nftPrices;
}
function enableBuy(bool enabled) external onlyOwner {
if (buyEnable == enabled) {
revert IdenticalValue();
}
emit BuyEnableUpdated({oldAccess: buyEnable, newAccess: enabled});
buyEnable = enabled;
}
function changeSigner(
address newSigner
) external checkAddressZero(newSigner) onlyOwner {
address oldSigner = signerWallet;
if (oldSigner == newSigner) {
revert IdenticalValue();
}
emit SignerUpdated({oldSigner: oldSigner, newSigner: newSigner});
signerWallet = newSigner;
}
function changeFundsWallet(
address newFundsWallet
) external checkAddressZero(newFundsWallet) onlyOwner {
address oldWallet = fundsWallet;
if (oldWallet == newFundsWallet) {
revert IdenticalValue();
}
emit FundsWalletUpdated({
oldAddress: oldWallet,
newAddress: newFundsWallet
});
fundsWallet = newFundsWallet;
}
function updateBlackListedUser(
address which,
bool access
) external checkAddressZero(which) onlyOwner {
bool oldAccess = blacklistAddress[which];
if (oldAccess == access) {
revert IdenticalValue();
}
emit BlacklistUpdated({which: which, accessNow: access});
blacklistAddress[which] = access;
}
function purchaseTokenWithEth(
string memory code,
uint32 round,
uint256 deadline,
uint256 minAmountDop,
uint8 v,
bytes32 r,
bytes32 s
) external payable canBuy {
_validatePurchaseWithEth(msg.value, round, deadline, code, v, r, s);
uint256 roundPrice = _getRoundPriceForToken(round, ETH);
TokenInfo memory tokenInfo = getLatestPrice(ETH);
if (tokenInfo.latestPrice == 0) {
revert PriceNotFound();
}
uint256 toReturn = _calculateDop(
msg.value,
tokenInfo.latestPrice,
tokenInfo.normalizationFactorForToken,
roundPrice
);
if (toReturn < minAmountDop) {
revert UnexpectedPriceDifference();
}
claims[msg.sender][round] += toReturn;
payable(fundsWallet).sendValue(msg.value);
emit InvestedWithETH({
by: msg.sender,
code: code,
amountInvestedEth: msg.value,
round: round,
roundPrice: roundPrice,
dopPurchased: toReturn
});
}
function purchaseTokenWithToken(
IERC20 token,
uint8 referenceNormalizationFactor,
uint256 referenceTokenPrice,
uint256 investment,
uint256 minAmountDop,
string memory code,
uint32 round,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) external canBuy nonReentrant {
_validatePurchaseWithToken(
token,
round,
deadline,
code,
referenceTokenPrice,
referenceNormalizationFactor,
v,
r,
s
);
_checkValue(investment);
uint256 roundPrice = _getRoundPriceForToken(round, token);
(uint256 latestPrice, uint256 normalizationFactor) = _validatePrice(
token,
referenceTokenPrice,
referenceNormalizationFactor
);
uint256 toReturn = _calculateDop(
investment,
latestPrice,
normalizationFactor,
roundPrice
);
if (toReturn < minAmountDop) {
revert UnexpectedPriceDifference();
}
claims[msg.sender][round] += toReturn;
token.safeTransferFrom(msg.sender, fundsWallet, investment);
emit InvestedWithToken({
token: token,
tokenPrice: latestPrice,
by: msg.sender,
code: code,
amountInvested: investment,
dopPurchased: toReturn,
round: round
});
}
function purchaseNFTWithEth(
string memory code,
uint32 round,
uint256[] calldata nftAmounts,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) external payable canBuy nonReentrant {
uint256[] memory nftPrices = nftPricing;
_validateArrays(nftAmounts.length, nftPrices.length);
_validatePurchaseWithEth(msg.value, round, deadline, code, v, r, s);
TokenInfo memory tokenInfo = getLatestPrice(ETH);
if (tokenInfo.latestPrice == 0) {
revert PriceNotFound();
}
(uint256 value, uint256 roundPrice) = _processPurchaseNFT(
ETH,
tokenInfo.latestPrice,
tokenInfo.normalizationFactorForNFT,
round,
nftAmounts,
nftPrices
);
if (msg.value < value) {
revert InvalidInvestment();
}
_checkValue(value);
uint256 amountUnused = msg.value - value;
if (amountUnused > 0) {
payable(msg.sender).sendValue(amountUnused);
}
payable(fundsWallet).sendValue(value);
emit InvestedWithETHForNFT({
by: msg.sender,
code: code,
amountInEth: value,
ethPrice: tokenInfo.latestPrice,
round: round,
roundPrice: roundPrice,
nftAmounts: nftAmounts
});
}
function purchaseNFTWithToken(
IERC20 token,
uint256 referenceTokenPrice,
uint8 referenceNormalizationFactor,
string memory code,
uint32 round,
uint256[] calldata nftAmounts,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) external canBuy nonReentrant {
uint256[] memory nftPrices = nftPricing;
_validateArrays(nftAmounts.length, nftPrices.length);
_validatePurchaseWithToken(
token,
round,
deadline,
code,
referenceTokenPrice,
referenceNormalizationFactor,
v,
r,
s
);
TokenInfo memory tokenInfo = getLatestPrice(token);
if (tokenInfo.latestPrice != 0) {
if (referenceTokenPrice != 0 || referenceNormalizationFactor != 0) {
revert CodeSyncIssue();
}
}
if (tokenInfo.latestPrice == 0) {
if (referenceTokenPrice == 0 || referenceNormalizationFactor == 0) {
revert ZeroValue();
}
tokenInfo.latestPrice = referenceTokenPrice;
tokenInfo.normalizationFactorForNFT = referenceNormalizationFactor;
}
(uint256 value, uint256 roundPrice) = _processPurchaseNFT(
token,
tokenInfo.latestPrice,
tokenInfo.normalizationFactorForNFT,
round,
nftAmounts,
nftPrices
);
_checkValue(value);
token.safeTransferFrom(msg.sender, fundsWallet, value);
emit InvestedWithTokenForNFT({
token: token,
tokenPrice: tokenInfo.latestPrice,
by: msg.sender,
code: code,
amountInvested: value,
round: round,
roundPrice: roundPrice,
nftAmounts: nftAmounts
});
}
function purchaseWithClaim(
IERC20 token,
uint256 referenceTokenPrice,
uint8 referenceNormalizationFactor,
uint256 amount,
uint256 minAmountDop,
address recipient,
uint32 round
) external payable canBuy nonReentrant {
if (msg.sender != claimsContract) {
revert OnlyClaims();
}
_checkBlacklist(recipient);
if (!allowedTokens[round][token].access) {
revert TokenDisallowed();
}
uint256 roundPrice = _getRoundPriceForToken(round, token);
(uint256 latestPrice, uint256 normalizationFactor) = _validatePrice(
token,
referenceTokenPrice,
referenceNormalizationFactor
);
uint256 toReturn = _calculateDop(
amount,
latestPrice,
normalizationFactor,
roundPrice
);
if (toReturn < minAmountDop) {
revert UnexpectedPriceDifference();
}
claims[recipient][round] += toReturn;
if (token == ETH) {
payable(fundsWallet).sendValue(msg.value);
} else {
token.safeTransferFrom(claimsContract, fundsWallet, amount);
}
emit InvestedWithClaimAmount({
by: recipient,
amount: amount,
token: token,
round: round,
tokenPrice: latestPrice,
dopPurchased: toReturn
});
}
function updatePricing(uint256[] memory newPrices) external onlyOwner {
uint256[] memory oldPrices = nftPricing;
if (newPrices.length != oldPrices.length) {
revert ArrayLengthMismatch();
}
for (uint256 i = 0; i < newPrices.length; ++i) {
uint256 newPrice = newPrices[i];
_checkValue(newPrice);
emit PricingUpdated({oldPrice: oldPrices[i], newPrice: newPrice});
}
nftPricing = newPrices;
}
function verifyPurchaseWithClaim(
address recipient,
uint32 round,
uint256 deadline,
uint256[] calldata tokenPrices,
uint8[] calldata normalizationFactors,
IERC20[] calldata tokens,
uint256[] calldata amounts,
uint8 v,
bytes32 r,
bytes32 s
) external view {
if (msg.sender != claimsContract) {
revert OnlyClaims();
}
bytes32 encodedMessageHash = keccak256(
abi.encodePacked(
recipient,
round,
tokenPrices,
normalizationFactors,
deadline,
tokens,
amounts
)
);
_verifyMessage(encodedMessageHash, v, r, s);
}
function getLatestPrice(
IERC20 token
) public view returns (TokenInfo memory) {
PriceFeedData memory data = tokenData[token];
TokenInfo memory tokenInfo;
if (address(data.priceFeed) == address(0)) {
return tokenInfo;
}
(
,
int price ,
,
,
) = data.priceFeed.latestRoundData();
tokenInfo = TokenInfo({
latestPrice: uint256(price),
normalizationFactorForToken: data.normalizationFactorForToken,
normalizationFactorForNFT: data.normalizationFactorForNFT
});
return tokenInfo;
}
function _checkValue(uint256 value) private pure {
if (value == 0) {
revert ZeroValue();
}
}
function _validatePurchase(
uint32 round,
uint256 deadline,
IERC20 token
) private view {
if (block.timestamp > deadline) {
revert DeadlineExpired();
}
_checkBlacklist(msg.sender);
if (!allowedTokens[round][token].access) {
revert TokenDisallowed();
}
_verifyInRound(round);
}
function _verifyCode(
string memory code,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) private view {
bytes32 encodedMessageHash = keccak256(
abi.encodePacked(msg.sender, code, deadline)
);
_verifyMessage(encodedMessageHash, v, r, s);
}
function _verifyCodeWithPrice(
string memory code,
uint256 deadline,
uint256 referenceTokenPrice,
IERC20 token,
uint256 normalizationFactor,
uint8 v,
bytes32 r,
bytes32 s
) private view {
bytes32 encodedMessageHash = keccak256(
abi.encodePacked(
msg.sender,
code,
referenceTokenPrice,
deadline,
token,
normalizationFactor
)
);
_verifyMessage(encodedMessageHash, v, r, s);
}
function _verifyMessage(
bytes32 encodedMessageHash,
uint8 v,
bytes32 r,
bytes32 s
) private view {
if (
signerWallet !=
ECDSA.recover(
MessageHashUtils.toEthSignedMessageHash(encodedMessageHash),
v,
r,
s
)
) {
revert InvalidSignature();
}
}
function _processPurchaseNFT(
IERC20 token,
uint256 price,
uint256 normalizationFactor,
uint32 round,
uint256[] calldata nftAmounts,
uint256[] memory nftPrices
) private returns (uint256, uint256) {
uint256 value = 0;
for (uint256 i = 0; i < nftPrices.length; ++i) {
value +=
(nftAmounts[i] * nftPrices[i] * (10 ** (normalizationFactor))) /
price;
}
uint256 roundPrice = _getRoundPriceForToken(round, token);
ClaimNFT memory amounts = ClaimNFT({
nftAmounts: nftAmounts,
roundPrice: roundPrice
});
claimNFT[msg.sender][round].push(amounts);
return (value, roundPrice);
}
function _checkBlacklist(address which) private view {
if (blacklistAddress[which]) {
revert Blacklisted();
}
}
function _validatePurchaseWithEth(
uint256 amount,
uint32 round,
uint256 deadline,
string memory code,
uint8 v,
bytes32 r,
bytes32 s
) private view {
_checkValue(amount);
_validatePurchase(round, deadline, ETH);
_verifyCode(code, deadline, v, r, s);
}
function _validatePurchaseWithToken(
IERC20 token,
uint32 round,
uint256 deadline,
string memory code,
uint256 referenceTokenPrice,
uint256 normalizationFactor,
uint8 v,
bytes32 r,
bytes32 s
) private view {
_validatePurchase(round, deadline, token);
_verifyCodeWithPrice(
code,
deadline,
referenceTokenPrice,
token,
normalizationFactor,
v,
r,
s
);
}
function _getRoundPriceForToken(
uint32 round,
IERC20 token
) private view returns (uint256) {
uint256 customPrice = allowedTokens[round][token].customPrice;
uint256 roundPrice = customPrice > 0
? customPrice
: rounds[round].price;
return roundPrice;
}
function _calculateDop(
uint256 investment,
uint256 referenceTokenPrice,
uint256 normalizationFactor,
uint256 roundPrice
) private pure returns (uint256) {
uint256 toReturn = (investment *
referenceTokenPrice *
(10 ** normalizationFactor)) / roundPrice;
return toReturn;
}
function _validatePrice(
IERC20 token,
uint256 referenceTokenPrice,
uint8 referenceNormalizationFactor
) private view returns (uint256, uint256) {
TokenInfo memory tokenInfo = getLatestPrice(token);
if (tokenInfo.latestPrice != 0) {
if (referenceTokenPrice != 0 || referenceNormalizationFactor != 0) {
revert CodeSyncIssue();
}
}
if (tokenInfo.latestPrice == 0) {
if (referenceTokenPrice == 0 || referenceNormalizationFactor == 0) {
revert ZeroValue();
}
tokenInfo.latestPrice = referenceTokenPrice;
tokenInfo
.normalizationFactorForToken = referenceNormalizationFactor;
}
return (tokenInfo.latestPrice, tokenInfo.normalizationFactorForToken);
}
}
文件 14 的 19:ReentrancyGuard.sol
pragma solidity ^0.8.20;
abstract contract ReentrancyGuard {
uint256 private constant NOT_ENTERED = 1;
uint256 private constant ENTERED = 2;
uint256 private _status;
error ReentrancyGuardReentrantCall();
constructor() {
_status = NOT_ENTERED;
}
modifier nonReentrant() {
_nonReentrantBefore();
_;
_nonReentrantAfter();
}
function _nonReentrantBefore() private {
if (_status == ENTERED) {
revert ReentrancyGuardReentrantCall();
}
_status = ENTERED;
}
function _nonReentrantAfter() private {
_status = NOT_ENTERED;
}
function _reentrancyGuardEntered() internal view returns (bool) {
return _status == ENTERED;
}
}
文件 15 的 19:Rounds.sol
pragma solidity 0.8.22;
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol";
import {TokensRegistry} from "./TokensRegistry.sol";
import {IRounds} from "./IRounds.sol";
import {ZeroAddress, ArrayLengthMismatch, ZeroLengthArray} from "./Common.sol";
abstract contract Rounds is IRounds, Ownable, TokensRegistry {
error RoundNotStarted();
error RoundEnded();
error IncorrectRound();
error PriceLessThanOldRound();
error InvalidStartTime();
error InvalidEndTime();
error PriceInvalid();
error IncorrectStartTime();
error IncorrectEndTime();
error PriceGreaterThanNextRound();
error TokenDisallowed();
uint32 internal immutable _startRound;
uint32 internal _roundIndex;
mapping(uint32 => mapping(IERC20 => AllowedToken)) public allowedTokens;
mapping(uint32 => RoundData) public rounds;
struct AllowedToken {
bool access;
uint256 customPrice;
}
struct RoundData {
uint256 startTime;
uint256 endTime;
uint256 price;
}
event RoundCreated(uint32 indexed newRound, RoundData roundData);
event RoundUpdated(uint32 indexed round, RoundData roundData);
event TokensAccessUpdated(
uint32 indexed round,
IERC20 indexed token,
bool indexed access,
uint256 customPrice
);
constructor(uint32 lastRound) {
_startRound = lastRound;
_roundIndex = lastRound;
}
function createNewRound(
uint256 startTime,
uint256 endTime,
uint256 price
) external onlyOwner {
RoundData memory prevRoundData = rounds[_roundIndex];
uint32 newRound = ++_roundIndex;
if (price < prevRoundData.price) {
revert PriceLessThanOldRound();
}
if (startTime < prevRoundData.endTime) {
revert InvalidStartTime();
}
_verifyRound(startTime, endTime, price);
prevRoundData = RoundData({
startTime: startTime,
endTime: endTime,
price: price
});
rounds[newRound] = prevRoundData;
emit RoundCreated({newRound: newRound, roundData: prevRoundData});
}
function updateAllowedTokens(
uint32 round,
IERC20[] calldata tokens,
bool[] memory accesses,
uint256[] memory customPrices
) external onlyOwner {
if (tokens.length == 0) {
revert ZeroLengthArray();
}
if (
tokens.length != accesses.length ||
accesses.length != customPrices.length
) {
revert ArrayLengthMismatch();
}
mapping(IERC20 => AllowedToken) storage selectedRound = allowedTokens[
round
];
for (uint256 i = 0; i < tokens.length; ++i) {
IERC20 token = tokens[i];
if (address(token) == address(0)) {
revert ZeroAddress();
}
AllowedToken memory allowedToken = AllowedToken({
access: accesses[i],
customPrice: customPrices[i]
});
selectedRound[token] = allowedToken;
emit TokensAccessUpdated({
round: round,
token: token,
access: allowedToken.access,
customPrice: allowedToken.customPrice
});
}
}
function updateRound(
uint32 round,
uint256 startTime,
uint256 endTime,
uint256 price
) external onlyOwner {
if (round <= _startRound || round > _roundIndex) {
revert IncorrectRound();
}
RoundData memory previousRound = rounds[round - 1];
RoundData memory nextRound = rounds[round + 1];
if (startTime < previousRound.endTime) {
revert IncorrectStartTime();
}
if (round != _roundIndex && endTime > nextRound.startTime) {
revert IncorrectEndTime();
}
if (price < previousRound.price) {
revert PriceLessThanOldRound();
}
if (round != _roundIndex && price > nextRound.price) {
revert PriceGreaterThanNextRound();
}
_verifyRound(startTime, endTime, price);
rounds[round] = RoundData({
startTime: startTime,
endTime: endTime,
price: price
});
emit RoundUpdated({round: round, roundData: rounds[round]});
}
function getRoundCount() external view returns (uint32) {
return _roundIndex;
}
function _validateArrays(
uint256 firstLength,
uint256 secondLength
) internal pure {
if (firstLength == 0) {
revert ZeroLengthArray();
}
if (firstLength != secondLength) {
revert ArrayLengthMismatch();
}
}
function _verifyInRound(uint32 round) internal view {
RoundData memory dataRound = rounds[round];
if (block.timestamp < dataRound.startTime) {
revert RoundNotStarted();
}
if (block.timestamp >= dataRound.endTime) {
revert RoundEnded();
}
}
function _verifyRound(
uint256 startTime,
uint256 endTime,
uint256 price
) internal view {
if (startTime < block.timestamp) {
revert InvalidStartTime();
}
if (endTime <= startTime) {
revert InvalidEndTime();
}
if (price == 0) {
revert PriceInvalid();
}
}
}
文件 16 的 19:SafeERC20.sol
pragma solidity ^0.8.20;
import {IERC20} from "../IERC20.sol";
import {IERC20Permit} from "../extensions/IERC20Permit.sol";
import {Address} from "../../../utils/Address.sol";
library SafeERC20 {
using Address for address;
error SafeERC20FailedOperation(address token);
error SafeERC20FailedDecreaseAllowance(address spender, uint256 currentAllowance, uint256 requestedDecrease);
function safeTransfer(IERC20 token, address to, uint256 value) internal {
_callOptionalReturn(token, abi.encodeCall(token.transfer, (to, value)));
}
function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal {
_callOptionalReturn(token, abi.encodeCall(token.transferFrom, (from, to, value)));
}
function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal {
uint256 oldAllowance = token.allowance(address(this), spender);
forceApprove(token, spender, oldAllowance + value);
}
function safeDecreaseAllowance(IERC20 token, address spender, uint256 requestedDecrease) internal {
unchecked {
uint256 currentAllowance = token.allowance(address(this), spender);
if (currentAllowance < requestedDecrease) {
revert SafeERC20FailedDecreaseAllowance(spender, currentAllowance, requestedDecrease);
}
forceApprove(token, spender, currentAllowance - requestedDecrease);
}
}
function forceApprove(IERC20 token, address spender, uint256 value) internal {
bytes memory approvalCall = abi.encodeCall(token.approve, (spender, value));
if (!_callOptionalReturnBool(token, approvalCall)) {
_callOptionalReturn(token, abi.encodeCall(token.approve, (spender, 0)));
_callOptionalReturn(token, approvalCall);
}
}
function _callOptionalReturn(IERC20 token, bytes memory data) private {
bytes memory returndata = address(token).functionCall(data);
if (returndata.length != 0 && !abi.decode(returndata, (bool))) {
revert SafeERC20FailedOperation(address(token));
}
}
function _callOptionalReturnBool(IERC20 token, bytes memory data) private returns (bool) {
(bool success, bytes memory returndata) = address(token).call(data);
return success && (returndata.length == 0 || abi.decode(returndata, (bool))) && address(token).code.length > 0;
}
}
文件 17 的 19:SignedMath.sol
pragma solidity ^0.8.20;
library SignedMath {
function max(int256 a, int256 b) internal pure returns (int256) {
return a > b ? a : b;
}
function min(int256 a, int256 b) internal pure returns (int256) {
return a < b ? a : b;
}
function average(int256 a, int256 b) internal pure returns (int256) {
int256 x = (a & b) + ((a ^ b) >> 1);
return x + (int256(uint256(x) >> 255) & (a ^ b));
}
function abs(int256 n) internal pure returns (uint256) {
unchecked {
return uint256(n >= 0 ? n : -n);
}
}
}
文件 18 的 19:Strings.sol
pragma solidity ^0.8.20;
import {Math} from "./math/Math.sol";
import {SignedMath} from "./math/SignedMath.sol";
library Strings {
bytes16 private constant HEX_DIGITS = "0123456789abcdef";
uint8 private constant ADDRESS_LENGTH = 20;
error StringsInsufficientHexLength(uint256 value, uint256 length);
function toString(uint256 value) internal pure returns (string memory) {
unchecked {
uint256 length = Math.log10(value) + 1;
string memory buffer = new string(length);
uint256 ptr;
assembly {
ptr := add(buffer, add(32, length))
}
while (true) {
ptr--;
assembly {
mstore8(ptr, byte(mod(value, 10), HEX_DIGITS))
}
value /= 10;
if (value == 0) break;
}
return buffer;
}
}
function toStringSigned(int256 value) internal pure returns (string memory) {
return string.concat(value < 0 ? "-" : "", toString(SignedMath.abs(value)));
}
function toHexString(uint256 value) internal pure returns (string memory) {
unchecked {
return toHexString(value, Math.log256(value) + 1);
}
}
function toHexString(uint256 value, uint256 length) internal pure returns (string memory) {
uint256 localValue = value;
bytes memory buffer = new bytes(2 * length + 2);
buffer[0] = "0";
buffer[1] = "x";
for (uint256 i = 2 * length + 1; i > 1; --i) {
buffer[i] = HEX_DIGITS[localValue & 0xf];
localValue >>= 4;
}
if (localValue != 0) {
revert StringsInsufficientHexLength(value, length);
}
return string(buffer);
}
function toHexString(address addr) internal pure returns (string memory) {
return toHexString(uint256(uint160(addr)), ADDRESS_LENGTH);
}
function equal(string memory a, string memory b) internal pure returns (bool) {
return bytes(a).length == bytes(b).length && keccak256(bytes(a)) == keccak256(bytes(b));
}
}
文件 19 的 19:TokensRegistry.sol
pragma solidity 0.8.22;
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol";
import {AggregatorV3Interface} from "@chainlink/contracts/src/v0.8/interfaces/AggregatorV3Interface.sol";
import {ZeroAddress, ArrayLengthMismatch, ZeroLengthArray, IdenticalValue} from "./Common.sol";
abstract contract TokensRegistry is Ownable {
uint256 internal constant NORMALIZATION_FACTOR_DOP_USDT = 1e30;
mapping(IERC20 => PriceFeedData) public tokenData;
event TokenDataAdded(IERC20 token, AggregatorV3Interface priceFeed);
struct PriceFeedData {
AggregatorV3Interface priceFeed;
uint8 normalizationFactorForToken;
uint8 normalizationFactorForNFT;
}
function setTokenPriceFeed(
IERC20[] calldata tokens,
PriceFeedData[] calldata priceFeedData
) external onlyOwner {
if (tokens.length == 0) {
revert ZeroLengthArray();
}
if (tokens.length != priceFeedData.length) {
revert ArrayLengthMismatch();
}
for (uint256 i = 0; i < tokens.length; ++i) {
PriceFeedData memory data = priceFeedData[i];
IERC20 token = tokens[i];
PriceFeedData memory currentPriceFeedData = tokenData[token];
if (
address(token) == address(0) ||
address(data.priceFeed) == address(0)
) {
revert ZeroAddress();
}
if (
currentPriceFeedData.priceFeed == data.priceFeed &&
currentPriceFeedData.normalizationFactorForToken ==
data.normalizationFactorForToken &&
currentPriceFeedData.normalizationFactorForNFT ==
data.normalizationFactorForNFT
) {
revert IdenticalValue();
}
emit TokenDataAdded({token: token, priceFeed: data.priceFeed});
tokenData[token] = data;
}
}
}
{
"compilationTarget": {
"contracts/PreSaleDop.sol": "PreSaleDop"
},
"evmVersion": "shanghai",
"libraries": {},
"metadata": {
"bytecodeHash": "ipfs"
},
"optimizer": {
"enabled": true,
"runs": 1000000
},
"remappings": [],
"viaIR": true
}
[{"inputs":[{"internalType":"address","name":"fundsWalletAddress","type":"address"},{"internalType":"address","name":"signerAddress","type":"address"},{"internalType":"address","name":"claimsContractAddress","type":"address"},{"internalType":"address","name":"owner","type":"address"},{"internalType":"uint32","name":"lastRound","type":"uint32"},{"internalType":"uint256[]","name":"nftPrices","type":"uint256[]"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[{"internalType":"address","name":"target","type":"address"}],"name":"AddressEmptyCode","type":"error"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"AddressInsufficientBalance","type":"error"},{"inputs":[],"name":"ArrayLengthMismatch","type":"error"},{"inputs":[],"name":"Blacklisted","type":"error"},{"inputs":[],"name":"BuyNotEnable","type":"error"},{"inputs":[],"name":"CodeSyncIssue","type":"error"},{"inputs":[],"name":"DeadlineExpired","type":"error"},{"inputs":[],"name":"ECDSAInvalidSignature","type":"error"},{"inputs":[{"internalType":"uint256","name":"length","type":"uint256"}],"name":"ECDSAInvalidSignatureLength","type":"error"},{"inputs":[{"internalType":"bytes32","name":"s","type":"bytes32"}],"name":"ECDSAInvalidSignatureS","type":"error"},{"inputs":[],"name":"FailedInnerCall","type":"error"},{"inputs":[],"name":"IdenticalValue","type":"error"},{"inputs":[],"name":"IncorrectEndTime","type":"error"},{"inputs":[],"name":"IncorrectRound","type":"error"},{"inputs":[],"name":"IncorrectStartTime","type":"error"},{"inputs":[],"name":"InvalidEndTime","type":"error"},{"inputs":[],"name":"InvalidInvestment","type":"error"},{"inputs":[],"name":"InvalidSignature","type":"error"},{"inputs":[],"name":"InvalidStartTime","type":"error"},{"inputs":[],"name":"OnlyClaims","type":"error"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"OwnableInvalidOwner","type":"error"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"OwnableUnauthorizedAccount","type":"error"},{"inputs":[],"name":"PriceGreaterThanNextRound","type":"error"},{"inputs":[],"name":"PriceInvalid","type":"error"},{"inputs":[],"name":"PriceLessThanOldRound","type":"error"},{"inputs":[],"name":"PriceNotFound","type":"error"},{"inputs":[],"name":"ReentrancyGuardReentrantCall","type":"error"},{"inputs":[],"name":"RoundEnded","type":"error"},{"inputs":[],"name":"RoundNotStarted","type":"error"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"SafeERC20FailedOperation","type":"error"},{"inputs":[],"name":"TokenDisallowed","type":"error"},{"inputs":[],"name":"UnexpectedPriceDifference","type":"error"},{"inputs":[],"name":"ZeroAddress","type":"error"},{"inputs":[],"name":"ZeroLengthArray","type":"error"},{"inputs":[],"name":"ZeroValue","type":"error"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"which","type":"address"},{"indexed":false,"internalType":"bool","name":"accessNow","type":"bool"}],"name":"BlacklistUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bool","name":"oldAccess","type":"bool"},{"indexed":false,"internalType":"bool","name":"newAccess","type":"bool"}],"name":"BuyEnableUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"oldAddress","type":"address"},{"indexed":false,"internalType":"address","name":"newAddress","type":"address"}],"name":"FundsWalletUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"by","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"contract IERC20","name":"token","type":"address"},{"indexed":true,"internalType":"uint32","name":"round","type":"uint32"},{"indexed":true,"internalType":"uint256","name":"tokenPrice","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"dopPurchased","type":"uint256"}],"name":"InvestedWithClaimAmount","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"by","type":"address"},{"indexed":false,"internalType":"string","name":"code","type":"string"},{"indexed":false,"internalType":"uint256","name":"amountInvestedEth","type":"uint256"},{"indexed":true,"internalType":"uint32","name":"round","type":"uint32"},{"indexed":true,"internalType":"uint256","name":"roundPrice","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"dopPurchased","type":"uint256"}],"name":"InvestedWithETH","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"by","type":"address"},{"indexed":false,"internalType":"string","name":"code","type":"string"},{"indexed":false,"internalType":"uint256","name":"amountInEth","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"ethPrice","type":"uint256"},{"indexed":true,"internalType":"uint32","name":"round","type":"uint32"},{"indexed":false,"internalType":"uint256","name":"roundPrice","type":"uint256"},{"indexed":false,"internalType":"uint256[]","name":"nftAmounts","type":"uint256[]"}],"name":"InvestedWithETHForNFT","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"contract IERC20","name":"token","type":"address"},{"indexed":false,"internalType":"uint256","name":"tokenPrice","type":"uint256"},{"indexed":true,"internalType":"address","name":"by","type":"address"},{"indexed":false,"internalType":"string","name":"code","type":"string"},{"indexed":false,"internalType":"uint256","name":"amountInvested","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"dopPurchased","type":"uint256"},{"indexed":true,"internalType":"uint32","name":"round","type":"uint32"}],"name":"InvestedWithToken","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"contract IERC20","name":"token","type":"address"},{"indexed":false,"internalType":"uint256","name":"tokenPrice","type":"uint256"},{"indexed":true,"internalType":"address","name":"by","type":"address"},{"indexed":false,"internalType":"string","name":"code","type":"string"},{"indexed":false,"internalType":"uint256","name":"amountInvested","type":"uint256"},{"indexed":true,"internalType":"uint32","name":"round","type":"uint32"},{"indexed":false,"internalType":"uint256","name":"roundPrice","type":"uint256"},{"indexed":false,"internalType":"uint256[]","name":"nftAmounts","type":"uint256[]"}],"name":"InvestedWithTokenForNFT","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":"uint256","name":"oldPrice","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"newPrice","type":"uint256"}],"name":"PricingUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint32","name":"newRound","type":"uint32"},{"components":[{"internalType":"uint256","name":"startTime","type":"uint256"},{"internalType":"uint256","name":"endTime","type":"uint256"},{"internalType":"uint256","name":"price","type":"uint256"}],"indexed":false,"internalType":"struct Rounds.RoundData","name":"roundData","type":"tuple"}],"name":"RoundCreated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint32","name":"round","type":"uint32"},{"components":[{"internalType":"uint256","name":"startTime","type":"uint256"},{"internalType":"uint256","name":"endTime","type":"uint256"},{"internalType":"uint256","name":"price","type":"uint256"}],"indexed":false,"internalType":"struct Rounds.RoundData","name":"roundData","type":"tuple"}],"name":"RoundUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"oldSigner","type":"address"},{"indexed":false,"internalType":"address","name":"newSigner","type":"address"}],"name":"SignerUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"contract IERC20","name":"token","type":"address"},{"indexed":false,"internalType":"contract AggregatorV3Interface","name":"priceFeed","type":"address"}],"name":"TokenDataAdded","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint32","name":"round","type":"uint32"},{"indexed":true,"internalType":"contract IERC20","name":"token","type":"address"},{"indexed":true,"internalType":"bool","name":"access","type":"bool"},{"indexed":false,"internalType":"uint256","name":"customPrice","type":"uint256"}],"name":"TokensAccessUpdated","type":"event"},{"inputs":[{"internalType":"uint32","name":"","type":"uint32"},{"internalType":"contract IERC20","name":"","type":"address"}],"name":"allowedTokens","outputs":[{"internalType":"bool","name":"access","type":"bool"},{"internalType":"uint256","name":"customPrice","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"blacklistAddress","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"buyEnable","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"newFundsWallet","type":"address"}],"name":"changeFundsWallet","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newSigner","type":"address"}],"name":"changeSigner","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint32","name":"","type":"uint32"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"claimNFT","outputs":[{"internalType":"uint256","name":"roundPrice","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint32","name":"","type":"uint32"}],"name":"claims","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"claimsContract","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"startTime","type":"uint256"},{"internalType":"uint256","name":"endTime","type":"uint256"},{"internalType":"uint256","name":"price","type":"uint256"}],"name":"createNewRound","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bool","name":"enabled","type":"bool"}],"name":"enableBuy","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"fundsWallet","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract IERC20","name":"token","type":"address"}],"name":"getLatestPrice","outputs":[{"components":[{"internalType":"uint256","name":"latestPrice","type":"uint256"},{"internalType":"uint8","name":"normalizationFactorForToken","type":"uint8"},{"internalType":"uint8","name":"normalizationFactorForNFT","type":"uint8"}],"internalType":"struct PreSaleDop.TokenInfo","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getRoundCount","outputs":[{"internalType":"uint32","name":"","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"nftPricing","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"code","type":"string"},{"internalType":"uint32","name":"round","type":"uint32"},{"internalType":"uint256[]","name":"nftAmounts","type":"uint256[]"},{"internalType":"uint256","name":"deadline","type":"uint256"},{"internalType":"uint8","name":"v","type":"uint8"},{"internalType":"bytes32","name":"r","type":"bytes32"},{"internalType":"bytes32","name":"s","type":"bytes32"}],"name":"purchaseNFTWithEth","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"contract IERC20","name":"token","type":"address"},{"internalType":"uint256","name":"referenceTokenPrice","type":"uint256"},{"internalType":"uint8","name":"referenceNormalizationFactor","type":"uint8"},{"internalType":"string","name":"code","type":"string"},{"internalType":"uint32","name":"round","type":"uint32"},{"internalType":"uint256[]","name":"nftAmounts","type":"uint256[]"},{"internalType":"uint256","name":"deadline","type":"uint256"},{"internalType":"uint8","name":"v","type":"uint8"},{"internalType":"bytes32","name":"r","type":"bytes32"},{"internalType":"bytes32","name":"s","type":"bytes32"}],"name":"purchaseNFTWithToken","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"code","type":"string"},{"internalType":"uint32","name":"round","type":"uint32"},{"internalType":"uint256","name":"deadline","type":"uint256"},{"internalType":"uint256","name":"minAmountDop","type":"uint256"},{"internalType":"uint8","name":"v","type":"uint8"},{"internalType":"bytes32","name":"r","type":"bytes32"},{"internalType":"bytes32","name":"s","type":"bytes32"}],"name":"purchaseTokenWithEth","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"contract IERC20","name":"token","type":"address"},{"internalType":"uint8","name":"referenceNormalizationFactor","type":"uint8"},{"internalType":"uint256","name":"referenceTokenPrice","type":"uint256"},{"internalType":"uint256","name":"investment","type":"uint256"},{"internalType":"uint256","name":"minAmountDop","type":"uint256"},{"internalType":"string","name":"code","type":"string"},{"internalType":"uint32","name":"round","type":"uint32"},{"internalType":"uint256","name":"deadline","type":"uint256"},{"internalType":"uint8","name":"v","type":"uint8"},{"internalType":"bytes32","name":"r","type":"bytes32"},{"internalType":"bytes32","name":"s","type":"bytes32"}],"name":"purchaseTokenWithToken","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract IERC20","name":"token","type":"address"},{"internalType":"uint256","name":"referenceTokenPrice","type":"uint256"},{"internalType":"uint8","name":"referenceNormalizationFactor","type":"uint8"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"minAmountDop","type":"uint256"},{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint32","name":"round","type":"uint32"}],"name":"purchaseWithClaim","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint32","name":"","type":"uint32"}],"name":"rounds","outputs":[{"internalType":"uint256","name":"startTime","type":"uint256"},{"internalType":"uint256","name":"endTime","type":"uint256"},{"internalType":"uint256","name":"price","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract IERC20[]","name":"tokens","type":"address[]"},{"components":[{"internalType":"contract AggregatorV3Interface","name":"priceFeed","type":"address"},{"internalType":"uint8","name":"normalizationFactorForToken","type":"uint8"},{"internalType":"uint8","name":"normalizationFactorForNFT","type":"uint8"}],"internalType":"struct TokensRegistry.PriceFeedData[]","name":"priceFeedData","type":"tuple[]"}],"name":"setTokenPriceFeed","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"signerWallet","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"name":"tokenData","outputs":[{"internalType":"contract AggregatorV3Interface","name":"priceFeed","type":"address"},{"internalType":"uint8","name":"normalizationFactorForToken","type":"uint8"},{"internalType":"uint8","name":"normalizationFactorForNFT","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint32","name":"round","type":"uint32"},{"internalType":"contract IERC20[]","name":"tokens","type":"address[]"},{"internalType":"bool[]","name":"accesses","type":"bool[]"},{"internalType":"uint256[]","name":"customPrices","type":"uint256[]"}],"name":"updateAllowedTokens","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"which","type":"address"},{"internalType":"bool","name":"access","type":"bool"}],"name":"updateBlackListedUser","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"newPrices","type":"uint256[]"}],"name":"updatePricing","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint32","name":"round","type":"uint32"},{"internalType":"uint256","name":"startTime","type":"uint256"},{"internalType":"uint256","name":"endTime","type":"uint256"},{"internalType":"uint256","name":"price","type":"uint256"}],"name":"updateRound","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint32","name":"round","type":"uint32"},{"internalType":"uint256","name":"deadline","type":"uint256"},{"internalType":"uint256[]","name":"tokenPrices","type":"uint256[]"},{"internalType":"uint8[]","name":"normalizationFactors","type":"uint8[]"},{"internalType":"contract IERC20[]","name":"tokens","type":"address[]"},{"internalType":"uint256[]","name":"amounts","type":"uint256[]"},{"internalType":"uint8","name":"v","type":"uint8"},{"internalType":"bytes32","name":"r","type":"bytes32"},{"internalType":"bytes32","name":"s","type":"bytes32"}],"name":"verifyPurchaseWithClaim","outputs":[],"stateMutability":"view","type":"function"}]