Overview
ETH Balance
0 ETH
Token Holdings
More Info
ContractCreator
Multichain Info
N/A
Latest 25 from a total of 68 transactions
| Transaction Hash |
Method
|
Block
|
From
|
To
|
Amount
|
||||
|---|---|---|---|---|---|---|---|---|---|
| Claim Launchpad ... | 24399602 | 379 days ago | IN | 0 ETH | 0.000002964227 | ||||
| Claim Launchpad ... | 24399496 | 379 days ago | IN | 0 ETH | 0.000002510076 | ||||
| Claim Launchpad ... | 24399002 | 379 days ago | IN | 0 ETH | 0.000000329051 | ||||
| Claim Launchpad ... | 24398985 | 379 days ago | IN | 0 ETH | 0.000001966508 | ||||
| Claim Launchpad ... | 24398952 | 379 days ago | IN | 0 ETH | 0.000002901267 | ||||
| Claim Launchpad ... | 24398942 | 379 days ago | IN | 0 ETH | 0.000000520786 | ||||
| Claim Launchpad ... | 24398873 | 379 days ago | IN | 0 ETH | 0.000003111329 | ||||
| Claim Launchpad ... | 24398729 | 379 days ago | IN | 0 ETH | 0.000002658727 | ||||
| Claim Launchpad ... | 24398692 | 379 days ago | IN | 0 ETH | 0.000486867957 | ||||
| Claim Launchpad ... | 24398661 | 379 days ago | IN | 0 ETH | 0.00000325142 | ||||
| Claim Launchpad ... | 24398627 | 379 days ago | IN | 0 ETH | 0.000002101823 | ||||
| Claim Launchpad ... | 24398617 | 379 days ago | IN | 0 ETH | 0.000002413897 | ||||
| Claim Launchpad ... | 24398579 | 379 days ago | IN | 0 ETH | 0.000002817681 | ||||
| Claim Launchpad ... | 24398574 | 379 days ago | IN | 0 ETH | 0.000003347373 | ||||
| Claim Launchpad ... | 24398530 | 379 days ago | IN | 0 ETH | 0.000001744764 | ||||
| Claim Launchpad ... | 24398515 | 379 days ago | IN | 0 ETH | 0.000005245955 | ||||
| Claim Launchpad ... | 24398485 | 379 days ago | IN | 0 ETH | 0.000000270745 | ||||
| Claim Launchpad ... | 24398470 | 379 days ago | IN | 0 ETH | 0.000003170086 | ||||
| Claim Launchpad ... | 24398421 | 379 days ago | IN | 0 ETH | 0.000001486693 | ||||
| Upload Winners B... | 24398143 | 379 days ago | IN | 0 ETH | 0.000000280615 | ||||
| Init Shuffler | 24398141 | 379 days ago | IN | 0 ETH | 0.000001727808 | ||||
| Filter Tickets | 24398139 | 379 days ago | IN | 0 ETH | 0.000002620929 | ||||
| Confirm Tickets | 24398078 | 379 days ago | IN | 0 ETH | 0.000000807287 | ||||
| Confirm Tickets | 24398057 | 379 days ago | IN | 0 ETH | 0.000001741663 | ||||
| Confirm Tickets | 24398030 | 379 days ago | IN | 0 ETH | 0.000281225694 |
Latest 1 internal transaction
| Parent Transaction Hash | Block | From | To | Amount | ||
|---|---|---|---|---|---|---|
| 24396338 | 379 days ago | Contract Creation | 0 ETH |
Loading...
Loading
Minimal Proxy Contract for 0xc8293630724bea150fb3244bc78b6956a575d578
Contract Name:
Launchpad
Compiler Version
v0.8.24+commit.e11b9ed9
Optimization Enabled:
Yes with 1 runs
Other Settings:
paris EvmVersion
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import {Strings} from "@openzeppelin/contracts/utils/Strings.sol";
import "./VRF.sol";
import "./LaunchpadStorage.sol";
import "./Config.sol";
import "./Tickets.sol";
import "./UserInteractions.sol";
import "./Blacklist.sol";
import "./Nfts.sol";
contract Launchpad is LaunchpadStorage, Ownable, ConfigurationModule, Tickets, UserInteractions, Blacklist {
using SafeERC20 for IERC20;
event ShufflerInitialized();
bool private initialized;
function initShuffler(bytes memory proof_bytes) external {
require(state.publicKey[0] != 0 && state.publicKey[1] != 0, "Public key not set");
require(state.proofBytes.length == 0, "Already init");
uint256[4] memory proof = VRF.decodeProof(proof_bytes);
require(VRF.verify(state.publicKey, proof, bytes(getShufflerSeed())), "Invalid proof");
state.proofBytes = proof_bytes;
emit ShufflerInitialized();
}
function getProof() external view returns (bytes memory) {
require(state.proofBytes.length > 0, "Not init");
return state.proofBytes;
}
constructor() Ownable(msg.sender) {}
function initialize(
bytes memory proofZero,
bytes memory publicKeyBytes,
address launchpadTokenId,
uint256 launchpadTokensPerWinningTicket,
address ticketPaymentToken,
uint256 ticketPrice,
uint256 nrWinningTickets,
uint256 confirmationPeriodStartTime,
uint256 winnerSelectionStartTime,
uint256 claimStartTime,
address initialOwner,
bytes memory winnerSelectionScriptHash
) external {
require(!initialized, "Already initialized");
initialized = true;
// Set owner
_transferOwnership(initialOwner);
// Initialize state (copied from constructor logic)
require(nrWinningTickets > 0, "Number of winning tickets must be greater than zero");
uint256[2] memory publicKey = VRF.decodePoint(publicKeyBytes);
state.publicKey = publicKey;
require(VRF.verify(publicKey, VRF.decodeProof(proofZero), bytes("0")), "Invalid proof");
state.configuration = ConfigModule({
timeline: Timeline({
confirmationPeriodStartTime: confirmationPeriodStartTime,
winnerSelectionStartTime: winnerSelectionStartTime,
claimStartTime: claimStartTime
}),
launchpadToken: launchpadTokenId,
launchpadTokensPerWinningTicket: launchpadTokensPerWinningTicket,
ticketToken: ticketPaymentToken,
ticketPrice: ticketPrice,
numberOfWinningTickets: NumberOfWinningTickets({
ticketType: NumberOfWinningTicketsType.BeforeFiltering,
value: nrWinningTickets
}),
launchpadTokensDeposited: false,
claimableTicketPayment: 0
});
state.flags = Flags({
hasWinnerSelectionProcessStarted: false,
wereTicketsFiltered: false,
winnersSelected: false,
launchpadNfts: false
});
state.lastTicketId = 0;
state.winnerSelectionScriptHash = winnerSelectionScriptHash;
require(
confirmationPeriodStartTime < winnerSelectionStartTime &&
winnerSelectionStartTime < claimStartTime &&
confirmationPeriodStartTime > block.timestamp,
"Invalid time periods"
);
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (access/Ownable.sol)
pragma solidity ^0.8.20;
import {Context} from "../utils/Context.sol";
/**
* @dev Contract module which provides a basic access control mechanism, where
* there is an account (an owner) that can be granted exclusive access to
* specific functions.
*
* The initial owner is set to the address provided by the deployer. This can
* later be changed with {transferOwnership}.
*
* This module is used through inheritance. It will make available the modifier
* `onlyOwner`, which can be applied to your functions to restrict their use to
* the owner.
*/
abstract contract Ownable is Context {
address private _owner;
/**
* @dev The caller account is not authorized to perform an operation.
*/
error OwnableUnauthorizedAccount(address account);
/**
* @dev The owner is not a valid owner account. (eg. `address(0)`)
*/
error OwnableInvalidOwner(address owner);
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
/**
* @dev Initializes the contract setting the address provided by the deployer as the initial owner.
*/
constructor(address initialOwner) {
if (initialOwner == address(0)) {
revert OwnableInvalidOwner(address(0));
}
_transferOwnership(initialOwner);
}
/**
* @dev Throws if called by any account other than the owner.
*/
modifier onlyOwner() {
_checkOwner();
_;
}
/**
* @dev Returns the address of the current owner.
*/
function owner() public view virtual returns (address) {
return _owner;
}
/**
* @dev Throws if the sender is not the owner.
*/
function _checkOwner() internal view virtual {
if (owner() != _msgSender()) {
revert OwnableUnauthorizedAccount(_msgSender());
}
}
/**
* @dev Leaves the contract without owner. It will not be possible to call
* `onlyOwner` functions. Can only be called by the current owner.
*
* NOTE: Renouncing ownership will leave the contract without an owner,
* thereby disabling any functionality that is only available to the owner.
*/
function renounceOwnership() public virtual onlyOwner {
_transferOwnership(address(0));
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`).
* Can only be called by the current owner.
*/
function transferOwnership(address newOwner) public virtual onlyOwner {
if (newOwner == address(0)) {
revert OwnableInvalidOwner(address(0));
}
_transferOwnership(newOwner);
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`).
* Internal function without access restriction.
*/
function _transferOwnership(address newOwner) internal virtual {
address oldOwner = _owner;
_owner = newOwner;
emit OwnershipTransferred(oldOwner, newOwner);
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (interfaces/draft-IERC6093.sol)
pragma solidity ^0.8.20;
/**
* @dev Standard ERC20 Errors
* Interface of the https://eips.ethereum.org/EIPS/eip-6093[ERC-6093] custom errors for ERC20 tokens.
*/
interface IERC20Errors {
/**
* @dev Indicates an error related to the current `balance` of a `sender`. Used in transfers.
* @param sender Address whose tokens are being transferred.
* @param balance Current balance for the interacting account.
* @param needed Minimum amount required to perform a transfer.
*/
error ERC20InsufficientBalance(address sender, uint256 balance, uint256 needed);
/**
* @dev Indicates a failure with the token `sender`. Used in transfers.
* @param sender Address whose tokens are being transferred.
*/
error ERC20InvalidSender(address sender);
/**
* @dev Indicates a failure with the token `receiver`. Used in transfers.
* @param receiver Address to which tokens are being transferred.
*/
error ERC20InvalidReceiver(address receiver);
/**
* @dev Indicates a failure with the `spender`’s `allowance`. Used in transfers.
* @param spender Address that may be allowed to operate on tokens without being their owner.
* @param allowance Amount of tokens a `spender` is allowed to operate with.
* @param needed Minimum amount required to perform a transfer.
*/
error ERC20InsufficientAllowance(address spender, uint256 allowance, uint256 needed);
/**
* @dev Indicates a failure with the `approver` of a token to be approved. Used in approvals.
* @param approver Address initiating an approval operation.
*/
error ERC20InvalidApprover(address approver);
/**
* @dev Indicates a failure with the `spender` to be approved. Used in approvals.
* @param spender Address that may be allowed to operate on tokens without being their owner.
*/
error ERC20InvalidSpender(address spender);
}
/**
* @dev Standard ERC721 Errors
* Interface of the https://eips.ethereum.org/EIPS/eip-6093[ERC-6093] custom errors for ERC721 tokens.
*/
interface IERC721Errors {
/**
* @dev Indicates that an address can't be an owner. For example, `address(0)` is a forbidden owner in EIP-20.
* Used in balance queries.
* @param owner Address of the current owner of a token.
*/
error ERC721InvalidOwner(address owner);
/**
* @dev Indicates a `tokenId` whose `owner` is the zero address.
* @param tokenId Identifier number of a token.
*/
error ERC721NonexistentToken(uint256 tokenId);
/**
* @dev Indicates an error related to the ownership over a particular token. Used in transfers.
* @param sender Address whose tokens are being transferred.
* @param tokenId Identifier number of a token.
* @param owner Address of the current owner of a token.
*/
error ERC721IncorrectOwner(address sender, uint256 tokenId, address owner);
/**
* @dev Indicates a failure with the token `sender`. Used in transfers.
* @param sender Address whose tokens are being transferred.
*/
error ERC721InvalidSender(address sender);
/**
* @dev Indicates a failure with the token `receiver`. Used in transfers.
* @param receiver Address to which tokens are being transferred.
*/
error ERC721InvalidReceiver(address receiver);
/**
* @dev Indicates a failure with the `operator`’s approval. Used in transfers.
* @param operator Address that may be allowed to operate on tokens without being their owner.
* @param tokenId Identifier number of a token.
*/
error ERC721InsufficientApproval(address operator, uint256 tokenId);
/**
* @dev Indicates a failure with the `approver` of a token to be approved. Used in approvals.
* @param approver Address initiating an approval operation.
*/
error ERC721InvalidApprover(address approver);
/**
* @dev Indicates a failure with the `operator` to be approved. Used in approvals.
* @param operator Address that may be allowed to operate on tokens without being their owner.
*/
error ERC721InvalidOperator(address operator);
}
/**
* @dev Standard ERC1155 Errors
* Interface of the https://eips.ethereum.org/EIPS/eip-6093[ERC-6093] custom errors for ERC1155 tokens.
*/
interface IERC1155Errors {
/**
* @dev Indicates an error related to the current `balance` of a `sender`. Used in transfers.
* @param sender Address whose tokens are being transferred.
* @param balance Current balance for the interacting account.
* @param needed Minimum amount required to perform a transfer.
* @param tokenId Identifier number of a token.
*/
error ERC1155InsufficientBalance(address sender, uint256 balance, uint256 needed, uint256 tokenId);
/**
* @dev Indicates a failure with the token `sender`. Used in transfers.
* @param sender Address whose tokens are being transferred.
*/
error ERC1155InvalidSender(address sender);
/**
* @dev Indicates a failure with the token `receiver`. Used in transfers.
* @param receiver Address to which tokens are being transferred.
*/
error ERC1155InvalidReceiver(address receiver);
/**
* @dev Indicates a failure with the `operator`’s approval. Used in transfers.
* @param operator Address that may be allowed to operate on tokens without being their owner.
* @param owner Address of the current owner of a token.
*/
error ERC1155MissingApprovalForAll(address operator, address owner);
/**
* @dev Indicates a failure with the `approver` of a token to be approved. Used in approvals.
* @param approver Address initiating an approval operation.
*/
error ERC1155InvalidApprover(address approver);
/**
* @dev Indicates a failure with the `operator` to be approved. Used in approvals.
* @param operator Address that may be allowed to operate on tokens without being their owner.
*/
error ERC1155InvalidOperator(address operator);
/**
* @dev Indicates an array length mismatch between ids and values in a safeBatchTransferFrom operation.
* Used in batch transfers.
* @param idsLength Length of the array of token identifiers
* @param valuesLength Length of the array of token amounts
*/
error ERC1155InvalidArrayLength(uint256 idsLength, uint256 valuesLength);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (proxy/Clones.sol)
pragma solidity ^0.8.20;
/**
* @dev https://eips.ethereum.org/EIPS/eip-1167[EIP 1167] is a standard for
* deploying minimal proxy contracts, also known as "clones".
*
* > To simply and cheaply clone contract functionality in an immutable way, this standard specifies
* > a minimal bytecode implementation that delegates all calls to a known, fixed address.
*
* The library includes functions to deploy a proxy using either `create` (traditional deployment) or `create2`
* (salted deterministic deployment). It also includes functions to predict the addresses of clones deployed using the
* deterministic method.
*/
library Clones {
/**
* @dev A clone instance deployment failed.
*/
error ERC1167FailedCreateClone();
/**
* @dev Deploys and returns the address of a clone that mimics the behaviour of `implementation`.
*
* This function uses the create opcode, which should never revert.
*/
function clone(address implementation) internal returns (address instance) {
/// @solidity memory-safe-assembly
assembly {
// Cleans the upper 96 bits of the `implementation` word, then packs the first 3 bytes
// of the `implementation` address with the bytecode before the address.
mstore(0x00, or(shr(0xe8, shl(0x60, implementation)), 0x3d602d80600a3d3981f3363d3d373d3d3d363d73000000))
// Packs the remaining 17 bytes of `implementation` with the bytecode after the address.
mstore(0x20, or(shl(0x78, implementation), 0x5af43d82803e903d91602b57fd5bf3))
instance := create(0, 0x09, 0x37)
}
if (instance == address(0)) {
revert ERC1167FailedCreateClone();
}
}
/**
* @dev Deploys and returns the address of a clone that mimics the behaviour of `implementation`.
*
* This function uses the create2 opcode and a `salt` to deterministically deploy
* the clone. Using the same `implementation` and `salt` multiple time will revert, since
* the clones cannot be deployed twice at the same address.
*/
function cloneDeterministic(address implementation, bytes32 salt) internal returns (address instance) {
/// @solidity memory-safe-assembly
assembly {
// Cleans the upper 96 bits of the `implementation` word, then packs the first 3 bytes
// of the `implementation` address with the bytecode before the address.
mstore(0x00, or(shr(0xe8, shl(0x60, implementation)), 0x3d602d80600a3d3981f3363d3d373d3d3d363d73000000))
// Packs the remaining 17 bytes of `implementation` with the bytecode after the address.
mstore(0x20, or(shl(0x78, implementation), 0x5af43d82803e903d91602b57fd5bf3))
instance := create2(0, 0x09, 0x37, salt)
}
if (instance == address(0)) {
revert ERC1167FailedCreateClone();
}
}
/**
* @dev Computes the address of a clone deployed using {Clones-cloneDeterministic}.
*/
function predictDeterministicAddress(
address implementation,
bytes32 salt,
address deployer
) internal pure returns (address predicted) {
/// @solidity memory-safe-assembly
assembly {
let ptr := mload(0x40)
mstore(add(ptr, 0x38), deployer)
mstore(add(ptr, 0x24), 0x5af43d82803e903d91602b57fd5bf3ff)
mstore(add(ptr, 0x14), implementation)
mstore(ptr, 0x3d602d80600a3d3981f3363d3d373d3d3d363d73)
mstore(add(ptr, 0x58), salt)
mstore(add(ptr, 0x78), keccak256(add(ptr, 0x0c), 0x37))
predicted := keccak256(add(ptr, 0x43), 0x55)
}
}
/**
* @dev Computes the address of a clone deployed using {Clones-cloneDeterministic}.
*/
function predictDeterministicAddress(
address implementation,
bytes32 salt
) internal view returns (address predicted) {
return predictDeterministicAddress(implementation, salt, address(this));
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/extensions/IERC20Permit.sol)
pragma solidity ^0.8.20;
/**
* @dev Interface of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in
* https://eips.ethereum.org/EIPS/eip-2612[EIP-2612].
*
* Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by
* presenting a message signed by the account. By not relying on {IERC20-approve}, the token holder account doesn't
* need to send a transaction, and thus is not required to hold Ether at all.
*
* ==== Security Considerations
*
* There are two important considerations concerning the use of `permit`. The first is that a valid permit signature
* expresses an allowance, and it should not be assumed to convey additional meaning. In particular, it should not be
* considered as an intention to spend the allowance in any specific way. The second is that because permits have
* built-in replay protection and can be submitted by anyone, they can be frontrun. A protocol that uses permits should
* take this into consideration and allow a `permit` call to fail. Combining these two aspects, a pattern that may be
* generally recommended is:
*
* ```solidity
* function doThingWithPermit(..., uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s) public {
* try token.permit(msg.sender, address(this), value, deadline, v, r, s) {} catch {}
* doThing(..., value);
* }
*
* function doThing(..., uint256 value) public {
* token.safeTransferFrom(msg.sender, address(this), value);
* ...
* }
* ```
*
* Observe that: 1) `msg.sender` is used as the owner, leaving no ambiguity as to the signer intent, and 2) the use of
* `try/catch` allows the permit to fail and makes the code tolerant to frontrunning. (See also
* {SafeERC20-safeTransferFrom}).
*
* Additionally, note that smart contract wallets (such as Argent or Safe) are not able to produce permit signatures, so
* contracts should have entry points that don't rely on permit.
*/
interface IERC20Permit {
/**
* @dev Sets `value` as the allowance of `spender` over ``owner``'s tokens,
* given ``owner``'s signed approval.
*
* IMPORTANT: The same issues {IERC20-approve} has related to transaction
* ordering also apply here.
*
* Emits an {Approval} event.
*
* Requirements:
*
* - `spender` cannot be the zero address.
* - `deadline` must be a timestamp in the future.
* - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner`
* over the EIP712-formatted function arguments.
* - the signature must use ``owner``'s current nonce (see {nonces}).
*
* For more information on the signature format, see the
* https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP
* section].
*
* CAUTION: See Security Considerations above.
*/
function permit(
address owner,
address spender,
uint256 value,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) external;
/**
* @dev Returns the current nonce for `owner`. This value must be
* included whenever a signature is generated for {permit}.
*
* Every successful call to {permit} increases ``owner``'s nonce by one. This
* prevents a signature from being used multiple times.
*/
function nonces(address owner) external view returns (uint256);
/**
* @dev Returns the domain separator used in the encoding of the signature for {permit}, as defined by {EIP712}.
*/
// solhint-disable-next-line func-name-mixedcase
function DOMAIN_SEPARATOR() external view returns (bytes32);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/IERC20.sol)
pragma solidity ^0.8.20;
/**
* @dev Interface of the ERC20 standard as defined in the EIP.
*/
interface IERC20 {
/**
* @dev Emitted when `value` tokens are moved from one account (`from`) to
* another (`to`).
*
* Note that `value` may be zero.
*/
event Transfer(address indexed from, address indexed to, uint256 value);
/**
* @dev Emitted when the allowance of a `spender` for an `owner` is set by
* a call to {approve}. `value` is the new allowance.
*/
event Approval(address indexed owner, address indexed spender, uint256 value);
/**
* @dev Returns the value of tokens in existence.
*/
function totalSupply() external view returns (uint256);
/**
* @dev Returns the value of tokens owned by `account`.
*/
function balanceOf(address account) external view returns (uint256);
/**
* @dev Moves a `value` amount of tokens from the caller's account to `to`.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transfer(address to, uint256 value) external returns (bool);
/**
* @dev Returns the remaining number of tokens that `spender` will be
* allowed to spend on behalf of `owner` through {transferFrom}. This is
* zero by default.
*
* This value changes when {approve} or {transferFrom} are called.
*/
function allowance(address owner, address spender) external view returns (uint256);
/**
* @dev Sets a `value` amount of tokens as the allowance of `spender` over the
* caller's tokens.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* IMPORTANT: Beware that changing an allowance with this method brings the risk
* that someone may use both the old and the new allowance by unfortunate
* transaction ordering. One possible solution to mitigate this race
* condition is to first reduce the spender's allowance to 0 and set the
* desired value afterwards:
* https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
*
* Emits an {Approval} event.
*/
function approve(address spender, uint256 value) external returns (bool);
/**
* @dev Moves a `value` amount of tokens from `from` to `to` using the
* allowance mechanism. `value` is then deducted from the caller's
* allowance.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transferFrom(address from, address to, uint256 value) external returns (bool);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/utils/SafeERC20.sol)
pragma solidity ^0.8.20;
import {IERC20} from "../IERC20.sol";
import {IERC20Permit} from "../extensions/IERC20Permit.sol";
import {Address} from "../../../utils/Address.sol";
/**
* @title SafeERC20
* @dev Wrappers around ERC20 operations that throw on failure (when the token
* contract returns false). Tokens that return no value (and instead revert or
* throw on failure) are also supported, non-reverting calls are assumed to be
* successful.
* To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,
* which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
*/
library SafeERC20 {
using Address for address;
/**
* @dev An operation with an ERC20 token failed.
*/
error SafeERC20FailedOperation(address token);
/**
* @dev Indicates a failed `decreaseAllowance` request.
*/
error SafeERC20FailedDecreaseAllowance(address spender, uint256 currentAllowance, uint256 requestedDecrease);
/**
* @dev Transfer `value` amount of `token` from the calling contract to `to`. If `token` returns no value,
* non-reverting calls are assumed to be successful.
*/
function safeTransfer(IERC20 token, address to, uint256 value) internal {
_callOptionalReturn(token, abi.encodeCall(token.transfer, (to, value)));
}
/**
* @dev Transfer `value` amount of `token` from `from` to `to`, spending the approval given by `from` to the
* calling contract. If `token` returns no value, non-reverting calls are assumed to be successful.
*/
function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal {
_callOptionalReturn(token, abi.encodeCall(token.transferFrom, (from, to, value)));
}
/**
* @dev Increase the calling contract's allowance toward `spender` by `value`. If `token` returns no value,
* non-reverting calls are assumed to be successful.
*/
function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal {
uint256 oldAllowance = token.allowance(address(this), spender);
forceApprove(token, spender, oldAllowance + value);
}
/**
* @dev Decrease the calling contract's allowance toward `spender` by `requestedDecrease`. If `token` returns no
* value, non-reverting calls are assumed to be successful.
*/
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);
}
}
/**
* @dev Set the calling contract's allowance toward `spender` to `value`. If `token` returns no value,
* non-reverting calls are assumed to be successful. Meant to be used with tokens that require the approval
* to be set to zero before setting it to a non-zero value, such as USDT.
*/
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);
}
}
/**
* @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
* on the return value: the return value is optional (but if data is returned, it must not be false).
* @param token The token targeted by the call.
* @param data The call data (encoded using abi.encode or one of its variants).
*/
function _callOptionalReturn(IERC20 token, bytes memory data) private {
// We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
// we're implementing it ourselves. We use {Address-functionCall} to perform this call, which verifies that
// the target address contains contract code and also asserts for success in the low-level call.
bytes memory returndata = address(token).functionCall(data);
if (returndata.length != 0 && !abi.decode(returndata, (bool))) {
revert SafeERC20FailedOperation(address(token));
}
}
/**
* @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
* on the return value: the return value is optional (but if data is returned, it must not be false).
* @param token The token targeted by the call.
* @param data The call data (encoded using abi.encode or one of its variants).
*
* This is a variant of {_callOptionalReturn} that silents catches all reverts and returns a bool instead.
*/
function _callOptionalReturnBool(IERC20 token, bytes memory data) private returns (bool) {
// We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
// we're implementing it ourselves. We cannot use {Address-functionCall} here since this should return false
// and not revert is the subcall reverts.
(bool success, bytes memory returndata) = address(token).call(data);
return success && (returndata.length == 0 || abi.decode(returndata, (bool))) && address(token).code.length > 0;
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC721/ERC721.sol)
pragma solidity ^0.8.20;
import {IERC721} from "./IERC721.sol";
import {IERC721Receiver} from "./IERC721Receiver.sol";
import {IERC721Metadata} from "./extensions/IERC721Metadata.sol";
import {Context} from "../../utils/Context.sol";
import {Strings} from "../../utils/Strings.sol";
import {IERC165, ERC165} from "../../utils/introspection/ERC165.sol";
import {IERC721Errors} from "../../interfaces/draft-IERC6093.sol";
/**
* @dev Implementation of https://eips.ethereum.org/EIPS/eip-721[ERC721] Non-Fungible Token Standard, including
* the Metadata extension, but not including the Enumerable extension, which is available separately as
* {ERC721Enumerable}.
*/
abstract contract ERC721 is Context, ERC165, IERC721, IERC721Metadata, IERC721Errors {
using Strings for uint256;
// Token name
string private _name;
// Token symbol
string private _symbol;
mapping(uint256 tokenId => address) private _owners;
mapping(address owner => uint256) private _balances;
mapping(uint256 tokenId => address) private _tokenApprovals;
mapping(address owner => mapping(address operator => bool)) private _operatorApprovals;
/**
* @dev Initializes the contract by setting a `name` and a `symbol` to the token collection.
*/
constructor(string memory name_, string memory symbol_) {
_name = name_;
_symbol = symbol_;
}
/**
* @dev See {IERC165-supportsInterface}.
*/
function supportsInterface(bytes4 interfaceId) public view virtual override(ERC165, IERC165) returns (bool) {
return
interfaceId == type(IERC721).interfaceId ||
interfaceId == type(IERC721Metadata).interfaceId ||
super.supportsInterface(interfaceId);
}
/**
* @dev See {IERC721-balanceOf}.
*/
function balanceOf(address owner) public view virtual returns (uint256) {
if (owner == address(0)) {
revert ERC721InvalidOwner(address(0));
}
return _balances[owner];
}
/**
* @dev See {IERC721-ownerOf}.
*/
function ownerOf(uint256 tokenId) public view virtual returns (address) {
return _requireOwned(tokenId);
}
/**
* @dev See {IERC721Metadata-name}.
*/
function name() public view virtual returns (string memory) {
return _name;
}
/**
* @dev See {IERC721Metadata-symbol}.
*/
function symbol() public view virtual returns (string memory) {
return _symbol;
}
/**
* @dev See {IERC721Metadata-tokenURI}.
*/
function tokenURI(uint256 tokenId) public view virtual returns (string memory) {
_requireOwned(tokenId);
string memory baseURI = _baseURI();
return bytes(baseURI).length > 0 ? string.concat(baseURI, tokenId.toString()) : "";
}
/**
* @dev Base URI for computing {tokenURI}. If set, the resulting URI for each
* token will be the concatenation of the `baseURI` and the `tokenId`. Empty
* by default, can be overridden in child contracts.
*/
function _baseURI() internal view virtual returns (string memory) {
return "";
}
/**
* @dev See {IERC721-approve}.
*/
function approve(address to, uint256 tokenId) public virtual {
_approve(to, tokenId, _msgSender());
}
/**
* @dev See {IERC721-getApproved}.
*/
function getApproved(uint256 tokenId) public view virtual returns (address) {
_requireOwned(tokenId);
return _getApproved(tokenId);
}
/**
* @dev See {IERC721-setApprovalForAll}.
*/
function setApprovalForAll(address operator, bool approved) public virtual {
_setApprovalForAll(_msgSender(), operator, approved);
}
/**
* @dev See {IERC721-isApprovedForAll}.
*/
function isApprovedForAll(address owner, address operator) public view virtual returns (bool) {
return _operatorApprovals[owner][operator];
}
/**
* @dev See {IERC721-transferFrom}.
*/
function transferFrom(address from, address to, uint256 tokenId) public virtual {
if (to == address(0)) {
revert ERC721InvalidReceiver(address(0));
}
// Setting an "auth" arguments enables the `_isAuthorized` check which verifies that the token exists
// (from != 0). Therefore, it is not needed to verify that the return value is not 0 here.
address previousOwner = _update(to, tokenId, _msgSender());
if (previousOwner != from) {
revert ERC721IncorrectOwner(from, tokenId, previousOwner);
}
}
/**
* @dev See {IERC721-safeTransferFrom}.
*/
function safeTransferFrom(address from, address to, uint256 tokenId) public {
safeTransferFrom(from, to, tokenId, "");
}
/**
* @dev See {IERC721-safeTransferFrom}.
*/
function safeTransferFrom(address from, address to, uint256 tokenId, bytes memory data) public virtual {
transferFrom(from, to, tokenId);
_checkOnERC721Received(from, to, tokenId, data);
}
/**
* @dev Returns the owner of the `tokenId`. Does NOT revert if token doesn't exist
*
* IMPORTANT: Any overrides to this function that add ownership of tokens not tracked by the
* core ERC721 logic MUST be matched with the use of {_increaseBalance} to keep balances
* consistent with ownership. The invariant to preserve is that for any address `a` the value returned by
* `balanceOf(a)` must be equal to the number of tokens such that `_ownerOf(tokenId)` is `a`.
*/
function _ownerOf(uint256 tokenId) internal view virtual returns (address) {
return _owners[tokenId];
}
/**
* @dev Returns the approved address for `tokenId`. Returns 0 if `tokenId` is not minted.
*/
function _getApproved(uint256 tokenId) internal view virtual returns (address) {
return _tokenApprovals[tokenId];
}
/**
* @dev Returns whether `spender` is allowed to manage `owner`'s tokens, or `tokenId` in
* particular (ignoring whether it is owned by `owner`).
*
* WARNING: This function assumes that `owner` is the actual owner of `tokenId` and does not verify this
* assumption.
*/
function _isAuthorized(address owner, address spender, uint256 tokenId) internal view virtual returns (bool) {
return
spender != address(0) &&
(owner == spender || isApprovedForAll(owner, spender) || _getApproved(tokenId) == spender);
}
/**
* @dev Checks if `spender` can operate on `tokenId`, assuming the provided `owner` is the actual owner.
* Reverts if `spender` does not have approval from the provided `owner` for the given token or for all its assets
* the `spender` for the specific `tokenId`.
*
* WARNING: This function assumes that `owner` is the actual owner of `tokenId` and does not verify this
* assumption.
*/
function _checkAuthorized(address owner, address spender, uint256 tokenId) internal view virtual {
if (!_isAuthorized(owner, spender, tokenId)) {
if (owner == address(0)) {
revert ERC721NonexistentToken(tokenId);
} else {
revert ERC721InsufficientApproval(spender, tokenId);
}
}
}
/**
* @dev Unsafe write access to the balances, used by extensions that "mint" tokens using an {ownerOf} override.
*
* NOTE: the value is limited to type(uint128).max. This protect against _balance overflow. It is unrealistic that
* a uint256 would ever overflow from increments when these increments are bounded to uint128 values.
*
* WARNING: Increasing an account's balance using this function tends to be paired with an override of the
* {_ownerOf} function to resolve the ownership of the corresponding tokens so that balances and ownership
* remain consistent with one another.
*/
function _increaseBalance(address account, uint128 value) internal virtual {
unchecked {
_balances[account] += value;
}
}
/**
* @dev Transfers `tokenId` from its current owner to `to`, or alternatively mints (or burns) if the current owner
* (or `to`) is the zero address. Returns the owner of the `tokenId` before the update.
*
* The `auth` argument is optional. If the value passed is non 0, then this function will check that
* `auth` is either the owner of the token, or approved to operate on the token (by the owner).
*
* Emits a {Transfer} event.
*
* NOTE: If overriding this function in a way that tracks balances, see also {_increaseBalance}.
*/
function _update(address to, uint256 tokenId, address auth) internal virtual returns (address) {
address from = _ownerOf(tokenId);
// Perform (optional) operator check
if (auth != address(0)) {
_checkAuthorized(from, auth, tokenId);
}
// Execute the update
if (from != address(0)) {
// Clear approval. No need to re-authorize or emit the Approval event
_approve(address(0), tokenId, address(0), false);
unchecked {
_balances[from] -= 1;
}
}
if (to != address(0)) {
unchecked {
_balances[to] += 1;
}
}
_owners[tokenId] = to;
emit Transfer(from, to, tokenId);
return from;
}
/**
* @dev Mints `tokenId` and transfers it to `to`.
*
* WARNING: Usage of this method is discouraged, use {_safeMint} whenever possible
*
* Requirements:
*
* - `tokenId` must not exist.
* - `to` cannot be the zero address.
*
* Emits a {Transfer} event.
*/
function _mint(address to, uint256 tokenId) internal {
if (to == address(0)) {
revert ERC721InvalidReceiver(address(0));
}
address previousOwner = _update(to, tokenId, address(0));
if (previousOwner != address(0)) {
revert ERC721InvalidSender(address(0));
}
}
/**
* @dev Mints `tokenId`, transfers it to `to` and checks for `to` acceptance.
*
* Requirements:
*
* - `tokenId` must not exist.
* - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
*
* Emits a {Transfer} event.
*/
function _safeMint(address to, uint256 tokenId) internal {
_safeMint(to, tokenId, "");
}
/**
* @dev Same as {xref-ERC721-_safeMint-address-uint256-}[`_safeMint`], with an additional `data` parameter which is
* forwarded in {IERC721Receiver-onERC721Received} to contract recipients.
*/
function _safeMint(address to, uint256 tokenId, bytes memory data) internal virtual {
_mint(to, tokenId);
_checkOnERC721Received(address(0), to, tokenId, data);
}
/**
* @dev Destroys `tokenId`.
* The approval is cleared when the token is burned.
* This is an internal function that does not check if the sender is authorized to operate on the token.
*
* Requirements:
*
* - `tokenId` must exist.
*
* Emits a {Transfer} event.
*/
function _burn(uint256 tokenId) internal {
address previousOwner = _update(address(0), tokenId, address(0));
if (previousOwner == address(0)) {
revert ERC721NonexistentToken(tokenId);
}
}
/**
* @dev Transfers `tokenId` from `from` to `to`.
* As opposed to {transferFrom}, this imposes no restrictions on msg.sender.
*
* Requirements:
*
* - `to` cannot be the zero address.
* - `tokenId` token must be owned by `from`.
*
* Emits a {Transfer} event.
*/
function _transfer(address from, address to, uint256 tokenId) internal {
if (to == address(0)) {
revert ERC721InvalidReceiver(address(0));
}
address previousOwner = _update(to, tokenId, address(0));
if (previousOwner == address(0)) {
revert ERC721NonexistentToken(tokenId);
} else if (previousOwner != from) {
revert ERC721IncorrectOwner(from, tokenId, previousOwner);
}
}
/**
* @dev Safely transfers `tokenId` token from `from` to `to`, checking that contract recipients
* are aware of the ERC721 standard to prevent tokens from being forever locked.
*
* `data` is additional data, it has no specified format and it is sent in call to `to`.
*
* This internal function is like {safeTransferFrom} in the sense that it invokes
* {IERC721Receiver-onERC721Received} on the receiver, and can be used to e.g.
* implement alternative mechanisms to perform token transfer, such as signature-based.
*
* Requirements:
*
* - `tokenId` token must exist and be owned by `from`.
* - `to` cannot be the zero address.
* - `from` cannot be the zero address.
* - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
*
* Emits a {Transfer} event.
*/
function _safeTransfer(address from, address to, uint256 tokenId) internal {
_safeTransfer(from, to, tokenId, "");
}
/**
* @dev Same as {xref-ERC721-_safeTransfer-address-address-uint256-}[`_safeTransfer`], with an additional `data` parameter which is
* forwarded in {IERC721Receiver-onERC721Received} to contract recipients.
*/
function _safeTransfer(address from, address to, uint256 tokenId, bytes memory data) internal virtual {
_transfer(from, to, tokenId);
_checkOnERC721Received(from, to, tokenId, data);
}
/**
* @dev Approve `to` to operate on `tokenId`
*
* The `auth` argument is optional. If the value passed is non 0, then this function will check that `auth` is
* either the owner of the token, or approved to operate on all tokens held by this owner.
*
* Emits an {Approval} event.
*
* Overrides to this logic should be done to the variant with an additional `bool emitEvent` argument.
*/
function _approve(address to, uint256 tokenId, address auth) internal {
_approve(to, tokenId, auth, true);
}
/**
* @dev Variant of `_approve` with an optional flag to enable or disable the {Approval} event. The event is not
* emitted in the context of transfers.
*/
function _approve(address to, uint256 tokenId, address auth, bool emitEvent) internal virtual {
// Avoid reading the owner unless necessary
if (emitEvent || auth != address(0)) {
address owner = _requireOwned(tokenId);
// We do not use _isAuthorized because single-token approvals should not be able to call approve
if (auth != address(0) && owner != auth && !isApprovedForAll(owner, auth)) {
revert ERC721InvalidApprover(auth);
}
if (emitEvent) {
emit Approval(owner, to, tokenId);
}
}
_tokenApprovals[tokenId] = to;
}
/**
* @dev Approve `operator` to operate on all of `owner` tokens
*
* Requirements:
* - operator can't be the address zero.
*
* Emits an {ApprovalForAll} event.
*/
function _setApprovalForAll(address owner, address operator, bool approved) internal virtual {
if (operator == address(0)) {
revert ERC721InvalidOperator(operator);
}
_operatorApprovals[owner][operator] = approved;
emit ApprovalForAll(owner, operator, approved);
}
/**
* @dev Reverts if the `tokenId` doesn't have a current owner (it hasn't been minted, or it has been burned).
* Returns the owner.
*
* Overrides to ownership logic should be done to {_ownerOf}.
*/
function _requireOwned(uint256 tokenId) internal view returns (address) {
address owner = _ownerOf(tokenId);
if (owner == address(0)) {
revert ERC721NonexistentToken(tokenId);
}
return owner;
}
/**
* @dev Private function to invoke {IERC721Receiver-onERC721Received} on a target address. This will revert if the
* recipient doesn't accept the token transfer. The call is not executed if the target address is not a contract.
*
* @param from address representing the previous owner of the given token ID
* @param to target address that will receive the tokens
* @param tokenId uint256 ID of the token to be transferred
* @param data bytes optional data to send along with the call
*/
function _checkOnERC721Received(address from, address to, uint256 tokenId, bytes memory data) private {
if (to.code.length > 0) {
try IERC721Receiver(to).onERC721Received(_msgSender(), from, tokenId, data) returns (bytes4 retval) {
if (retval != IERC721Receiver.onERC721Received.selector) {
revert ERC721InvalidReceiver(to);
}
} catch (bytes memory reason) {
if (reason.length == 0) {
revert ERC721InvalidReceiver(to);
} else {
/// @solidity memory-safe-assembly
assembly {
revert(add(32, reason), mload(reason))
}
}
}
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC721/extensions/IERC721Metadata.sol)
pragma solidity ^0.8.20;
import {IERC721} from "../IERC721.sol";
/**
* @title ERC-721 Non-Fungible Token Standard, optional metadata extension
* @dev See https://eips.ethereum.org/EIPS/eip-721
*/
interface IERC721Metadata is IERC721 {
/**
* @dev Returns the token collection name.
*/
function name() external view returns (string memory);
/**
* @dev Returns the token collection symbol.
*/
function symbol() external view returns (string memory);
/**
* @dev Returns the Uniform Resource Identifier (URI) for `tokenId` token.
*/
function tokenURI(uint256 tokenId) external view returns (string memory);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC721/IERC721.sol)
pragma solidity ^0.8.20;
import {IERC165} from "../../utils/introspection/IERC165.sol";
/**
* @dev Required interface of an ERC721 compliant contract.
*/
interface IERC721 is IERC165 {
/**
* @dev Emitted when `tokenId` token is transferred from `from` to `to`.
*/
event Transfer(address indexed from, address indexed to, uint256 indexed tokenId);
/**
* @dev Emitted when `owner` enables `approved` to manage the `tokenId` token.
*/
event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId);
/**
* @dev Emitted when `owner` enables or disables (`approved`) `operator` to manage all of its assets.
*/
event ApprovalForAll(address indexed owner, address indexed operator, bool approved);
/**
* @dev Returns the number of tokens in ``owner``'s account.
*/
function balanceOf(address owner) external view returns (uint256 balance);
/**
* @dev Returns the owner of the `tokenId` token.
*
* Requirements:
*
* - `tokenId` must exist.
*/
function ownerOf(uint256 tokenId) external view returns (address owner);
/**
* @dev Safely transfers `tokenId` token from `from` to `to`.
*
* Requirements:
*
* - `from` cannot be the zero address.
* - `to` cannot be the zero address.
* - `tokenId` token must exist and be owned by `from`.
* - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.
* - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon
* a safe transfer.
*
* Emits a {Transfer} event.
*/
function safeTransferFrom(address from, address to, uint256 tokenId, bytes calldata data) external;
/**
* @dev Safely transfers `tokenId` token from `from` to `to`, checking first that contract recipients
* are aware of the ERC721 protocol to prevent tokens from being forever locked.
*
* Requirements:
*
* - `from` cannot be the zero address.
* - `to` cannot be the zero address.
* - `tokenId` token must exist and be owned by `from`.
* - If the caller is not `from`, it must have been allowed to move this token by either {approve} or
* {setApprovalForAll}.
* - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon
* a safe transfer.
*
* Emits a {Transfer} event.
*/
function safeTransferFrom(address from, address to, uint256 tokenId) external;
/**
* @dev Transfers `tokenId` token from `from` to `to`.
*
* WARNING: Note that the caller is responsible to confirm that the recipient is capable of receiving ERC721
* or else they may be permanently lost. Usage of {safeTransferFrom} prevents loss, though the caller must
* understand this adds an external call which potentially creates a reentrancy vulnerability.
*
* Requirements:
*
* - `from` cannot be the zero address.
* - `to` cannot be the zero address.
* - `tokenId` token must be owned by `from`.
* - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.
*
* Emits a {Transfer} event.
*/
function transferFrom(address from, address to, uint256 tokenId) external;
/**
* @dev Gives permission to `to` to transfer `tokenId` token to another account.
* The approval is cleared when the token is transferred.
*
* Only a single account can be approved at a time, so approving the zero address clears previous approvals.
*
* Requirements:
*
* - The caller must own the token or be an approved operator.
* - `tokenId` must exist.
*
* Emits an {Approval} event.
*/
function approve(address to, uint256 tokenId) external;
/**
* @dev Approve or remove `operator` as an operator for the caller.
* Operators can call {transferFrom} or {safeTransferFrom} for any token owned by the caller.
*
* Requirements:
*
* - The `operator` cannot be the address zero.
*
* Emits an {ApprovalForAll} event.
*/
function setApprovalForAll(address operator, bool approved) external;
/**
* @dev Returns the account approved for `tokenId` token.
*
* Requirements:
*
* - `tokenId` must exist.
*/
function getApproved(uint256 tokenId) external view returns (address operator);
/**
* @dev Returns if the `operator` is allowed to manage all of the assets of `owner`.
*
* See {setApprovalForAll}
*/
function isApprovedForAll(address owner, address operator) external view returns (bool);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC721/IERC721Receiver.sol)
pragma solidity ^0.8.20;
/**
* @title ERC721 token receiver interface
* @dev Interface for any contract that wants to support safeTransfers
* from ERC721 asset contracts.
*/
interface IERC721Receiver {
/**
* @dev Whenever an {IERC721} `tokenId` token is transferred to this contract via {IERC721-safeTransferFrom}
* by `operator` from `from`, this function is called.
*
* It must return its Solidity selector to confirm the token transfer.
* If any other value is returned or the interface is not implemented by the recipient, the transfer will be
* reverted.
*
* The selector can be obtained in Solidity with `IERC721Receiver.onERC721Received.selector`.
*/
function onERC721Received(
address operator,
address from,
uint256 tokenId,
bytes calldata data
) external returns (bytes4);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/Address.sol)
pragma solidity ^0.8.20;
/**
* @dev Collection of functions related to the address type
*/
library Address {
/**
* @dev The ETH balance of the account is not enough to perform the operation.
*/
error AddressInsufficientBalance(address account);
/**
* @dev There's no code at `target` (it is not a contract).
*/
error AddressEmptyCode(address target);
/**
* @dev A call to an address target failed. The target may have reverted.
*/
error FailedInnerCall();
/**
* @dev Replacement for Solidity's `transfer`: sends `amount` wei to
* `recipient`, forwarding all available gas and reverting on errors.
*
* https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
* of certain opcodes, possibly making contracts go over the 2300 gas limit
* imposed by `transfer`, making them unable to receive funds via
* `transfer`. {sendValue} removes this limitation.
*
* https://consensys.net/diligence/blog/2019/09/stop-using-soliditys-transfer-now/[Learn more].
*
* IMPORTANT: because control is transferred to `recipient`, care must be
* taken to not create reentrancy vulnerabilities. Consider using
* {ReentrancyGuard} or the
* https://solidity.readthedocs.io/en/v0.8.20/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
*/
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();
}
}
/**
* @dev Performs a Solidity function call using a low level `call`. A
* plain `call` is an unsafe replacement for a function call: use this
* function instead.
*
* If `target` reverts with a revert reason or custom error, it is bubbled
* up by this function (like regular Solidity function calls). However, if
* the call reverted with no returned reason, this function reverts with a
* {FailedInnerCall} error.
*
* Returns the raw returned data. To convert to the expected return value,
* use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
*
* Requirements:
*
* - `target` must be a contract.
* - calling `target` with `data` must not revert.
*/
function functionCall(address target, bytes memory data) internal returns (bytes memory) {
return functionCallWithValue(target, data, 0);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but also transferring `value` wei to `target`.
*
* Requirements:
*
* - the calling contract must have an ETH balance of at least `value`.
* - the called Solidity function must be `payable`.
*/
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);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but performing a static call.
*/
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);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but performing a delegate call.
*/
function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
(bool success, bytes memory returndata) = target.delegatecall(data);
return verifyCallResultFromTarget(target, success, returndata);
}
/**
* @dev Tool to verify that a low level call to smart-contract was successful, and reverts if the target
* was not a contract or bubbling up the revert reason (falling back to {FailedInnerCall}) in case of an
* unsuccessful call.
*/
function verifyCallResultFromTarget(
address target,
bool success,
bytes memory returndata
) internal view returns (bytes memory) {
if (!success) {
_revert(returndata);
} else {
// only check if target is a contract if the call was successful and the return data is empty
// otherwise we already know that it was a contract
if (returndata.length == 0 && target.code.length == 0) {
revert AddressEmptyCode(target);
}
return returndata;
}
}
/**
* @dev Tool to verify that a low level call was successful, and reverts if it wasn't, either by bubbling the
* revert reason or with a default {FailedInnerCall} error.
*/
function verifyCallResult(bool success, bytes memory returndata) internal pure returns (bytes memory) {
if (!success) {
_revert(returndata);
} else {
return returndata;
}
}
/**
* @dev Reverts with returndata if present. Otherwise reverts with {FailedInnerCall}.
*/
function _revert(bytes memory returndata) private pure {
// Look for revert reason and bubble it up if present
if (returndata.length > 0) {
// The easiest way to bubble the revert reason is using memory via assembly
/// @solidity memory-safe-assembly
assembly {
let returndata_size := mload(returndata)
revert(add(32, returndata), returndata_size)
}
} else {
revert FailedInnerCall();
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.1) (utils/Context.sol)
pragma solidity ^0.8.20;
/**
* @dev Provides information about the current execution context, including the
* sender of the transaction and its data. While these are generally available
* via msg.sender and msg.data, they should not be accessed in such a direct
* manner, since when dealing with meta-transactions the account sending and
* paying for execution may not be the actual sender (as far as an application
* is concerned).
*
* This contract is only required for intermediate, library-like contracts.
*/
abstract contract Context {
function _msgSender() internal view virtual returns (address) {
return msg.sender;
}
function _msgData() internal view virtual returns (bytes calldata) {
return msg.data;
}
function _contextSuffixLength() internal view virtual returns (uint256) {
return 0;
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/introspection/ERC165.sol)
pragma solidity ^0.8.20;
import {IERC165} from "./IERC165.sol";
/**
* @dev Implementation of the {IERC165} interface.
*
* Contracts that want to implement ERC165 should inherit from this contract and override {supportsInterface} to check
* for the additional interface id that will be supported. For example:
*
* ```solidity
* function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
* return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId);
* }
* ```
*/
abstract contract ERC165 is IERC165 {
/**
* @dev See {IERC165-supportsInterface}.
*/
function supportsInterface(bytes4 interfaceId) public view virtual returns (bool) {
return interfaceId == type(IERC165).interfaceId;
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/introspection/IERC165.sol)
pragma solidity ^0.8.20;
/**
* @dev Interface of the ERC165 standard, as defined in the
* https://eips.ethereum.org/EIPS/eip-165[EIP].
*
* Implementers can declare support of contract interfaces, which can then be
* queried by others ({ERC165Checker}).
*
* For an implementation, see {ERC165}.
*/
interface IERC165 {
/**
* @dev Returns true if this contract implements the interface defined by
* `interfaceId`. See the corresponding
* https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section]
* to learn more about how these ids are created.
*
* This function call must use less than 30 000 gas.
*/
function supportsInterface(bytes4 interfaceId) external view returns (bool);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/math/Math.sol)
pragma solidity ^0.8.20;
/**
* @dev Standard math utilities missing in the Solidity language.
*/
library Math {
/**
* @dev Muldiv operation overflow.
*/
error MathOverflowedMulDiv();
enum Rounding {
Floor, // Toward negative infinity
Ceil, // Toward positive infinity
Trunc, // Toward zero
Expand // Away from zero
}
/**
* @dev Returns the addition of two unsigned integers, with an overflow flag.
*/
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);
}
}
/**
* @dev Returns the subtraction of two unsigned integers, with an overflow flag.
*/
function trySub(uint256 a, uint256 b) internal pure returns (bool, uint256) {
unchecked {
if (b > a) return (false, 0);
return (true, a - b);
}
}
/**
* @dev Returns the multiplication of two unsigned integers, with an overflow flag.
*/
function tryMul(uint256 a, uint256 b) internal pure returns (bool, uint256) {
unchecked {
// Gas optimization: this is cheaper than requiring 'a' not being zero, but the
// benefit is lost if 'b' is also tested.
// See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522
if (a == 0) return (true, 0);
uint256 c = a * b;
if (c / a != b) return (false, 0);
return (true, c);
}
}
/**
* @dev Returns the division of two unsigned integers, with a division by zero flag.
*/
function tryDiv(uint256 a, uint256 b) internal pure returns (bool, uint256) {
unchecked {
if (b == 0) return (false, 0);
return (true, a / b);
}
}
/**
* @dev Returns the remainder of dividing two unsigned integers, with a division by zero flag.
*/
function tryMod(uint256 a, uint256 b) internal pure returns (bool, uint256) {
unchecked {
if (b == 0) return (false, 0);
return (true, a % b);
}
}
/**
* @dev Returns the largest of two numbers.
*/
function max(uint256 a, uint256 b) internal pure returns (uint256) {
return a > b ? a : b;
}
/**
* @dev Returns the smallest of two numbers.
*/
function min(uint256 a, uint256 b) internal pure returns (uint256) {
return a < b ? a : b;
}
/**
* @dev Returns the average of two numbers. The result is rounded towards
* zero.
*/
function average(uint256 a, uint256 b) internal pure returns (uint256) {
// (a + b) / 2 can overflow.
return (a & b) + (a ^ b) / 2;
}
/**
* @dev Returns the ceiling of the division of two numbers.
*
* This differs from standard division with `/` in that it rounds towards infinity instead
* of rounding towards zero.
*/
function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {
if (b == 0) {
// Guarantee the same behavior as in a regular Solidity division.
return a / b;
}
// (a + b - 1) / b can overflow on addition, so we distribute.
return a == 0 ? 0 : (a - 1) / b + 1;
}
/**
* @notice Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or
* denominator == 0.
* @dev Original credit to Remco Bloemen under MIT license (https://xn--2-umb.com/21/muldiv) with further edits by
* Uniswap Labs also under MIT license.
*/
function mulDiv(uint256 x, uint256 y, uint256 denominator) internal pure returns (uint256 result) {
unchecked {
// 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2^256 and mod 2^256 - 1, then use
// use the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256
// variables such that product = prod1 * 2^256 + prod0.
uint256 prod0 = x * y; // Least significant 256 bits of the product
uint256 prod1; // Most significant 256 bits of the product
assembly {
let mm := mulmod(x, y, not(0))
prod1 := sub(sub(mm, prod0), lt(mm, prod0))
}
// Handle non-overflow cases, 256 by 256 division.
if (prod1 == 0) {
// Solidity will revert if denominator == 0, unlike the div opcode on its own.
// The surrounding unchecked block does not change this fact.
// See https://docs.soliditylang.org/en/latest/control-structures.html#checked-or-unchecked-arithmetic.
return prod0 / denominator;
}
// Make sure the result is less than 2^256. Also prevents denominator == 0.
if (denominator <= prod1) {
revert MathOverflowedMulDiv();
}
///////////////////////////////////////////////
// 512 by 256 division.
///////////////////////////////////////////////
// Make division exact by subtracting the remainder from [prod1 prod0].
uint256 remainder;
assembly {
// Compute remainder using mulmod.
remainder := mulmod(x, y, denominator)
// Subtract 256 bit number from 512 bit number.
prod1 := sub(prod1, gt(remainder, prod0))
prod0 := sub(prod0, remainder)
}
// Factor powers of two out of denominator and compute largest power of two divisor of denominator.
// Always >= 1. See https://cs.stackexchange.com/q/138556/92363.
uint256 twos = denominator & (0 - denominator);
assembly {
// Divide denominator by twos.
denominator := div(denominator, twos)
// Divide [prod1 prod0] by twos.
prod0 := div(prod0, twos)
// Flip twos such that it is 2^256 / twos. If twos is zero, then it becomes one.
twos := add(div(sub(0, twos), twos), 1)
}
// Shift in bits from prod1 into prod0.
prod0 |= prod1 * twos;
// Invert denominator mod 2^256. Now that denominator is an odd number, it has an inverse modulo 2^256 such
// that denominator * inv = 1 mod 2^256. Compute the inverse by starting with a seed that is correct for
// four bits. That is, denominator * inv = 1 mod 2^4.
uint256 inverse = (3 * denominator) ^ 2;
// Use the Newton-Raphson iteration to improve the precision. Thanks to Hensel's lifting lemma, this also
// works in modular arithmetic, doubling the correct bits in each step.
inverse *= 2 - denominator * inverse; // inverse mod 2^8
inverse *= 2 - denominator * inverse; // inverse mod 2^16
inverse *= 2 - denominator * inverse; // inverse mod 2^32
inverse *= 2 - denominator * inverse; // inverse mod 2^64
inverse *= 2 - denominator * inverse; // inverse mod 2^128
inverse *= 2 - denominator * inverse; // inverse mod 2^256
// Because the division is now exact we can divide by multiplying with the modular inverse of denominator.
// This will give us the correct result modulo 2^256. Since the preconditions guarantee that the outcome is
// less than 2^256, this is the final result. We don't need to compute the high bits of the result and prod1
// is no longer required.
result = prod0 * inverse;
return result;
}
}
/**
* @notice Calculates x * y / denominator with full precision, following the selected rounding direction.
*/
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;
}
/**
* @dev Returns the square root of a number. If the number is not a perfect square, the value is rounded
* towards zero.
*
* Inspired by Henry S. Warren, Jr.'s "Hacker's Delight" (Chapter 11).
*/
function sqrt(uint256 a) internal pure returns (uint256) {
if (a == 0) {
return 0;
}
// For our first guess, we get the biggest power of 2 which is smaller than the square root of the target.
//
// We know that the "msb" (most significant bit) of our target number `a` is a power of 2 such that we have
// `msb(a) <= a < 2*msb(a)`. This value can be written `msb(a)=2**k` with `k=log2(a)`.
//
// This can be rewritten `2**log2(a) <= a < 2**(log2(a) + 1)`
// → `sqrt(2**k) <= sqrt(a) < sqrt(2**(k+1))`
// → `2**(k/2) <= sqrt(a) < 2**((k+1)/2) <= 2**(k/2 + 1)`
//
// Consequently, `2**(log2(a) / 2)` is a good first approximation of `sqrt(a)` with at least 1 correct bit.
uint256 result = 1 << (log2(a) >> 1);
// At this point `result` is an estimation with one bit of precision. We know the true value is a uint128,
// since it is the square root of a uint256. Newton's method converges quadratically (precision doubles at
// every iteration). We thus need at most 7 iteration to turn our partial result with one bit of precision
// into the expected uint128 result.
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);
}
}
/**
* @notice Calculates sqrt(a), following the selected rounding direction.
*/
function sqrt(uint256 a, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = sqrt(a);
return result + (unsignedRoundsUp(rounding) && result * result < a ? 1 : 0);
}
}
/**
* @dev Return the log in base 2 of a positive value rounded towards zero.
* Returns 0 if given 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;
}
/**
* @dev Return the log in base 2, following the selected rounding direction, of a positive value.
* Returns 0 if given 0.
*/
function log2(uint256 value, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = log2(value);
return result + (unsignedRoundsUp(rounding) && 1 << result < value ? 1 : 0);
}
}
/**
* @dev Return the log in base 10 of a positive value rounded towards zero.
* Returns 0 if given 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;
}
/**
* @dev Return the log in base 10, following the selected rounding direction, of a positive value.
* Returns 0 if given 0.
*/
function log10(uint256 value, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = log10(value);
return result + (unsignedRoundsUp(rounding) && 10 ** result < value ? 1 : 0);
}
}
/**
* @dev Return the log in base 256 of a positive value rounded towards zero.
* Returns 0 if given 0.
*
* Adding one to the result gives the number of pairs of hex symbols needed to represent `value` as a hex string.
*/
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;
}
/**
* @dev Return the log in base 256, following the selected rounding direction, of a positive value.
* Returns 0 if given 0.
*/
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);
}
}
/**
* @dev Returns whether a provided rounding mode is considered rounding up for unsigned integers.
*/
function unsignedRoundsUp(Rounding rounding) internal pure returns (bool) {
return uint8(rounding) % 2 == 1;
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/math/SignedMath.sol)
pragma solidity ^0.8.20;
/**
* @dev Standard signed math utilities missing in the Solidity language.
*/
library SignedMath {
/**
* @dev Returns the largest of two signed numbers.
*/
function max(int256 a, int256 b) internal pure returns (int256) {
return a > b ? a : b;
}
/**
* @dev Returns the smallest of two signed numbers.
*/
function min(int256 a, int256 b) internal pure returns (int256) {
return a < b ? a : b;
}
/**
* @dev Returns the average of two signed numbers without overflow.
* The result is rounded towards zero.
*/
function average(int256 a, int256 b) internal pure returns (int256) {
// Formula from the book "Hacker's Delight"
int256 x = (a & b) + ((a ^ b) >> 1);
return x + (int256(uint256(x) >> 255) & (a ^ b));
}
/**
* @dev Returns the absolute unsigned value of a signed value.
*/
function abs(int256 n) internal pure returns (uint256) {
unchecked {
// must be unchecked in order to support `n = type(int256).min`
return uint256(n >= 0 ? n : -n);
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/Strings.sol)
pragma solidity ^0.8.20;
import {Math} from "./math/Math.sol";
import {SignedMath} from "./math/SignedMath.sol";
/**
* @dev String operations.
*/
library Strings {
bytes16 private constant HEX_DIGITS = "0123456789abcdef";
uint8 private constant ADDRESS_LENGTH = 20;
/**
* @dev The `value` string doesn't fit in the specified `length`.
*/
error StringsInsufficientHexLength(uint256 value, uint256 length);
/**
* @dev Converts a `uint256` to its ASCII `string` decimal representation.
*/
function toString(uint256 value) internal pure returns (string memory) {
unchecked {
uint256 length = Math.log10(value) + 1;
string memory buffer = new string(length);
uint256 ptr;
/// @solidity memory-safe-assembly
assembly {
ptr := add(buffer, add(32, length))
}
while (true) {
ptr--;
/// @solidity memory-safe-assembly
assembly {
mstore8(ptr, byte(mod(value, 10), HEX_DIGITS))
}
value /= 10;
if (value == 0) break;
}
return buffer;
}
}
/**
* @dev Converts a `int256` to its ASCII `string` decimal representation.
*/
function toStringSigned(int256 value) internal pure returns (string memory) {
return string.concat(value < 0 ? "-" : "", toString(SignedMath.abs(value)));
}
/**
* @dev Converts a `uint256` to its ASCII `string` hexadecimal representation.
*/
function toHexString(uint256 value) internal pure returns (string memory) {
unchecked {
return toHexString(value, Math.log256(value) + 1);
}
}
/**
* @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length.
*/
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);
}
/**
* @dev Converts an `address` with fixed length of 20 bytes to its not checksummed ASCII `string` hexadecimal
* representation.
*/
function toHexString(address addr) internal pure returns (string memory) {
return toHexString(uint256(uint160(addr)), ADDRESS_LENGTH);
}
/**
* @dev Returns true if the two strings are equal.
*/
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));
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "./LaunchpadStorage.sol";
import "./Setup.sol";
import "./WinnerSelection.sol";
import "./Tickets.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
abstract contract Blacklist is LaunchpadStorage, Ownable, SetupModule, WinnerSelection {
using SafeERC20 for IERC20;
event UsersBlacklisted(address[] users);
event UsersRemovedFromBlacklist(address[] users);
function addUsersToBlacklist(address[] calldata usersList) external onlyOwner beforeWinnerSelection nonEmptyList(usersList) {
for (uint256 i = 0; i < usersList.length; i++) {
address user = usersList[i];
_processRefund(user);
state.blacklist[user] = true;
}
emit UsersBlacklisted(usersList);
}
function removeUsersFromBlacklist(address[] calldata usersList) external onlyOwner beforeWinnerSelection nonEmptyList(usersList) {
for (uint256 i = 0; i < usersList.length; i++) {
state.blacklist[usersList[i]] = false;
}
emit UsersRemovedFromBlacklist(usersList);
}
function _processRefund(address user) internal {
uint256 confirmedTickets = getNumberOfConfirmedTickets(user);
if (confirmedTickets > 0) {
uint256 refundAmount = confirmedTickets * (state.configuration.ticketPrice);
delete state.confirmedTicketsForAddress[user];
if (state.configuration.ticketToken == address(0)) {
(bool sent,) = user.call{value: refundAmount}("");
require(sent, "Failed to send native coin");
} else {
IERC20(state.configuration.ticketToken).transfer(user, refundAmount);
}
}
}
modifier nonEmptyList(address[] calldata usersList) {
require(usersList.length > 0, "Empty list");
_;
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "./LaunchpadStorage.sol";
import {Strings} from "@openzeppelin/contracts/utils/Strings.sol";
abstract contract ConfigurationModule is LaunchpadStorage {
// function getTimeline() public view returns (Timeline memory) {
// return state.configuration.timeline;
// }
function getShufflerSeed() public view returns (string memory) {
return Strings.toString(uint256(state.seed));
}
// function wereLaunchpadTokensDeposited() public view returns (bool) {
// return state.configuration.launchpadTokensDeposited;
// }
// function getLaunchpadTokenId() public view returns (address) {
// return state.configuration.launchpadToken;
// }
// function getLaunchpadTokensPerWinningTicket() public view returns (uint256) {
// return state.configuration.launchpadTokensPerWinningTicket;
// }
// function getTicketPrice() public view returns (uint256) {
// require(state.configuration.ticketPrice != 0, "Ticket price not set");
// return state.configuration.ticketPrice;
// }
// function getNumberOfWinningTickets() public view returns (NumberOfWinningTickets memory) {
// return state.configuration.numberOfWinningTickets;
// }
function setClaimableTicketPayment(uint256 ticketPayment) internal {
state.configuration.claimableTicketPayment = ticketPayment;
}
// function getClaimableTicketPayment() public view returns (uint256) {
// return state.configuration.claimableTicketPayment;
// }
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
/**
** @title Elliptic Curve Library
** @dev Library providing arithmetic operations over elliptic curves.
** This library does not check whether the inserted points belong to the curve
** `isOnCurve` function should be used by the library user to check the aforementioned statement.
** @author Witnet Foundation
*/
library EllipticCurve {
// Pre-computed constant for 2 ** 255
uint256 private constant U255_MAX_PLUS_1 =
57896044618658097711785492504343953926634992332820282019728792003956564819968;
/// @dev Modular euclidean inverse of a number (mod p).
/// @param _x The number
/// @param _pp The modulus
/// @return q such that x*q = 1 (mod _pp)
function invMod(uint256 _x, uint256 _pp) internal pure returns (uint256) {
require(_x != 0 && _x != _pp && _pp != 0, "Invalid number");
uint256 q = 0;
uint256 newT = 1;
uint256 r = _pp;
uint256 t;
while (_x != 0) {
t = r / _x;
(q, newT) = (newT, addmod(q, (_pp - mulmod(t, newT, _pp)), _pp));
(r, _x) = (_x, r - t * _x);
}
return q;
}
/// @dev Modular exponentiation, b^e % _pp.
/// Source: https://github.com/androlo/standard-contracts/blob/master/contracts/src/crypto/ECCMath.sol
/// @param _base base
/// @param _exp exponent
/// @param _pp modulus
/// @return r such that r = b**e (mod _pp)
function expMod(
uint256 _base,
uint256 _exp,
uint256 _pp
)
internal pure
returns (uint256)
{
require(_pp != 0, "EllipticCurve: modulus is zero");
if (_base == 0) return 0;
if (_exp == 0) return 1;
uint256 r = 1;
uint256 bit = U255_MAX_PLUS_1;
assembly {
for {
} gt(bit, 0) {
} {
r := mulmod(
mulmod(r, r, _pp),
exp(_base, iszero(iszero(and(_exp, bit)))),
_pp
)
r := mulmod(
mulmod(r, r, _pp),
exp(_base, iszero(iszero(and(_exp, div(bit, 2))))),
_pp
)
r := mulmod(
mulmod(r, r, _pp),
exp(_base, iszero(iszero(and(_exp, div(bit, 4))))),
_pp
)
r := mulmod(
mulmod(r, r, _pp),
exp(_base, iszero(iszero(and(_exp, div(bit, 8))))),
_pp
)
bit := div(bit, 16)
}
}
return r;
}
/// @dev Converts a point (x, y, z) expressed in Jacobian coordinates to affine coordinates (x', y', 1).
/// @param _x coordinate x
/// @param _y coordinate y
/// @param _z coordinate z
/// @param _pp the modulus
/// @return (x', y') affine coordinates
function toAffine(
uint256 _x,
uint256 _y,
uint256 _z,
uint256 _pp
)
internal pure
returns (uint256, uint256)
{
uint256 zInv = invMod(_z, _pp);
uint256 zInv2 = mulmod(zInv, zInv, _pp);
uint256 x2 = mulmod(_x, zInv2, _pp);
uint256 y2 = mulmod(_y, mulmod(zInv, zInv2, _pp), _pp);
return (x2, y2);
}
/// @dev Derives the y coordinate from a compressed-format point x [[SEC-1]](https://www.secg.org/SEC1-Ver-1.0.pdf).
/// @param _prefix parity byte (0x02 even, 0x03 odd)
/// @param _x coordinate x
/// @param _aa constant of curve
/// @param _bb constant of curve
/// @param _pp the modulus
/// @return y coordinate y
function deriveY(
uint8 _prefix,
uint256 _x,
uint256 _aa,
uint256 _bb,
uint256 _pp
)
internal pure
returns (uint256)
{
require(
_prefix == 0x02 || _prefix == 0x03,
"EllipticCurve:innvalid compressed EC point prefix"
);
// x^3 + ax + b
uint256 y2 = addmod(
mulmod(_x, mulmod(_x, _x, _pp), _pp),
addmod(mulmod(_x, _aa, _pp), _bb, _pp),
_pp
);
y2 = expMod(y2, (_pp + 1) / 4, _pp);
// uint256 cmp = yBit ^ y_ & 1;
uint256 y = (y2 + _prefix) % 2 == 0 ? y2 : _pp - y2;
return y;
}
/// @dev Check whether point (x,y) is on curve defined by a, b, and _pp.
/// @param _x coordinate x of P1
/// @param _y coordinate y of P1
/// @param _aa constant of curve
/// @param _bb constant of curve
/// @param _pp the modulus
/// @return true if x,y in the curve, false else
function isOnCurve(
uint _x,
uint _y,
uint _aa,
uint _bb,
uint _pp
)
internal pure
returns (bool)
{
if (0 == _x || _x >= _pp || 0 == _y || _y >= _pp) {
return false;
}
// y^2
uint lhs = mulmod(_y, _y, _pp);
// x^3
uint rhs = mulmod(mulmod(_x, _x, _pp), _x, _pp);
if (_aa != 0) {
// x^3 + a*x
rhs = addmod(rhs, mulmod(_x, _aa, _pp), _pp);
}
if (_bb != 0) {
// x^3 + a*x + b
rhs = addmod(rhs, _bb, _pp);
}
return lhs == rhs;
}
/// @dev Calculate inverse (x, -y) of point (x, y).
/// @param _x coordinate x of P1
/// @param _y coordinate y of P1
/// @param _pp the modulus
/// @return (x, -y)
function ecInv(
uint256 _x,
uint256 _y,
uint256 _pp
)
internal pure
returns (uint256, uint256)
{
return (_x, (_pp - _y) % _pp);
}
/// @dev Add two points (x1, y1) and (x2, y2) in affine coordinates.
/// @param _x1 coordinate x of P1
/// @param _y1 coordinate y of P1
/// @param _x2 coordinate x of P2
/// @param _y2 coordinate y of P2
/// @param _aa constant of the curve
/// @param _pp the modulus
/// @return (qx, qy) = P1+P2 in affine coordinates
function ecAdd(
uint256 _x1,
uint256 _y1,
uint256 _x2,
uint256 _y2,
uint256 _aa,
uint256 _pp
)
internal pure
returns (uint256, uint256)
{
uint x = 0;
uint y = 0;
uint z = 0;
// Double if x1==x2 else add
if (_x1 == _x2) {
// y1 = -y2 mod p
if (addmod(_y1, _y2, _pp) == 0) {
return (0, 0);
} else {
// P1 = P2
(x, y, z) = jacDouble(_x1, _y1, 1, _aa, _pp);
}
} else {
(x, y, z) = jacAdd(_x1, _y1, 1, _x2, _y2, 1, _pp);
}
// Get back to affine
return toAffine(x, y, z, _pp);
}
/// @dev Substract two points (x1, y1) and (x2, y2) in affine coordinates.
/// @param _x1 coordinate x of P1
/// @param _y1 coordinate y of P1
/// @param _x2 coordinate x of P2
/// @param _y2 coordinate y of P2
/// @param _aa constant of the curve
/// @param _pp the modulus
/// @return (qx, qy) = P1-P2 in affine coordinates
function ecSub(
uint256 _x1,
uint256 _y1,
uint256 _x2,
uint256 _y2,
uint256 _aa,
uint256 _pp
)
internal pure
returns (uint256, uint256)
{
// invert square
(uint256 x, uint256 y) = ecInv(_x2, _y2, _pp);
// P1-square
return ecAdd(_x1, _y1, x, y, _aa, _pp);
}
/// @dev Multiply point (x1, y1, z1) times d in affine coordinates.
/// @param _k scalar to multiply
/// @param _x coordinate x of P1
/// @param _y coordinate y of P1
/// @param _aa constant of the curve
/// @param _pp the modulus
/// @return (qx, qy) = d*P in affine coordinates
function ecMul(
uint256 _k,
uint256 _x,
uint256 _y,
uint256 _aa,
uint256 _pp
)
internal pure
returns (uint256, uint256)
{
// Jacobian multiplication
(uint256 x1, uint256 y1, uint256 z1) = jacMul(_k, _x, _y, 1, _aa, _pp);
// Get back to affine
return toAffine(x1, y1, z1, _pp);
}
/// @dev Adds two points (x1, y1, z1) and (x2 y2, z2).
/// @param _x1 coordinate x of P1
/// @param _y1 coordinate y of P1
/// @param _z1 coordinate z of P1
/// @param _x2 coordinate x of square
/// @param _y2 coordinate y of square
/// @param _z2 coordinate z of square
/// @param _pp the modulus
/// @return (qx, qy, qz) P1+square in Jacobian
function jacAdd(
uint256 _x1,
uint256 _y1,
uint256 _z1,
uint256 _x2,
uint256 _y2,
uint256 _z2,
uint256 _pp
)
internal pure
returns (uint256, uint256, uint256)
{
if (_x1 == 0 && _y1 == 0) return (_x2, _y2, _z2);
if (_x2 == 0 && _y2 == 0) return (_x1, _y1, _z1);
// We follow the equations described in https://pdfs.semanticscholar.org/5c64/29952e08025a9649c2b0ba32518e9a7fb5c2.pdf Section 5
uint[4] memory zs; // z1^2, z1^3, z2^2, z2^3
zs[0] = mulmod(_z1, _z1, _pp);
zs[1] = mulmod(_z1, zs[0], _pp);
zs[2] = mulmod(_z2, _z2, _pp);
zs[3] = mulmod(_z2, zs[2], _pp);
// u1, s1, u2, s2
zs = [
mulmod(_x1, zs[2], _pp),
mulmod(_y1, zs[3], _pp),
mulmod(_x2, zs[0], _pp),
mulmod(_y2, zs[1], _pp)
];
// In case of zs[0] == zs[2] && zs[1] == zs[3], double function should be used
require(
zs[0] != zs[2] || zs[1] != zs[3],
"Use jacDouble function instead"
);
uint[4] memory hr;
//h
hr[0] = addmod(zs[2], _pp - zs[0], _pp);
//r
hr[1] = addmod(zs[3], _pp - zs[1], _pp);
//h^2
hr[2] = mulmod(hr[0], hr[0], _pp);
// h^3
hr[3] = mulmod(hr[2], hr[0], _pp);
// qx = -h^3 -2u1h^2+r^2
uint256 qx = addmod(mulmod(hr[1], hr[1], _pp), _pp - hr[3], _pp);
qx = addmod(qx, _pp - mulmod(2, mulmod(zs[0], hr[2], _pp), _pp), _pp);
// qy = -s1*z1*h^3+r(u1*h^2 -x^3)
uint256 qy = mulmod(
hr[1],
addmod(mulmod(zs[0], hr[2], _pp), _pp - qx, _pp),
_pp
);
qy = addmod(qy, _pp - mulmod(zs[1], hr[3], _pp), _pp);
// qz = h*z1*z2
uint256 qz = mulmod(hr[0], mulmod(_z1, _z2, _pp), _pp);
return (qx, qy, qz);
}
/// @dev Doubles a points (x, y, z).
/// @param _x coordinate x of P1
/// @param _y coordinate y of P1
/// @param _z coordinate z of P1
/// @param _aa the a scalar in the curve equation
/// @param _pp the modulus
/// @return (qx, qy, qz) 2P in Jacobian
function jacDouble(
uint256 _x,
uint256 _y,
uint256 _z,
uint256 _aa,
uint256 _pp
)
internal pure
returns (uint256, uint256, uint256)
{
if (_z == 0) return (_x, _y, _z);
// We follow the equations described in https://pdfs.semanticscholar.org/5c64/29952e08025a9649c2b0ba32518e9a7fb5c2.pdf Section 5
// Note: there is a bug in the paper regarding the m parameter, M=3*(x1^2)+a*(z1^4)
// x, y, z at this point represent the squares of _x, _y, _z
uint256 x = mulmod(_x, _x, _pp); //x1^2
uint256 y = mulmod(_y, _y, _pp); //y1^2
uint256 z = mulmod(_z, _z, _pp); //z1^2
// s
uint s = mulmod(4, mulmod(_x, y, _pp), _pp);
// m
uint m = addmod(
mulmod(3, x, _pp),
mulmod(_aa, mulmod(z, z, _pp), _pp),
_pp
);
// x, y, z at this point will be reassigned and rather represent qx, qy, qz from the paper
// This allows to reduce the gas cost and stack footprint of the algorithm
// qx
x = addmod(mulmod(m, m, _pp), _pp - addmod(s, s, _pp), _pp);
// qy = -8*y1^4 + M(S-T)
y = addmod(
mulmod(m, addmod(s, _pp - x, _pp), _pp),
_pp - mulmod(8, mulmod(y, y, _pp), _pp),
_pp
);
// qz = 2*y1*z1
z = mulmod(2, mulmod(_y, _z, _pp), _pp);
return (x, y, z);
}
/// @dev Multiply point (x, y, z) times d.
/// @param _d scalar to multiply
/// @param _x coordinate x of P1
/// @param _y coordinate y of P1
/// @param _z coordinate z of P1
/// @param _aa constant of curve
/// @param _pp the modulus
/// @return (qx, qy, qz) d*P1 in Jacobian
function jacMul(
uint256 _d,
uint256 _x,
uint256 _y,
uint256 _z,
uint256 _aa,
uint256 _pp
)
internal pure
returns (uint256, uint256, uint256)
{
// Early return in case that `_d == 0`
if (_d == 0) {
return (_x, _y, _z);
}
uint256 remaining = _d;
uint256 qx = 0;
uint256 qy = 0;
uint256 qz = 1;
// Double and add algorithm
while (remaining != 0) {
if ((remaining & 1) != 0) {
(qx, qy, qz) = jacAdd(qx, qy, qz, _x, _y, _z, _pp);
}
remaining = remaining / 2;
(_x, _y, _z) = jacDouble(_x, _y, _z, _aa, _pp);
}
return (qx, qy, qz);
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/proxy/Clones.sol";
import "./Launchpad.sol";
contract LaunchpadFactory is Ownable {
using Clones for address;
address public immutable implementation;
mapping(address => LaunchpadConfig) public launchpadConfigs;
address[] public deployedLaunchpads;
event LaunchpadDeployed(
address indexed launchpadAddress,
address indexed tokenAddress,
uint256 ticketPrice,
uint256 nrWinningTickets,
uint256 deploymentTimestamp
);
struct LaunchpadConfig {
bytes proofZero;
bytes publicKeyBytes;
address launchpadTokenId;
uint256 launchpadTokensPerWinningTicket;
address ticketPaymentToken;
uint256 ticketPrice;
uint256 nrWinningTickets;
uint256 confirmationPeriodStartTime;
uint256 winnerSelectionStartTime;
uint256 claimStartTime;
address initialOwner;
bytes winnerSelectionScriptHash;
}
constructor(address _implementation) Ownable(msg.sender) {
implementation = _implementation;
}
function deployLaunchpad(LaunchpadConfig calldata config) external onlyOwner returns (address) {
// Create a clone using minimal proxy pattern
address launchpadAddress = implementation.clone();
// Initialize the clone
Launchpad(launchpadAddress).initialize(
config.proofZero,
config.publicKeyBytes,
config.launchpadTokenId,
config.launchpadTokensPerWinningTicket,
config.ticketPaymentToken,
config.ticketPrice,
config.nrWinningTickets,
config.confirmationPeriodStartTime,
config.winnerSelectionStartTime,
config.claimStartTime,
config.initialOwner,
config.winnerSelectionScriptHash
);
// Store config and update deployed launchpads
launchpadConfigs[launchpadAddress] = config;
deployedLaunchpads.push(launchpadAddress);
emit LaunchpadDeployed(
launchpadAddress,
config.launchpadTokenId,
config.ticketPrice,
config.nrWinningTickets,
block.timestamp
);
return launchpadAddress;
}
// function getLaunchpadConfig(address _launchpadAddress) external view returns (LaunchpadConfig memory) {
// return launchpadConfigs[_launchpadAddress];
// }
// function getDeployedLaunchpadsCount() external view returns (uint256) {
// return deployedLaunchpads.length;
// }
// function getDeployedLaunchpadAt(uint256 _index) external view returns (address) {
// require(_index < deployedLaunchpads.length, "Index out of bounds");
// return deployedLaunchpads[_index];
// }
function getLastDeployedLaunchpad() external view returns (address launchpadAddress, LaunchpadConfig memory config) {
require(deployedLaunchpads.length > 0, "No launchpads deployed");
launchpadAddress = deployedLaunchpads[deployedLaunchpads.length - 1];
config = launchpadConfigs[launchpadAddress];
return (launchpadAddress, config);
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
abstract contract LaunchpadStorage {
struct ProjectInfo {
// Token related
address ticketToken; // from getTicketToken() - need to add this getter
address launchpadToken; // from getLaunchpadTokenId()
uint256 launchpadTokensPerWinningTicket; // from getLaunchpadTokensPerWinningTicket()
uint256 pricePerToken; // from getTicketPrice()
// Status related
bool tokensDeposited; // from wereLaunchpadTokensDeposited()
LaunchStage currentPhase; // from getLaunchStage()
Flags flags; // from getFlags()
// Configuration related
Timeline timeline; // from getTimeline()
NumberOfWinningTickets numberOfWinningTickets; // from getNumberOfWinningTickets()
bytes winnerSelectionScriptHash;
}
enum LaunchStage {
AddTickets,
Confirm,
WinnerSelection,
Claim
}
struct Flags {
bool hasWinnerSelectionProcessStarted;
bool wereTicketsFiltered;
bool winnersSelected;
bool launchpadNfts;
}
struct Timeline {
uint256 confirmationPeriodStartTime;
uint256 winnerSelectionStartTime;
uint256 claimStartTime;
}
enum NumberOfWinningTicketsType {BeforeFiltering, AfterFiltering}
struct NumberOfWinningTickets {
NumberOfWinningTicketsType ticketType;
uint256 value;
}
struct ConfigModule {
Timeline timeline;
address ticketToken;
address launchpadToken;
uint256 launchpadTokensPerWinningTicket;
uint256 ticketPrice;
NumberOfWinningTickets numberOfWinningTickets;
bool launchpadTokensDeposited;
uint256 claimableTicketPayment;
}
struct TicketBatch {
address addr;
uint256 nrTickets;
}
struct TicketRange {
uint256 firstId;
uint256 lastId;
uint256 guaranteedWinners;
}
struct LaunchpadState {
Flags flags;
ConfigModule configuration;
uint256 lastTicketId;
uint256 totalLaunchpadTokens;
mapping(uint256 => bool) winningTickets;
uint256 confirmedGuaranteedTickets;
bool ownerPaymentClaimed;
mapping(uint256 => TicketBatch) ticketBatch;
mapping(address => TicketRange) ticketRangeForAddress;
mapping(address => uint256) confirmedTicketsForAddress;
mapping(address => bool) blacklist;
uint256 totalGuaranteedWinningTickets;
uint256 filterTicketsProgressStart;
uint256 filterTicketsProgressEnd;
mapping(address => bool) claimList;
mapping(address => uint256) numberOfWinningTicketsPerUser;
uint256[2] publicKey;
bytes32 seed;
bytes proofBytes;
mapping(uint256 => uint256) winnersBitmap;
uint256 shufflableTickets; // number of tickets that can be shuffled, considering guaranteed tickets
bytes winnerSelectionScriptHash;
}
LaunchpadState internal state;
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "./LaunchpadStorage.sol";
abstract contract LaunchStageModule is LaunchpadStorage {
function getLaunchStage() public view returns (LaunchStage) {
uint256 currentTime = block.timestamp;
Timeline memory timeline = state.configuration.timeline;
// Determine the launch stage based on the current time and timeline
if (currentTime < timeline.confirmationPeriodStartTime) {
// Before confirmation period starts
return LaunchStage.AddTickets;
}
if (currentTime < timeline.winnerSelectionStartTime) {
// During confirmation period
return LaunchStage.Confirm;
}
if (currentTime < timeline.claimStartTime) {
// Before claim start time
return LaunchStage.WinnerSelection;
}
// After claim start time
return LaunchStage.Claim;
}
modifier requireAddTicketsPeriod() {
LaunchStage currentStage = getLaunchStage();
require(currentStage == LaunchStage.AddTickets, "Add tickets period has passed");
_;
}
modifier requireConfirmationPeriod() {
LaunchStage currentStage = getLaunchStage();
require(currentStage == LaunchStage.Confirm, "Not in confirmation period");
_;
}
modifier requireBeforeWinnerSelectionPeriod() {
LaunchStage currentStage = getLaunchStage();
require(currentStage < LaunchStage.WinnerSelection, "Add tickets period has passed");
_;
}
modifier requireWinnerSelectionPeriod() {
LaunchStage currentStage = getLaunchStage();
require(currentStage == LaunchStage.WinnerSelection, "Not in winner selection period");
_;
}
modifier requireClaimPeriod() {
LaunchStage currentStage = getLaunchStage();
require(currentStage == LaunchStage.Claim, "Not in claim period");
//require(state.flags.winnersSelected, "Winners not selected yet");
_;
}
// function getLaunchpadStageFlags() external view returns (Flags memory) {
// return state.flags;
// }
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "./LaunchpadStorage.sol";
import "./WinnerSelection.sol";
import {ERC721} from "@openzeppelin/contracts/token/ERC721/ERC721.sol";
import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol";
abstract contract NFT is ERC721, LaunchStageModule, WinnerSelection{
struct LaunchpadNftMetadata {
uint256 numberOfWinningTickets;
}
uint256 private tokenCounter;
mapping(uint256 => LaunchpadNftMetadata) private tokenIdToState;
mapping(address => uint256) private addressToTokenId;
event CreatedNFT(uint256 tokenId);
constructor() ERC721("Launchpad NFT", "LNFT") {
tokenCounter = 0;
}
function claimNft() public
requireClaimPeriod
requireNftLaunchpadEnabled
requireTokensClaimed
{
uint256 tokenId = tokenCounter;
_safeMint(msg.sender, tokenId);
uint256 winningTicketsPerUser = getNumberOfWinningPerUser(msg.sender);
tokenIdToState[tokenId] = LaunchpadNftMetadata(winningTicketsPerUser);
addressToTokenId[msg.sender] = tokenId;
tokenCounter ++;
emit CreatedNFT(tokenCounter);
}
function getTokenCounter() public view returns (uint256) {
return tokenCounter;
}
function setNftLaunchpad() public onlyOwner {
state.flags.launchpadNfts = true;
}
function getNftLaunchpadInfo(address owner) public view returns (LaunchpadNftMetadata memory) {
uint256 tokenId = addressToTokenId[owner];
return tokenIdToState[tokenId];
}
modifier requireNftLaunchpadEnabled() {
require(state.flags.launchpadNfts, "NFT launchpad is not enabled");
_;
}
modifier requireTokensClaimed() {
require(state.claimList[msg.sender], "Tokens not claimed yet");
_;
}
function transferFrom(address, address, uint256) public pure override(ERC721) {
revert("Transfers are not allowed");
}
function safeTransferFrom(address, address, uint256, bytes memory) public pure override(ERC721) {
revert("Transfers are not allowed");
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "./LaunchpadStorage.sol";
import "./LaunchStage.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
abstract contract SetupModule is LaunchpadStorage, Ownable, LaunchStageModule {
using SafeERC20 for IERC20;
event LaunchpadTokensDeposited(uint256 amount, address token);
event TicketPriceSet(uint256 amount, address token);
event LaunchpadTokensPerWinningTicketSet(uint256 amount);
event TicketTokenSet(address token);
event LaunchpadTokenSet(address token);
event ConfirmationPeriodStartTimeSet(uint256 startTime);
event WinnerSelectionStartTimeSet(uint256 startTime);
event ClaimStartTimeSet(uint256 startTime);
function depositInitialLaunchpadTokens() public onlyOwner beforeWinnerSelection {
depositLaunchpadTokens(state.configuration.numberOfWinningTickets.value, false);
}
// function depositExtraLaunchpadTokens(uint256 extraWinningTickets) public onlyOwner beforeWinnerSelection {
// depositLaunchpadTokens(extraWinningTickets, true);
// }
function depositLaunchpadTokens(uint256 totalWinningTickets, bool extra) internal onlyOwner beforeWinnerSelection {
require(!state.configuration.launchpadTokensDeposited || extra, "Tokens already deposited");
require(state.configuration.launchpadTokensDeposited || !extra, "Deposit initial launchpad tokens before adding extra");
if (!extra) {
require(totalWinningTickets == state.configuration.numberOfWinningTickets.value, "Incorrect deposit amount");
}
uint256 amountPerTicket = state.configuration.launchpadTokensPerWinningTicket;
uint256 amountNeeded = amountPerTicket * totalWinningTickets;
IERC20 launchpadToken = IERC20(state.configuration.launchpadToken);
uint256 balanceBefore = launchpadToken.balanceOf(address(this));
launchpadToken.transferFrom(msg.sender, address(this), amountNeeded);
uint256 balanceAfter = launchpadToken.balanceOf(address(this));
uint256 amountDeposited = balanceAfter - balanceBefore;
state.totalLaunchpadTokens = state.totalLaunchpadTokens + amountDeposited;
if (extra) {
state.configuration.numberOfWinningTickets = addToNumberOfWinningTickets(
state.configuration.numberOfWinningTickets,
totalWinningTickets
);
}
state.configuration.launchpadTokensDeposited = true;
emit LaunchpadTokensDeposited(amountDeposited, address(launchpadToken));
}
// function setTicketPrice(uint256 amount) public onlyOwner requireAddTicketsPeriod {
// require(amount > 0, "Ticket price must be higher than 0");
// state.configuration.ticketPrice = amount;
// emit TicketPriceSet(amount, state.configuration.ticketToken);
// }
// function setLaunchpadTokensPerWinningTicket(uint256 amount) public onlyOwner requireAddTicketsPeriod {
// require(amount > 0, "Launchpad tokens per winning ticket cannot be set to zero");
// state.configuration.launchpadTokensPerWinningTicket = amount;
// emit LaunchpadTokensPerWinningTicketSet(amount);
// }
// function setTicketToken(address newTicketToken) public onlyOwner requireAddTicketsPeriod {
// require(newTicketToken != address(0), "Invalid token address");
// state.configuration.ticketToken = newTicketToken;
// emit TicketTokenSet(newTicketToken);
// }
// function setLaunchpadToken(address newLaunchpadToken) public onlyOwner requireAddTicketsPeriod {
// require(newLaunchpadToken != address(0), "Invalid token address");
// state.configuration.launchpadToken = newLaunchpadToken;
// emit LaunchpadTokenSet(newLaunchpadToken);
// }
// function setConfirmationPeriodStartTime(uint256 newStartTime) public onlyOwner {
// require(newStartTime > block.timestamp, "New start time must be in the future");
// require(newStartTime < state.configuration.timeline.winnerSelectionStartTime, "Must be before winner selection");
// state.configuration.timeline.confirmationPeriodStartTime = newStartTime;
// emit ConfirmationPeriodStartTimeSet(newStartTime);
// }
// function setWinnerSelectionStartTime(uint256 newStartTime) public onlyOwner {
// require(newStartTime > block.timestamp, "New start time must be in the future");
// require(newStartTime > state.configuration.timeline.confirmationPeriodStartTime, "Must be after confirmation period");
// require(newStartTime < state.configuration.timeline.claimStartTime, "Must be before claim period");
// state.configuration.timeline.winnerSelectionStartTime = newStartTime;
// emit WinnerSelectionStartTimeSet(newStartTime);
// }
// function setClaimStartTime(uint256 newStartTime) public onlyOwner {
// require(newStartTime > block.timestamp, "New start time must be in the future");
// require(newStartTime > state.configuration.timeline.winnerSelectionStartTime, "Must be after winner selection");
// state.configuration.timeline.claimStartTime = newStartTime;
// emit ClaimStartTimeSet(newStartTime);
// }
modifier beforeWinnerSelection() {
require(getLaunchStage() < LaunchStage.WinnerSelection, "Winner selection has already started");
_;
}
function addToNumberOfWinningTickets(NumberOfWinningTickets memory nwt, uint256 value) internal pure returns (NumberOfWinningTickets memory) {
return NumberOfWinningTickets({
ticketType: nwt.ticketType,
value: nwt.value + value
});
}
function getProjectInfo() public view returns (ProjectInfo memory) {
return ProjectInfo({
ticketToken: state.configuration.ticketToken,
launchpadToken: state.configuration.launchpadToken,
launchpadTokensPerWinningTicket: state.configuration.launchpadTokensPerWinningTicket,
pricePerToken: state.configuration.ticketPrice,
tokensDeposited: state.configuration.launchpadTokensDeposited,
currentPhase: getLaunchStage(),
flags: state.flags,
timeline: state.configuration.timeline,
numberOfWinningTickets: state.configuration.numberOfWinningTickets,
winnerSelectionScriptHash: state.winnerSelectionScriptHash
});
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "./LaunchpadStorage.sol";
import "./LaunchStage.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
abstract contract Tickets is LaunchpadStorage, Ownable, LaunchStageModule {
using SafeERC20 for IERC20;
event TicketsAdded(uint256 uniqueAddressesAddedCount);
event TicketPaymentClaimed(uint256 claimedTicketPayment, uint256 extraLaunchpadTokens);
// function addTickets(address[] memory buyers, uint256[] memory confirmableTickets, uint256[] memory guaranteedWinning)
// public onlyOwner requireBeforeWinnerSelectionPeriod
// {
// require(buyers.length == confirmableTickets.length && buyers.length == guaranteedWinning.length, "Input arrays must have the same length");
// uint256 uniqueCount = 0;
// uint256 totalWinning = state.totalGuaranteedWinningTickets;
// uint256 maxWinning = state.configuration.numberOfWinningTickets.value;
// for (uint256 i = 0; i < buyers.length; i++) {
// if (tryCreateTickets(buyers[i], confirmableTickets[i], guaranteedWinning[i])) {
// require(confirmableTickets[i] >= guaranteedWinning[i], "Can't add more guaranteed winning than confirmable tickets");
// uniqueCount++;
// totalWinning += guaranteedWinning[i];
// require(totalWinning <= maxWinning, "Attempted to add too many guaranteed winning tickets");
// }
// }
// require(uniqueCount > 0, "All input addresses have already been added to the launchpad");
// state.totalGuaranteedWinningTickets = totalWinning;
// emit TicketsAdded(uniqueCount);
// }
function tryCreateTickets(address buyer, uint256 nrTickets, uint256 guaranteedWinners) internal returns (bool) {
if (state.ticketRangeForAddress[buyer].lastId != 0) {
return false;
}
uint256 firstTicketId = state.lastTicketId + 1;
uint256 lastTicketId = firstTicketId + nrTickets - 1;
state.ticketRangeForAddress[buyer] = TicketRange({
firstId: firstTicketId,
lastId: lastTicketId,
guaranteedWinners: guaranteedWinners
});
state.ticketBatch[firstTicketId] = TicketBatch({
addr: buyer,
nrTickets: nrTickets
});
state.lastTicketId = lastTicketId;
return true;
}
function claimTicketPayment() public onlyOwner requireClaimPeriod {
require(!state.ownerPaymentClaimed, "Ticket payment + leftover launchpad token refund for owner already claimed");
state.ownerPaymentClaimed = true;
uint256 claimableTicketPayment = state.configuration.claimableTicketPayment;
uint256 extraLaunchpadTokens = 0;
if (claimableTicketPayment > 0 && state.flags.winnersSelected) {
// Transfer ticket payment to owner
if (state.configuration.ticketToken == address(0)) {
(bool sent,) = owner().call{value: claimableTicketPayment}("");
require(sent, "Failed to send native coin");
} else {
IERC20(state.configuration.ticketToken).transfer(owner(), claimableTicketPayment);
}
}
uint256 launchpadTokensBalance = state.totalLaunchpadTokens;
uint256 numberOfWinningTickets = state.flags.winnersSelected ?
state.configuration.numberOfWinningTickets.value : 0;
uint256 launchpadTokensNeeded = state.configuration.launchpadTokensPerWinningTicket * numberOfWinningTickets;
if (launchpadTokensBalance > launchpadTokensNeeded) {
extraLaunchpadTokens = launchpadTokensBalance - launchpadTokensNeeded;
// Transfer extra launchpad tokens to owner
// Assuming launchpadToken is an ERC20 token
IERC20(state.configuration.launchpadToken).transfer(owner(), extraLaunchpadTokens);
}
emit TicketPaymentClaimed(claimableTicketPayment, extraLaunchpadTokens);
}
function getTotalNumberOfTicketsForAddress(address addr) public view returns (uint256) {
TicketRange memory range = state.ticketRangeForAddress[addr];
if (range.lastId == 0) return 0;
return range.lastId - range.firstId + 1;
}
// function getTotalNumberOfTickets() public view returns (uint256) {
// return state.lastTicketId;
// }
// function clearTicketPos(uint256 ticketPos) internal {
// delete state.ticketPositionForTicketId[ticketPos];
// }
// function getTotalConfirmedTickets() public view returns (uint256) {
// return state.lastTicketId;
// }
function setNumberOfConfirmedTickets(address addr, uint256 tickets) internal {
state.confirmedTicketsForAddress[addr] = tickets;
}
function getLastTicketId() public view returns (uint256) {
return state.lastTicketId;
}
// function getAddTicketsPeriodEndTime() public view returns (uint256) {
// return state.configuration.timeline.confirmationPeriodStartTime;
// }
// function getWinnerSelectionStartTime() public view returns (uint256) {
// return state.configuration.timeline.winnerSelectionStartTime;
// }
// function getClaimStartTime() public view returns (uint256) {
// return state.configuration.timeline.claimStartTime;
// }
function numberOfWinningTicketsToUint256(NumberOfWinningTickets memory nwt) internal pure returns (uint256) {
return nwt.value;
}
function clearTickets(address addr) internal {
delete state.confirmedTicketsForAddress[addr];
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "./LaunchpadStorage.sol";
import "./LaunchStage.sol";
import "./Tickets.sol";
import "./WinnerSelection.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
uint256 constant MAX_TICKETS_PER_ADDRESS = 100;
abstract contract UserInteractions is LaunchpadStorage, Ownable, LaunchStageModule, Tickets, WinnerSelection {
using SafeERC20 for IERC20;
event TicketsConfirmed(address indexed user, uint256 ticketsConfirmed);
event TokensClaimed(address indexed user, uint256 redeemableTickets, uint256 refundedTickets);
function confirmTickets(
address payer,
address receiver,
uint256 nrTicketsToConfirm
) payable public requireConfirmationPeriod {
address actualPayer = payer == address(0) ? msg.sender : payer;
address actualReceiver = receiver == address(0) ? msg.sender : receiver;
require(nrTicketsToConfirm > 0, "Must confirm at least one ticket");
require(state.configuration.launchpadTokensDeposited, "Launchpad tokens not deposited yet");
requireUserIsNotBlacklisted(msg.sender);
requireUserIsNotBlacklisted(actualReceiver);
// Get current confirmed tickets for this address
uint256 nrConfirmed = getNumberOfConfirmedTickets(actualReceiver);
// Check if user is trying to confirm more than the maximum allowed
require(nrConfirmed + nrTicketsToConfirm <= MAX_TICKETS_PER_ADDRESS, "Exceeds maximum tickets per address");
// Calculate total price
uint256 ticketPrice = state.configuration.ticketPrice;
uint256 totalPrice = ticketPrice * nrTicketsToConfirm;
// Handle payment
if (state.configuration.ticketToken == address(0)) {
require(msg.value == totalPrice, "Funds in message should match total required price");
} else {
IERC20(state.configuration.ticketToken).transferFrom(actualPayer, address(this), totalPrice);
}
// If this is the first time the address is confirming tickets, create a new ticket range
if (state.ticketRangeForAddress[actualReceiver].firstId == 0) {
uint256 firstId = state.lastTicketId + 1;
uint256 lastId = firstId + MAX_TICKETS_PER_ADDRESS - 1; // Reserve the full range
state.ticketRangeForAddress[actualReceiver] = TicketRange({
firstId: firstId,
lastId: lastId,
guaranteedWinners: 0 // No guaranteed winners in the new system
});
// Create ticket batch record
state.ticketBatch[firstId] = TicketBatch({
addr: actualReceiver,
nrTickets: MAX_TICKETS_PER_ADDRESS // Reserve full allocation
});
state.lastTicketId = lastId; // Update last ticket ID
}
// Update confirmed tickets count
setNumberOfConfirmedTickets(actualReceiver, nrConfirmed + nrTicketsToConfirm);
// Update seed for randomness
state.seed = keccak256(bytes.concat(state.seed, keccak256(bytes.concat(bytes32(nrConfirmed + nrTicketsToConfirm)))));
emit TicketsConfirmed(actualReceiver, nrTicketsToConfirm);
}
function requireUserIsNotBlacklisted(address user) internal view {
require(!state.blacklist[user], "User is blacklisted and may not confirm tickets");
}
// TODO: review guarantee winners path
function claimLaunchpadTokens() public requireClaimPeriod {
require(!state.claimList[msg.sender], "Already claimed");
TicketRange memory ticketRange = getTicketRangeForAddress(msg.sender);
uint256 nrConfirmedTickets = ticketRange.guaranteedWinners;
uint256 nrRedeemableTickets = ticketRange.guaranteedWinners;
state.claimList[msg.sender] = true;
if (!state.flags.winnersSelected) {
uint256 confirmedTickets = getNumberOfConfirmedTickets(msg.sender);
uint256 refundAmount = state.configuration.ticketPrice * confirmedTickets;
if (state.configuration.ticketToken == address(0)) {
(bool sent,) = msg.sender.call{value: refundAmount}("");
require(sent, "Failed to send native coin");
} else {
IERC20(state.configuration.ticketToken).transfer(msg.sender, refundAmount);
}
emit TokensClaimed(msg.sender, 0, confirmedTickets);
} else {
for (uint256 ticketId = ticketRange.firstId; ticketId <= ticketRange.lastId; ticketId++) {
nrConfirmedTickets = nrConfirmedTickets + 1;
if (isWinningTicket(msg.sender, ticketId)) {
delete state.winningTickets[ticketId];
nrRedeemableTickets = nrRedeemableTickets + 1;
}
//clearTicketPos(ticketId);
}
state.numberOfWinningTicketsPerUser[msg.sender] = nrRedeemableTickets;
clearTickets(msg.sender);
uint256 nrTicketsToRefund = nrConfirmedTickets - nrRedeemableTickets;
uint256 ticketPaymentRefundAmount = state.configuration.ticketPrice * nrTicketsToRefund;
if (state.configuration.ticketToken == address(0)) {
(bool sent,) = msg.sender.call{value: ticketPaymentRefundAmount}("");
require(sent, "Failed to send native coin");
} else {
IERC20(state.configuration.ticketToken).transfer(msg.sender, ticketPaymentRefundAmount);
}
uint256 tokensPerWinningTicket = state.configuration.launchpadTokensPerWinningTicket;
uint256 launchpadTokensAmountToSend = nrRedeemableTickets * tokensPerWinningTicket;
if (launchpadTokensAmountToSend > 0) {
IERC20(state.configuration.launchpadToken).transfer(msg.sender, launchpadTokensAmountToSend);
}
emit TokensClaimed(msg.sender, nrRedeemableTickets, nrTicketsToRefund);
}
}
function hasUserClaimed(address user) public view returns (bool) {
return state.claimList[user];
}
}// SPDX-License-Identifier: MIT
pragma solidity >=0.5.3;
import "./EllipticCurve.sol";
/**
* @title Verifiable Random Functions (VRF)
* @notice Library verifying VRF proofs using the `Secp256k1` curve and the `SHA256` hash function.
* @dev This library follows the algorithms described in [VRF-draft-04](https://tools.ietf.org/pdf/draft-irtf-cfrg-vrf-04) and [RFC6979](https://tools.ietf.org/html/rfc6979).
* It supports the _SECP256K1_SHA256_TAI_ cipher suite, i.e. the aforementioned algorithms using `SHA256` and the `Secp256k1` curve.
* @author Witnet Foundation
*/
library VRF {
/**
* Secp256k1 parameters
*/
// Generator coordinate `x` of the EC curve
uint256 public constant GX = 0x79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798;
// Generator coordinate `y` of the EC curve
uint256 public constant GY = 0x483ADA7726A3C4655DA4FBFC0E1108A8FD17B448A68554199C47D08FFB10D4B8;
// Constant `a` of EC equation
uint256 public constant AA = 0;
// Constant `b` of EC equation
uint256 public constant BB = 7;
// Prime number of the curve
uint256 public constant PP = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F;
// Order of the curve
uint256 public constant NN = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141;
/// @dev Public key derivation from private key.
/// Warning: this function should not be used to derive your public key as it would expose the private key.
/// @param _d The scalar
/// @param _x The coordinate x
/// @param _y The coordinate y
/// @return (qx, qy) The derived point
function derivePoint(uint256 _d, uint256 _x, uint256 _y) internal pure returns (uint256, uint256) {
return EllipticCurve.ecMul(
_d,
_x,
_y,
AA,
PP
);
}
/// @dev Function to derive the `y` coordinate given the `x` coordinate and the parity byte (`0x03` for odd `y` and `0x04` for even `y`).
/// @param _yByte The parity byte following the ec point compressed format
/// @param _x The coordinate `x` of the point
/// @return The coordinate `y` of the point
function deriveY(uint8 _yByte, uint256 _x) internal pure returns (uint256) {
return EllipticCurve.deriveY(
_yByte,
_x,
AA,
BB,
PP);
}
/// @dev Computes the VRF hash output as result of the digest of a ciphersuite-dependent prefix
/// concatenated with the gamma point
/// @param _gammaX The x-coordinate of the gamma EC point
/// @param _gammaY The y-coordinate of the gamma EC point
/// @return The VRF hash ouput as shas256 digest
function gammaToHash(uint256 _gammaX, uint256 _gammaY) internal pure returns (bytes32) {
bytes memory c = abi.encodePacked(
// Cipher suite code (SECP256K1-SHA256-TAI is 0xFE)
uint8(0xFE),
// 0x03
uint8(0x03),
// Compressed Gamma Point
encodePoint(_gammaX, _gammaY));
return sha256(c);
}
/// @dev VRF verification by providing the public key, the message and the VRF proof.
/// This function computes several elliptic curve operations which may lead to extensive gas consumption.
/// @param _publicKey The public key as an array composed of `[pubKey-x, pubKey-y]`
/// @param _proof The VRF proof as an array composed of `[gamma-x, gamma-y, c, s]`
/// @param _message The message (in bytes) used for computing the VRF
/// @return true, if VRF proof is valid
function verify(uint256[2] memory _publicKey, uint256[4] memory _proof, bytes memory _message) internal pure returns (bool) {
// Step 2: Hash to try and increment (outputs a hashed value, a finite EC point in G)
(uint256 hPointX, uint256 hPointY) = hashToTryAndIncrement(_publicKey, _message);
// Step 3: U = s*B - c*Y (where B is the generator)
(uint256 uPointX, uint256 uPointY) = ecMulSubMul(
_proof[3],
GX,
GY,
_proof[2],
_publicKey[0],
_publicKey[1]);
// Step 4: V = s*H - c*Gamma
(uint256 vPointX, uint256 vPointY) = ecMulSubMul(
_proof[3],
hPointX,
hPointY,
_proof[2],
_proof[0],_proof[1]);
// Step 5: derived c from hash points(...)
bytes16 derivedC = hashPoints(
hPointX,
hPointY,
_proof[0],
_proof[1],
uPointX,
uPointY,
vPointX,
vPointY);
// Step 6: Check validity c == c'
return uint128(derivedC) == _proof[2];
}
/// @dev VRF fast verification by providing the public key, the message, the VRF proof and several intermediate elliptic curve points that enable the verification shortcut.
/// This function leverages the EVM's `ecrecover` precompile to verify elliptic curve multiplications by decreasing the security from 32 to 20 bytes.
/// Based on the original idea of Vitalik Buterin: https://ethresear.ch/t/you-can-kinda-abuse-ecrecover-to-do-ecmul-in-secp256k1-today/2384/9
/// @param _publicKey The public key as an array composed of `[pubKey-x, pubKey-y]`
/// @param _proof The VRF proof as an array composed of `[gamma-x, gamma-y, c, s]`
/// @param _message The message (in bytes) used for computing the VRF
/// @param _uPoint The `u` EC point defined as `U = s*B - c*Y`
/// @param _vComponents The components required to compute `v` as `V = s*H - c*Gamma`
/// @return true, if VRF proof is valid
function fastVerify(
uint256[2] memory _publicKey,
uint256[4] memory _proof,
bytes memory _message,
uint256[2] memory _uPoint,
uint256[4] memory _vComponents)
internal pure returns (bool)
{
// Step 2: Hash to try and increment -> hashed value, a finite EC point in G
(uint256 hPointX, uint256 hPointY) = hashToTryAndIncrement(_publicKey, _message);
// Step 3 & Step 4:
// U = s*B - c*Y (where B is the generator)
// V = s*H - c*Gamma
if (!ecMulSubMulVerify(
_proof[3],
_proof[2],
_publicKey[0],
_publicKey[1],
_uPoint[0],
_uPoint[1]) ||
!ecMulVerify(
_proof[3],
hPointX,
hPointY,
_vComponents[0],
_vComponents[1]) ||
!ecMulVerify(
_proof[2],
_proof[0],
_proof[1],
_vComponents[2],
_vComponents[3])
)
{
return false;
}
(uint256 vPointX, uint256 vPointY) = EllipticCurve.ecSub(
_vComponents[0],
_vComponents[1],
_vComponents[2],
_vComponents[3],
AA,
PP);
// Step 5: derived c from hash points(...)
bytes16 derivedC = hashPoints(
hPointX,
hPointY,
_proof[0],
_proof[1],
_uPoint[0],
_uPoint[1],
vPointX,
vPointY);
// Step 6: Check validity c == c'
return uint128(derivedC) == _proof[2];
}
/// @dev Decode VRF proof from bytes
/// @param _proof The VRF proof as bytes
/// @return The VRF proof as an array composed of `[gamma-x, gamma-y, c, s]`
function decodeProof(bytes memory _proof) internal pure returns (uint[4] memory) {
require(_proof.length == 81, "Malformed VRF proof");
uint8 gammaSign;
uint256 gammaX;
uint128 c;
uint256 s;
assembly {
gammaSign := mload(add(_proof, 1))
gammaX := mload(add(_proof, 33))
c := mload(add(_proof, 49))
s := mload(add(_proof, 81))
}
uint256 gammaY = deriveY(gammaSign, gammaX);
return [
gammaX,
gammaY,
c,
s];
}
/// @dev Decode EC point from bytes
/// @param _point The EC point as bytes
/// @return The point as `[point-x, point-y]`
function decodePoint(bytes memory _point) internal pure returns (uint[2] memory) {
require(_point.length == 33, "Malformed compressed EC point");
uint8 sign;
uint256 x;
assembly {
sign := mload(add(_point, 1))
x := mload(add(_point, 33))
}
uint256 y = deriveY(sign, x);
return [x, y];
}
/// @dev Compute the parameters (EC points) required for the VRF fast verification function.
/// @param _publicKey The public key as an array composed of `[pubKey-x, pubKey-y]`
/// @param _proof The VRF proof as an array composed of `[gamma-x, gamma-y, c, s]`
/// @param _message The message (in bytes) used for computing the VRF
/// @return The fast verify required parameters as the tuple `([uPointX, uPointY], [sHX, sHY, cGammaX, cGammaY])`
function computeFastVerifyParams(uint256[2] memory _publicKey, uint256[4] memory _proof, bytes memory _message)
internal pure returns (uint256[2] memory, uint256[4] memory)
{
// Requirements for Step 3: U = s*B - c*Y (where B is the generator)
(uint256 hPointX, uint256 hPointY) = hashToTryAndIncrement(_publicKey, _message);
(uint256 uPointX, uint256 uPointY) = ecMulSubMul(
_proof[3],
GX,
GY,
_proof[2],
_publicKey[0],
_publicKey[1]);
// Requirements for Step 4: V = s*H - c*Gamma
(uint256 sHX, uint256 sHY) = derivePoint(_proof[3], hPointX, hPointY);
(uint256 cGammaX, uint256 cGammaY) = derivePoint(_proof[2], _proof[0], _proof[1]);
return (
[uPointX, uPointY],
[
sHX,
sHY,
cGammaX,
cGammaY
]);
}
/// @dev Function to convert a `Hash(PK|DATA)` to a point in the curve as defined in [VRF-draft-04](https://tools.ietf.org/pdf/draft-irtf-cfrg-vrf-04).
/// Used in Step 2 of VRF verification function.
/// @param _publicKey The public key as an array composed of `[pubKey-x, pubKey-y]`
/// @param _message The message used for computing the VRF
/// @return The hash point in affine cooridnates
function hashToTryAndIncrement(uint256[2] memory _publicKey, bytes memory _message) internal pure returns (uint, uint) {
// Step 1: public key to bytes
// Step 2: V = cipher_suite | 0x01 | public_key_bytes | message | ctr
bytes memory c = abi.encodePacked(
// Cipher suite code (SECP256K1-SHA256-TAI is 0xFE)
uint8(254),
// 0x01
uint8(1),
// Public Key
encodePoint(_publicKey[0], _publicKey[1]),
// Message
_message);
// Step 3: find a valid EC point
// Loop over counter ctr starting at 0x00 and do hash
for (uint8 ctr = 0; ctr < 256; ctr++) {
// Counter update
// c[cLength-1] = byte(ctr);
bytes32 sha = sha256(abi.encodePacked(c, ctr));
// Step 4: arbitraty string to point and check if it is on curve
uint hPointX = uint256(sha);
uint hPointY = deriveY(2, hPointX);
if (EllipticCurve.isOnCurve(
hPointX,
hPointY,
AA,
BB,
PP))
{
// Step 5 (omitted): calculate H (cofactor is 1 on secp256k1)
// If H is not "INVALID" and cofactor > 1, set H = cofactor * H
return (hPointX, hPointY);
}
}
revert("No valid point was found");
}
/// @dev Function to hash a certain set of points as specified in [VRF-draft-04](https://tools.ietf.org/pdf/draft-irtf-cfrg-vrf-04).
/// Used in Step 5 of VRF verification function.
/// @param _hPointX The coordinate `x` of point `H`
/// @param _hPointY The coordinate `y` of point `H`
/// @param _gammaX The coordinate `x` of the point `Gamma`
/// @param _gammaX The coordinate `y` of the point `Gamma`
/// @param _uPointX The coordinate `x` of point `U`
/// @param _uPointY The coordinate `y` of point `U`
/// @param _vPointX The coordinate `x` of point `V`
/// @param _vPointY The coordinate `y` of point `V`
/// @return The first half of the digest of the points using SHA256
function hashPoints(
uint256 _hPointX,
uint256 _hPointY,
uint256 _gammaX,
uint256 _gammaY,
uint256 _uPointX,
uint256 _uPointY,
uint256 _vPointX,
uint256 _vPointY)
internal pure returns (bytes16)
{
bytes memory c = abi.encodePacked(
// Ciphersuite 0xFE
uint8(254),
// Prefix 0x02
uint8(2),
// Points to Bytes
encodePoint(_hPointX, _hPointY),
encodePoint(_gammaX, _gammaY),
encodePoint(_uPointX, _uPointY),
encodePoint(_vPointX, _vPointY)
);
// Hash bytes and truncate
bytes32 sha = sha256(c);
bytes16 half1;
assembly {
let freemem_pointer := mload(0x40)
mstore(add(freemem_pointer,0x00), sha)
half1 := mload(add(freemem_pointer,0x00))
}
return half1;
}
/// @dev Encode an EC point to bytes
/// @param _x The coordinate `x` of the point
/// @param _y The coordinate `y` of the point
/// @return The point coordinates as bytes
function encodePoint(uint256 _x, uint256 _y) internal pure returns (bytes memory) {
uint8 prefix = uint8(2 + (_y % 2));
return abi.encodePacked(prefix, _x);
}
/// @dev Substracts two key derivation functionsas `s1*A - s2*B`.
/// @param _scalar1 The scalar `s1`
/// @param _a1 The `x` coordinate of point `A`
/// @param _a2 The `y` coordinate of point `A`
/// @param _scalar2 The scalar `s2`
/// @param _b1 The `x` coordinate of point `B`
/// @param _b2 The `y` coordinate of point `B`
/// @return The derived point in affine cooridnates
function ecMulSubMul(
uint256 _scalar1,
uint256 _a1,
uint256 _a2,
uint256 _scalar2,
uint256 _b1,
uint256 _b2)
internal pure returns (uint256, uint256)
{
(uint256 m1, uint256 m2) = derivePoint(_scalar1, _a1, _a2);
(uint256 n1, uint256 n2) = derivePoint(_scalar2, _b1, _b2);
(uint256 r1, uint256 r2) = EllipticCurve.ecSub(
m1,
m2,
n1,
n2,
AA,
PP);
return (r1, r2);
}
/// @dev Verify an Elliptic Curve multiplication of the form `(qx,qy) = scalar*(x,y)` by using the precompiled `ecrecover` function.
/// The usage of the precompiled `ecrecover` function decreases the security from 32 to 20 bytes.
/// Based on the original idea of Vitalik Buterin: https://ethresear.ch/t/you-can-kinda-abuse-ecrecover-to-do-ecmul-in-secp256k1-today/2384/9
/// @param _scalar The scalar of the point multiplication
/// @param _x The coordinate `x` of the point
/// @param _y The coordinate `y` of the point
/// @param _qx The coordinate `x` of the multiplication result
/// @param _qy The coordinate `y` of the multiplication result
/// @return true, if first 20 bytes match
function ecMulVerify(
uint256 _scalar,
uint256 _x,
uint256 _y,
uint256 _qx,
uint256 _qy)
internal pure returns(bool)
{
address result = ecrecover(
0,
_y % 2 != 0 ? 28 : 27,
bytes32(_x),
bytes32(mulmod(_scalar, _x, NN)));
return pointToAddress(_qx, _qy) == result;
}
/// @dev Verify an Elliptic Curve operation of the form `Q = scalar1*(gx,gy) - scalar2*(x,y)` by using the precompiled `ecrecover` function, where `(gx,gy)` is the generator of the EC.
/// The usage of the precompiled `ecrecover` function decreases the security from 32 to 20 bytes.
/// Based on SolCrypto library: https://github.com/HarryR/solcrypto
/// @param _scalar1 The scalar of the multiplication of `(gx,gy)`
/// @param _scalar2 The scalar of the multiplication of `(x,y)`
/// @param _x The coordinate `x` of the point to be mutiply by `scalar2`
/// @param _y The coordinate `y` of the point to be mutiply by `scalar2`
/// @param _qx The coordinate `x` of the equation result
/// @param _qy The coordinate `y` of the equation result
/// @return true, if first 20 bytes match
function ecMulSubMulVerify(
uint256 _scalar1,
uint256 _scalar2,
uint256 _x,
uint256 _y,
uint256 _qx,
uint256 _qy)
internal pure returns(bool)
{
uint256 scalar1 = (NN - _scalar1) % NN;
scalar1 = mulmod(scalar1, _x, NN);
uint256 scalar2 = (NN - _scalar2) % NN;
address result = ecrecover(
bytes32(scalar1),
_y % 2 != 0 ? 28 : 27,
bytes32(_x),
bytes32(mulmod(scalar2, _x, NN)));
return pointToAddress(_qx, _qy) == result;
}
/// @dev Gets the address corresponding to the EC point digest (keccak256), i.e. the first 20 bytes of the digest.
/// This function is used for performing a fast EC multiplication verification.
/// @param _x The coordinate `x` of the point
/// @param _y The coordinate `y` of the point
/// @return The address of the EC point digest (keccak256)
function pointToAddress(uint256 _x, uint256 _y)
internal pure returns(address)
{
return address(uint160(uint256(keccak256(abi.encodePacked(_x, _y))) & 0x00FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF));
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "./LaunchpadStorage.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
import "./LaunchStage.sol";
abstract contract WinnerSelection is LaunchpadStorage, Ownable, LaunchStageModule {
event WinnersSelected(uint256 progress, uint256 total);
event TicketsFiltered(uint256 progress, uint256 total);
event WinnersBitmapUploaded();
function uploadWinnersBitmap(uint256[] calldata bitmap) external onlyOwner winnerSelectionStage {
require(state.flags.wereTicketsFiltered, "Must filter tickets first");
require(state.proofBytes.length > 0, "Shuffler not initialized");
require(!state.flags.winnersSelected, "Winners already selected");
// Calculate expected bitmap length (ceiling division by 256)
uint256 expectedLength = (state.lastTicketId + 255) / 256;
require(bitmap.length == expectedLength, "Invalid bitmap length");
// Store bitmap
for (uint256 i = 0; i < bitmap.length; i++) {
state.winnersBitmap[i] = bitmap[i];
}
state.flags.winnersSelected = true;
uint256 totalWinningTickets = state.configuration.numberOfWinningTickets.value;
state.configuration.claimableTicketPayment = state.configuration.ticketPrice * totalWinningTickets;
emit WinnersBitmapUploaded();
}
function filterTickets(uint256 batchSize) public onlyOwner winnerSelectionStage {
require(!state.flags.wereTicketsFiltered, "Tickets already filtered");
if (state.filterTicketsProgressStart == 0) {
state.filterTicketsProgressStart = 1;
}
uint256 maxTicketId = state.lastTicketId;
uint256 updatedMaxTicketId = maxTicketId + 1;
for (uint256 i = 0; i < batchSize && state.filterTicketsProgressStart <= maxTicketId; i++) {
(address ticketOwner, uint256 ticketsInBatch) = getTicketBatchDetails(state.filterTicketsProgressStart);
uint256 confirmedTicketCount = getNumberOfConfirmedTickets(ticketOwner);
if (isUserBlacklisted(ticketOwner) || confirmedTicketCount == 0) {
clearTicketDataForAddress(ticketOwner, state.filterTicketsProgressStart);
} else {
uint256 guaranteedWinners = getGuaranteedWinnersForAddress(ticketOwner);
uint256 ticketsToKeep = calculateTicketsToKeep(guaranteedWinners, confirmedTicketCount);
uint256 newFirstTicketId = state.filterTicketsProgressStart - (state.filterTicketsProgressEnd) - (state.confirmedGuaranteedTickets);
uint256 newLastTicketId = newFirstTicketId + (ticketsToKeep) - 1;
updatedMaxTicketId = newLastTicketId;
updateTicketRangeForAddress(ticketOwner, newFirstTicketId, newLastTicketId, guaranteedWinners, confirmedTicketCount);
state.confirmedGuaranteedTickets = state.confirmedGuaranteedTickets + (getTicketRangeForAddress(ticketOwner).guaranteedWinners);
updateTicketBatch(newFirstTicketId, ticketOwner, ticketsToKeep);
}
uint256 removedTicketsInBatch = ticketsInBatch - (confirmedTicketCount);
state.filterTicketsProgressEnd = state.filterTicketsProgressEnd + (removedTicketsInBatch);
state.filterTicketsProgressStart = state.filterTicketsProgressStart + (ticketsInBatch);
}
emit TicketsFiltered(state.filterTicketsProgressStart - 1, maxTicketId);
if (state.filterTicketsProgressStart > maxTicketId) {
state.shufflableTickets = updatedMaxTicketId;
updateNumberOfWinningTickets( updatedMaxTicketId + (state.confirmedGuaranteedTickets));
state.lastTicketId = updatedMaxTicketId;
state.flags.wereTicketsFiltered = true;
delete state.filterTicketsProgressStart;
delete state.filterTicketsProgressEnd;
}
}
function getTicketBatchDetails(uint256 batchStartId) private view returns (address, uint256) {
TicketBatch memory batch = state.ticketBatch[batchStartId];
return (batch.addr, batch.nrTickets);
}
function getNumberOfConfirmedTickets(address addr) public view returns (uint256) {
return state.confirmedTicketsForAddress[addr];
}
function isUserBlacklisted(address user) public view returns (bool) {
return state.blacklist[user];
}
function clearTicketDataForAddress(address user, uint256 batchStartId) private {
delete state.ticketRangeForAddress[user];
delete state.ticketBatch[batchStartId];
}
function getGuaranteedWinnersForAddress(address user) private view returns (uint256) {
return state.ticketRangeForAddress[user].guaranteedWinners;
}
function calculateTicketsToKeep(uint256 guaranteedWinners, uint256 confirmedTickets) private pure returns (uint256) {
return guaranteedWinners >= confirmedTickets ? 0 : confirmedTickets - (guaranteedWinners);
}
function updateTicketRangeForAddress(
address user,
uint256 firstId,
uint256 lastId,
uint256 guaranteedWinners,
uint256 totalConfirmedTickets
) private {
state.ticketRangeForAddress[user] = TicketRange({
firstId: firstId,
lastId: lastId,
guaranteedWinners: guaranteedWinners < totalConfirmedTickets ? guaranteedWinners : totalConfirmedTickets
});
}
function getTicketRangeForAddress(address addr) internal view returns (TicketRange memory) {
TicketRange memory range = state.ticketRangeForAddress[addr];
require(range.lastId != 0, "Ticket range not found");
return range;
}
function getNumberOfWinningTicketsForAddress(address user) public view winnersSelected returns (uint256) {
TicketRange memory range = getTicketRangeForAddress(user);
uint256 winningCount = range.guaranteedWinners; // Start with guaranteed winners
// Check bitmap for shuffled winners
for (uint256 ticketId = range.firstId; ticketId <= range.lastId; ticketId++) {
uint256 wordIndex = ticketId / 256;
uint256 bitIndex = ticketId % 256;
if ((state.winnersBitmap[wordIndex] & (1 << bitIndex)) != 0) {
winningCount++;
}
}
return winningCount;
}
function isWinningTicket(address user, uint256 ticketId) internal view winnersSelected returns (bool) {
// Get user's ticket range
TicketRange memory range = getTicketRangeForAddress(user);
// Verify ticket is within user's range
require(ticketId >= range.firstId && ticketId <= range.lastId, "Ticket not owned by user");
// If it's in guaranteed range, it's automatically a winner
if (ticketId < range.guaranteedWinners) {
return true;
}
// Otherwise check bitmap for shuffled winners
uint256 wordIndex = ticketId / 256;
uint256 bitIndex = ticketId % 256;
return (state.winnersBitmap[wordIndex] & (1 << bitIndex)) != 0;
}
function updateTicketBatch(
uint256 batchStartId,
address user,
uint256 nrTickets
) private {
delete state.ticketBatch[batchStartId];
state.ticketBatch[batchStartId] = TicketBatch({
addr: user,
nrTickets: nrTickets
});
}
function updateNumberOfWinningTickets(uint256 updatedMaxTicketId) private {
uint256 currentWinningTicketCount = state.configuration.numberOfWinningTickets.value;
if (currentWinningTicketCount > updatedMaxTicketId) {
state.configuration.numberOfWinningTickets = NumberOfWinningTickets({
ticketType: NumberOfWinningTicketsType.AfterFiltering,
value: updatedMaxTicketId
});
} else {
state.configuration.numberOfWinningTickets = NumberOfWinningTickets({
ticketType: NumberOfWinningTicketsType.AfterFiltering,
value: currentWinningTicketCount
});
}
}
function getWinningTicketIdsForAddress(address user) public view winnersSelected returns (uint256[] memory) {
TicketRange memory range = getTicketRangeForAddress(user);
uint256[] memory winningTickets = new uint256[](range.lastId - range.firstId + 1);
uint256 winningCount = 0;
// First add guaranteed winners
for (uint256 i = 0; i < range.guaranteedWinners; i++) {
winningTickets[winningCount] = i;
winningCount++;
}
// Then check bitmap for shuffled winners in valid range
for (uint256 ticketId = range.firstId; ticketId <= range.lastId; ticketId++) {
uint256 wordIndex = ticketId / 256;
uint256 bitIndex = ticketId % 256;
if ((state.winnersBitmap[wordIndex] & (1 << bitIndex)) != 0) {
winningTickets[winningCount] = ticketId;
winningCount++;
}
}
uint256[] memory result = new uint256[](winningCount);
for (uint256 i = 0; i < winningCount; i++) {
result[i] = winningTickets[i];
}
return result;
}
function getNumberOfWinningPerUser(address addr) public view returns (uint256) {
return state.numberOfWinningTicketsPerUser[addr];
}
function getWinnersBitmap() public view winnersSelected returns (uint256[] memory) {
uint256 length = (state.lastTicketId + 255) / 256;
uint256[] memory bitmap = new uint256[](length);
for (uint256 i = 0; i < length; i++) {
bitmap[i] = state.winnersBitmap[i];
}
return bitmap;
}
modifier winnerSelectionStage() {
require(getLaunchStage() == LaunchStage.WinnerSelection, "Not winner selection stage");
_;
}
modifier winnersSelected() {
require(state.flags.winnersSelected, "Winners not selected");
_;
}
}{
"viaIR": true,
"optimizer": {
"enabled": true,
"runs": 1
},
"evmVersion": "paris",
"outputSelection": {
"*": {
"*": [
"evm.bytecode",
"evm.deployedBytecode",
"devdoc",
"userdoc",
"metadata",
"abi"
]
}
}
}Contract ABI
API[{"inputs":[],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"OwnableInvalidOwner","type":"error"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"OwnableUnauthorizedAccount","type":"error"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"startTime","type":"uint256"}],"name":"ClaimStartTimeSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"startTime","type":"uint256"}],"name":"ConfirmationPeriodStartTimeSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"token","type":"address"}],"name":"LaunchpadTokenSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"address","name":"token","type":"address"}],"name":"LaunchpadTokensDeposited","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"LaunchpadTokensPerWinningTicketSet","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":[],"name":"ShufflerInitialized","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"claimedTicketPayment","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"extraLaunchpadTokens","type":"uint256"}],"name":"TicketPaymentClaimed","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"address","name":"token","type":"address"}],"name":"TicketPriceSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"token","type":"address"}],"name":"TicketTokenSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"uniqueAddressesAddedCount","type":"uint256"}],"name":"TicketsAdded","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":false,"internalType":"uint256","name":"ticketsConfirmed","type":"uint256"}],"name":"TicketsConfirmed","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"progress","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"total","type":"uint256"}],"name":"TicketsFiltered","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":false,"internalType":"uint256","name":"redeemableTickets","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"refundedTickets","type":"uint256"}],"name":"TokensClaimed","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address[]","name":"users","type":"address[]"}],"name":"UsersBlacklisted","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address[]","name":"users","type":"address[]"}],"name":"UsersRemovedFromBlacklist","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"startTime","type":"uint256"}],"name":"WinnerSelectionStartTimeSet","type":"event"},{"anonymous":false,"inputs":[],"name":"WinnersBitmapUploaded","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"progress","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"total","type":"uint256"}],"name":"WinnersSelected","type":"event"},{"inputs":[{"internalType":"address[]","name":"usersList","type":"address[]"}],"name":"addUsersToBlacklist","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"claimLaunchpadTokens","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"claimTicketPayment","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"payer","type":"address"},{"internalType":"address","name":"receiver","type":"address"},{"internalType":"uint256","name":"nrTicketsToConfirm","type":"uint256"}],"name":"confirmTickets","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"depositInitialLaunchpadTokens","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"batchSize","type":"uint256"}],"name":"filterTickets","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"getLastTicketId","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getLaunchStage","outputs":[{"internalType":"enum LaunchpadStorage.LaunchStage","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"addr","type":"address"}],"name":"getNumberOfConfirmedTickets","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"addr","type":"address"}],"name":"getNumberOfWinningPerUser","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"}],"name":"getNumberOfWinningTicketsForAddress","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getProjectInfo","outputs":[{"components":[{"internalType":"address","name":"ticketToken","type":"address"},{"internalType":"address","name":"launchpadToken","type":"address"},{"internalType":"uint256","name":"launchpadTokensPerWinningTicket","type":"uint256"},{"internalType":"uint256","name":"pricePerToken","type":"uint256"},{"internalType":"bool","name":"tokensDeposited","type":"bool"},{"internalType":"enum LaunchpadStorage.LaunchStage","name":"currentPhase","type":"uint8"},{"components":[{"internalType":"bool","name":"hasWinnerSelectionProcessStarted","type":"bool"},{"internalType":"bool","name":"wereTicketsFiltered","type":"bool"},{"internalType":"bool","name":"winnersSelected","type":"bool"},{"internalType":"bool","name":"launchpadNfts","type":"bool"}],"internalType":"struct LaunchpadStorage.Flags","name":"flags","type":"tuple"},{"components":[{"internalType":"uint256","name":"confirmationPeriodStartTime","type":"uint256"},{"internalType":"uint256","name":"winnerSelectionStartTime","type":"uint256"},{"internalType":"uint256","name":"claimStartTime","type":"uint256"}],"internalType":"struct LaunchpadStorage.Timeline","name":"timeline","type":"tuple"},{"components":[{"internalType":"enum LaunchpadStorage.NumberOfWinningTicketsType","name":"ticketType","type":"uint8"},{"internalType":"uint256","name":"value","type":"uint256"}],"internalType":"struct LaunchpadStorage.NumberOfWinningTickets","name":"numberOfWinningTickets","type":"tuple"},{"internalType":"bytes","name":"winnerSelectionScriptHash","type":"bytes"}],"internalType":"struct LaunchpadStorage.ProjectInfo","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getProof","outputs":[{"internalType":"bytes","name":"","type":"bytes"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getShufflerSeed","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"addr","type":"address"}],"name":"getTotalNumberOfTicketsForAddress","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getWinnersBitmap","outputs":[{"internalType":"uint256[]","name":"","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"}],"name":"getWinningTicketIdsForAddress","outputs":[{"internalType":"uint256[]","name":"","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"}],"name":"hasUserClaimed","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes","name":"proof_bytes","type":"bytes"}],"name":"initShuffler","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes","name":"proofZero","type":"bytes"},{"internalType":"bytes","name":"publicKeyBytes","type":"bytes"},{"internalType":"address","name":"launchpadTokenId","type":"address"},{"internalType":"uint256","name":"launchpadTokensPerWinningTicket","type":"uint256"},{"internalType":"address","name":"ticketPaymentToken","type":"address"},{"internalType":"uint256","name":"ticketPrice","type":"uint256"},{"internalType":"uint256","name":"nrWinningTickets","type":"uint256"},{"internalType":"uint256","name":"confirmationPeriodStartTime","type":"uint256"},{"internalType":"uint256","name":"winnerSelectionStartTime","type":"uint256"},{"internalType":"uint256","name":"claimStartTime","type":"uint256"},{"internalType":"address","name":"initialOwner","type":"address"},{"internalType":"bytes","name":"winnerSelectionScriptHash","type":"bytes"}],"name":"initialize","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"}],"name":"isUserBlacklisted","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address[]","name":"usersList","type":"address[]"}],"name":"removeUsersFromBlacklist","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"bitmap","type":"uint256[]"}],"name":"uploadWinnersBitmap","outputs":[],"stateMutability":"nonpayable","type":"function"}]Loading...
Loading
Loading...
Loading
Loading...
Loading
Loading...
Loading
[ Download: CSV Export ]
[ Download: CSV Export ]
A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.