¡El código fuente de este contrato está verificado!
Metadatos del Contrato
Compilador
0.8.20+commit.a1b79de6
Idioma
Solidity
Código Fuente del Contrato
Archivo 1 de 11: Context.sol
// SPDX-License-Identifier: MIT// OpenZeppelin Contracts (last updated v5.0.1) (utils/Context.sol)pragmasolidity ^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.
*/abstractcontractContext{
function_msgSender() internalviewvirtualreturns (address) {
returnmsg.sender;
}
function_msgData() internalviewvirtualreturns (bytescalldata) {
returnmsg.data;
}
function_contextSuffixLength() internalviewvirtualreturns (uint256) {
return0;
}
}
Código Fuente del Contrato
Archivo 2 de 11: ERC20.sol
// SPDX-License-Identifier: AGPL-3.0-onlypragmasolidity >=0.8.0;/// @notice Modern and gas efficient ERC20 + EIP-2612 implementation./// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/tokens/ERC20.sol)/// @author Modified from Uniswap (https://github.com/Uniswap/uniswap-v2-core/blob/master/contracts/UniswapV2ERC20.sol)/// @dev Do not manually set balances without updating totalSupply, as the sum of all user balances must not exceed it.abstractcontractERC20{
/*//////////////////////////////////////////////////////////////
EVENTS
//////////////////////////////////////////////////////////////*/eventTransfer(addressindexedfrom, addressindexed to, uint256 amount);
eventApproval(addressindexed owner, addressindexed spender, uint256 amount);
/*//////////////////////////////////////////////////////////////
METADATA STORAGE
//////////////////////////////////////////////////////////////*/stringpublic name;
stringpublic symbol;
uint8publicimmutable decimals;
/*//////////////////////////////////////////////////////////////
ERC20 STORAGE
//////////////////////////////////////////////////////////////*/uint256public totalSupply;
mapping(address=>uint256) public balanceOf;
mapping(address=>mapping(address=>uint256)) public allowance;
/*//////////////////////////////////////////////////////////////
EIP-2612 STORAGE
//////////////////////////////////////////////////////////////*/uint256internalimmutable INITIAL_CHAIN_ID;
bytes32internalimmutable INITIAL_DOMAIN_SEPARATOR;
mapping(address=>uint256) public nonces;
/*//////////////////////////////////////////////////////////////
CONSTRUCTOR
//////////////////////////////////////////////////////////////*/constructor(stringmemory _name,
stringmemory _symbol,
uint8 _decimals
) {
name = _name;
symbol = _symbol;
decimals = _decimals;
INITIAL_CHAIN_ID =block.chainid;
INITIAL_DOMAIN_SEPARATOR = computeDomainSeparator();
}
/*//////////////////////////////////////////////////////////////
ERC20 LOGIC
//////////////////////////////////////////////////////////////*/functionapprove(address spender, uint256 amount) publicvirtualreturns (bool) {
allowance[msg.sender][spender] = amount;
emit Approval(msg.sender, spender, amount);
returntrue;
}
functiontransfer(address to, uint256 amount) publicvirtualreturns (bool) {
balanceOf[msg.sender] -= amount;
// Cannot overflow because the sum of all user// balances can't exceed the max uint256 value.unchecked {
balanceOf[to] += amount;
}
emit Transfer(msg.sender, to, amount);
returntrue;
}
functiontransferFrom(addressfrom,
address to,
uint256 amount
) publicvirtualreturns (bool) {
uint256 allowed = allowance[from][msg.sender]; // Saves gas for limited approvals.if (allowed !=type(uint256).max) allowance[from][msg.sender] = allowed - amount;
balanceOf[from] -= amount;
// Cannot overflow because the sum of all user// balances can't exceed the max uint256 value.unchecked {
balanceOf[to] += amount;
}
emit Transfer(from, to, amount);
returntrue;
}
/*//////////////////////////////////////////////////////////////
EIP-2612 LOGIC
//////////////////////////////////////////////////////////////*/functionpermit(address owner,
address spender,
uint256 value,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) publicvirtual{
require(deadline >=block.timestamp, "PERMIT_DEADLINE_EXPIRED");
// Unchecked because the only math done is incrementing// the owner's nonce which cannot realistically overflow.unchecked {
address recoveredAddress =ecrecover(
keccak256(
abi.encodePacked(
"\x19\x01",
DOMAIN_SEPARATOR(),
keccak256(
abi.encode(
keccak256(
"Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)"
),
owner,
spender,
value,
nonces[owner]++,
deadline
)
)
)
),
v,
r,
s
);
require(recoveredAddress !=address(0) && recoveredAddress == owner, "INVALID_SIGNER");
allowance[recoveredAddress][spender] = value;
}
emit Approval(owner, spender, value);
}
functionDOMAIN_SEPARATOR() publicviewvirtualreturns (bytes32) {
returnblock.chainid== INITIAL_CHAIN_ID ? INITIAL_DOMAIN_SEPARATOR : computeDomainSeparator();
}
functioncomputeDomainSeparator() internalviewvirtualreturns (bytes32) {
returnkeccak256(
abi.encode(
keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"),
keccak256(bytes(name)),
keccak256("1"),
block.chainid,
address(this)
)
);
}
/*//////////////////////////////////////////////////////////////
INTERNAL MINT/BURN LOGIC
//////////////////////////////////////////////////////////////*/function_mint(address to, uint256 amount) internalvirtual{
totalSupply += amount;
// Cannot overflow because the sum of all user// balances can't exceed the max uint256 value.unchecked {
balanceOf[to] += amount;
}
emit Transfer(address(0), to, amount);
}
function_burn(addressfrom, uint256 amount) internalvirtual{
balanceOf[from] -= amount;
// Cannot underflow because a user's balance// will never be larger than the total supply.unchecked {
totalSupply -= amount;
}
emit Transfer(from, address(0), amount);
}
}
Código Fuente del Contrato
Archivo 3 de 11: ISupraConsumer.sol
// SPDX-License-Identifier: MITpragmasolidity ^0.8.0;/**
*
* @notice Interface for contracts using VRF randomness
* *****************************************************************************
* @dev PURPOSE
*
* @dev Reggie the Random Oracle (not his real job) wants to provide randomness
* @dev to Vera the verifier in such a way that Vera can be sure he's not
* @dev making his output up to suit himself. Reggie provides Vera a public key
* @dev to which he knows the secret key. Each time Vera provides a seed to
* @dev Reggie, he gives back a value which is computed completely
* @dev deterministically from the seed and the secret key.
*
* @dev Reggie provides a proof by which Vera can verify that the output was
* @dev correctly computed once Reggie tells it to her, but without that proof,
* @dev the output is indistinguishable to her from a uniform random sample
* @dev from the output space.
*
* @dev The purpose of this contract is to make it easy for unrelated contracts
* @dev to talk to Vera the verifier about the work Reggie is doing, to provide
* @dev simple access to a verifiable source of randomness. It ensures 2 things:
* @dev 1. The fulfillment came from the VRFCoordinator
* @dev 2. The consumer contract implements fulfillRandomWords.
* *****************************************************************************
* @dev USAGE
*
* @dev Calling contracts must inherit from VRFConsumerBase, and can
* @dev initialize VRFConsumerBase's attributes in their constructor as
* @dev shown:
*
* @dev contract VRFConsumer {
* @dev constructor(<other arguments>, address _vrfCoordinator, address _link)
* @dev VRFConsumerBase(_vrfCoordinator) public {
* @dev <initialization with other arguments goes here>
* @dev }
* @dev }
*
* @dev The oracle will have given you an ID for the VRF keypair they have
* @dev committed to (let's call it keyHash). Create subscription, fund it
* @dev and your consumer contract as a consumer of it (see VRFCoordinatorInterface
* @dev subscription management functions).
* @dev Call requestRandomWords(keyHash, subId, minimumRequestConfirmations,
* @dev callbackGasLimit, numWords),
* @dev see (VRFCoordinatorInterface for a description of the arguments).
*
* @dev Once the VRFCoordinator has received and validated the oracle's response
* @dev to your request, it will call your contract's fulfillRandomWords method.
*
* @dev The randomness argument to fulfillRandomWords is a set of random words
* @dev generated from your requestId and the blockHash of the request.
*
* @dev If your contract could have concurrent requests open, you can use the
* @dev requestId returned from requestRandomWords to track which response is associated
* @dev with which randomness request.
* @dev See "SECURITY CONSIDERATIONS" for principles to keep in mind,
* @dev if your contract could have multiple requests in flight simultaneously.
*
* @dev Colliding `requestId`s are cryptographically impossible as long as seeds
* @dev differ.
*
* *****************************************************************************
* @dev SECURITY CONSIDERATIONS
*
* @dev A method with the ability to call your fulfillRandomness method directly
* @dev could spoof a VRF response with any random value, so it's critical that
* @dev it cannot be directly called by anything other than this base contract
* @dev (specifically, by the VRFConsumerBase.rawFulfillRandomness method).
*
* @dev For your users to trust that your contract's random behavior is free
* @dev from malicious interference, it's best if you can write it so that all
* @dev behaviors implied by a VRF response are executed *during* your
* @dev fulfillRandomness method. If your contract must store the response (or
* @dev anything derived from it) and use it later, you must ensure that any
* @dev user-significant behavior which depends on that stored value cannot be
* @dev manipulated by a subsequent VRF request.
*
* @dev Similarly, both miners and the VRF oracle itself have some influence
* @dev over the order in which VRF responses appear on the blockchain, so if
* @dev your contract could have multiple VRF requests in flight simultaneously,
* @dev you must ensure that the order in which the VRF responses arrive cannot
* @dev be used to manipulate your contract's user-significant behavior.
*
* @dev Since the block hash of the block which contains the requestRandomness
* @dev call is mixed into the input to the VRF *last*, a sufficiently powerful
* @dev miner could, in principle, fork the blockchain to evict the block
* @dev containing the request, forcing the request to be included in a
* @dev different block with a different hash, and therefore a different input
* @dev to the VRF. However, such an attack would incur a substantial economic
* @dev cost. This cost scales with the number of blocks the VRF oracle waits
* @dev until it calls responds to a request. It is for this reason that
* @dev that you can signal to an oracle you'd like them to wait longer before
* @dev responding to the request (however this is not enforced in the contract
* @dev and so remains effective only in the case of unmodified oracle software).
*/interfaceISupraConsumer{
// rawFulfillRandomness is called by VRFCoordinator when it receives a valid VRF proof.// function rawFulfillRandomWords(uint256 requestId, uint256[] memory randomWords) external;
}
// SPDX-License-Identifier: MITpragmasolidity ^0.8.0;libraryMath{
functionmin(uint256 x, uint256 y) internalpurereturns (uint256 z) {
assembly {
z :=xor(x, mul(xor(x, y), lt(y, x)))
}
}
functionmax(uint256 x, uint256 y) internalpurereturns (uint256 z) {
assembly {
z :=xor(x, mul(xor(x, y), gt(y, x)))
}
}
/// @dev Returns max(x - y, 0).functionzeroFloorSub(uint256 x, uint256 y) internalpurereturns (uint256 z) {
assembly {
z :=mul(gt(x, y), sub(x, y))
}
}
/// @dev Returns x / y rounded up (x / y + boolAsInt(x % y > 0)).functiondivUp(uint256 x, uint256 y) internalpurereturns (uint256 z) {
// Division by 0 if// y = 0assembly {
ifiszero(y) { revert(0, 0) }
z :=add(gt(mod(x, y), 0), div(x, y))
}
}
/// @dev Returns the floor of log2(x) and returns 0 when x = 0./// @dev Uses a method by dichotomy to find the highest bit set of x.functionlog2(uint256 x) internalpurereturns (uint256 y) {
assembly {
// Finds if x has a 1 on the first 128 bits. If not then do nothing.// If that is the case then the result is more than 128.let z :=shl(7, gt(x, 0xffffffffffffffffffffffffffffffff))
y := z
x :=shr(z, x)
// Using y as an accumulator, we can now focus on the last 128 bits of x.// Repeat this process to divide the number of bits to handle by 2 every time.
z :=shl(6, gt(x, 0xffffffffffffffff))
y :=add(y, z)
x :=shr(z, x)
z :=shl(5, gt(x, 0xffffffff))
y :=add(y, z)
x :=shr(z, x)
z :=shl(4, gt(x, 0xffff))
y :=add(y, z)
x :=shr(z, x)
z :=shl(3, gt(x, 0xff))
y :=add(y, z)
x :=shr(z, x)
z :=shl(2, gt(x, 0xf))
y :=add(y, z)
x :=shr(z, x)
z :=shl(1, gt(x, 3))
y :=add(add(y, z), gt(shr(z, x), 1))
}
}
}
Código Fuente del Contrato
Archivo 7 de 11: Ownable.sol
// SPDX-License-Identifier: MIT// OpenZeppelin Contracts (last updated v5.0.0) (access/Ownable.sol)pragmasolidity ^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.
*/abstractcontractOwnableisContext{
addressprivate _owner;
/**
* @dev The caller account is not authorized to perform an operation.
*/errorOwnableUnauthorizedAccount(address account);
/**
* @dev The owner is not a valid owner account. (eg. `address(0)`)
*/errorOwnableInvalidOwner(address owner);
eventOwnershipTransferred(addressindexed previousOwner, addressindexed 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.
*/modifieronlyOwner() {
_checkOwner();
_;
}
/**
* @dev Returns the address of the current owner.
*/functionowner() publicviewvirtualreturns (address) {
return _owner;
}
/**
* @dev Throws if the sender is not the owner.
*/function_checkOwner() internalviewvirtual{
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.
*/functionrenounceOwnership() publicvirtualonlyOwner{
_transferOwnership(address(0));
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`).
* Can only be called by the current owner.
*/functiontransferOwnership(address newOwner) publicvirtualonlyOwner{
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) internalvirtual{
address oldOwner = _owner;
_owner = newOwner;
emit OwnershipTransferred(oldOwner, newOwner);
}
}
Código Fuente del Contrato
Archivo 8 de 11: Pausable.sol
// SPDX-License-Identifier: MIT// OpenZeppelin Contracts (last updated v5.0.0) (utils/Pausable.sol)pragmasolidity ^0.8.20;import {Context} from"../utils/Context.sol";
/**
* @dev Contract module which allows children to implement an emergency stop
* mechanism that can be triggered by an authorized account.
*
* This module is used through inheritance. It will make available the
* modifiers `whenNotPaused` and `whenPaused`, which can be applied to
* the functions of your contract. Note that they will not be pausable by
* simply including this module, only once the modifiers are put in place.
*/abstractcontractPausableisContext{
boolprivate _paused;
/**
* @dev Emitted when the pause is triggered by `account`.
*/eventPaused(address account);
/**
* @dev Emitted when the pause is lifted by `account`.
*/eventUnpaused(address account);
/**
* @dev The operation failed because the contract is paused.
*/errorEnforcedPause();
/**
* @dev The operation failed because the contract is not paused.
*/errorExpectedPause();
/**
* @dev Initializes the contract in unpaused state.
*/constructor() {
_paused =false;
}
/**
* @dev Modifier to make a function callable only when the contract is not paused.
*
* Requirements:
*
* - The contract must not be paused.
*/modifierwhenNotPaused() {
_requireNotPaused();
_;
}
/**
* @dev Modifier to make a function callable only when the contract is paused.
*
* Requirements:
*
* - The contract must be paused.
*/modifierwhenPaused() {
_requirePaused();
_;
}
/**
* @dev Returns true if the contract is paused, and false otherwise.
*/functionpaused() publicviewvirtualreturns (bool) {
return _paused;
}
/**
* @dev Throws if the contract is paused.
*/function_requireNotPaused() internalviewvirtual{
if (paused()) {
revert EnforcedPause();
}
}
/**
* @dev Throws if the contract is not paused.
*/function_requirePaused() internalviewvirtual{
if (!paused()) {
revert ExpectedPause();
}
}
/**
* @dev Triggers stopped state.
*
* Requirements:
*
* - The contract must not be paused.
*/function_pause() internalvirtualwhenNotPaused{
_paused =true;
emit Paused(_msgSender());
}
/**
* @dev Returns to normal state.
*
* Requirements:
*
* - The contract must be paused.
*/function_unpause() internalvirtualwhenPaused{
_paused =false;
emit Unpaused(_msgSender());
}
}
Código Fuente del Contrato
Archivo 9 de 11: PercentageMath.sol
// SPDX-License-Identifier: MITpragmasolidity ^0.8.0;libraryPercentageMath{
/* CONSTANTS */// Only direct number constants and references to such constants are supported by inline assembly.uint256internalconstant PERCENTAGE_FACTOR =100_00;
uint256internalconstant HALF_PERCENTAGE_FACTOR =50_00;
uint256internalconstant PERCENTAGE_FACTOR_MINUS_ONE =100_00-1;
uint256internalconstant MAX_UINT256 =2**256-1;
uint256internalconstant MAX_UINT256_MINUS_HALF_PERCENTAGE_FACTOR =2**256-1-50_00;
uint256internalconstant MAX_UINT256_MINUS_PERCENTAGE_FACTOR_MINUS_ONE =2**256-1- (100_00-1);
/* INTERNAL *//// @notice Executes the bps-based percentage addition (x * (1 + p)), rounded half up./// @param x The value to which to add the percentage./// @param percentage The percentage of the value to add (in bps)./// @return y The result of the addition.functionpercentAdd(uint256 x, uint256 percentage) internalpurereturns (uint256 y) {
// 1. Overflow if// PERCENTAGE_FACTOR + percentage > type(uint256).max// <=> percentage > type(uint256).max - PERCENTAGE_FACTOR// 2. Overflow if// x * (PERCENTAGE_FACTOR + percentage) + HALF_PERCENTAGE_FACTOR > type(uint256).max// <=> x > (type(uint256).max - HALF_PERCENTAGE_FACTOR) / (PERCENTAGE_FACTOR + percentage)assembly {
y :=add(PERCENTAGE_FACTOR, percentage) // Temporary assignment to save gas.ifor(
gt(percentage, sub(MAX_UINT256, PERCENTAGE_FACTOR)),
gt(x, div(MAX_UINT256_MINUS_HALF_PERCENTAGE_FACTOR, y))
) { revert(0, 0) }
y :=div(add(mul(x, y), HALF_PERCENTAGE_FACTOR), PERCENTAGE_FACTOR)
}
}
/// @notice Executes the bps-based percentage subtraction (x * (1 - p)), rounded half up./// @param x The value to which to subtract the percentage./// @param percentage The percentage of the value to subtract (in bps)./// @return y The result of the subtraction.functionpercentSub(uint256 x, uint256 percentage) internalpurereturns (uint256 y) {
// 1. Underflow if// percentage > PERCENTAGE_FACTOR// 2. Overflow if// x * (PERCENTAGE_FACTOR - percentage) + HALF_PERCENTAGE_FACTOR > type(uint256).max// <=> (PERCENTAGE_FACTOR - percentage) > 0 and x > (type(uint256).max - HALF_PERCENTAGE_FACTOR) / (PERCENTAGE_FACTOR - percentage)assembly {
y :=sub(PERCENTAGE_FACTOR, percentage) // Temporary assignment to save gas.ifor(gt(percentage, PERCENTAGE_FACTOR), mul(y, gt(x, div(MAX_UINT256_MINUS_HALF_PERCENTAGE_FACTOR, y)))) {
revert(0, 0)
}
y :=div(add(mul(x, y), HALF_PERCENTAGE_FACTOR), PERCENTAGE_FACTOR)
}
}
/// @notice Executes the bps-based multiplication (x * p), rounded half up./// @param x The value to multiply by the percentage./// @param percentage The percentage of the value to multiply (in bps)./// @return y The result of the multiplication.functionpercentMul(uint256 x, uint256 percentage) internalpurereturns (uint256 y) {
// Overflow if// x * percentage + HALF_PERCENTAGE_FACTOR > type(uint256).max// <=> percentage > 0 and x > (type(uint256).max - HALF_PERCENTAGE_FACTOR) / percentageassembly {
ifmul(percentage, gt(x, div(MAX_UINT256_MINUS_HALF_PERCENTAGE_FACTOR, percentage))) { revert(0, 0) }
y :=div(add(mul(x, percentage), HALF_PERCENTAGE_FACTOR), PERCENTAGE_FACTOR)
}
}
/// @notice Executes the bps-based multiplication (x * p), rounded down./// @param x The value to multiply by the percentage./// @param percentage The percentage of the value to multiply./// @return y The result of the multiplication.functionpercentMulDown(uint256 x, uint256 percentage) internalpurereturns (uint256 y) {
// Overflow if// x * percentage > type(uint256).max// <=> percentage > 0 and x > type(uint256).max / percentageassembly {
ifmul(percentage, gt(x, div(MAX_UINT256, percentage))) { revert(0, 0) }
y :=div(mul(x, percentage), PERCENTAGE_FACTOR)
}
}
/// @notice Executes the bps-based multiplication (x * p), rounded up./// @param x The value to multiply by the percentage./// @param percentage The percentage of the value to multiply./// @return y The result of the multiplication.functionpercentMulUp(uint256 x, uint256 percentage) internalpurereturns (uint256 y) {
// Overflow if// x * percentage + PERCENTAGE_FACTOR_MINUS_ONE > type(uint256).max// <=> percentage > 0 and x > (type(uint256).max - PERCENTAGE_FACTOR_MINUS_ONE) / percentageassembly {
ifmul(percentage, gt(x, div(MAX_UINT256_MINUS_PERCENTAGE_FACTOR_MINUS_ONE, percentage))) { revert(0, 0) }
y :=div(add(mul(x, percentage), PERCENTAGE_FACTOR_MINUS_ONE), PERCENTAGE_FACTOR)
}
}
/// @notice Executes the bps-based division (x / p), rounded half up./// @param x The value to divide by the percentage./// @param percentage The percentage of the value to divide (in bps)./// @return y The result of the division.functionpercentDiv(uint256 x, uint256 percentage) internalpurereturns (uint256 y) {
// 1. Division by 0 if// percentage == 0// 2. Overflow if// x * PERCENTAGE_FACTOR + percentage / 2 > type(uint256).max// <=> x > (type(uint256).max - percentage / 2) / PERCENTAGE_FACTORassembly {
y :=div(percentage, 2) // Temporary assignment to save gas.ifiszero(mul(percentage, iszero(gt(x, div(sub(MAX_UINT256, y), PERCENTAGE_FACTOR))))) { revert(0, 0) }
y :=div(add(mul(PERCENTAGE_FACTOR, x), y), percentage)
}
}
/// @notice Executes the bps-based division (x / p), rounded down./// @param x The value to divide by the percentage./// @param percentage The percentage of the value to divide./// @return y The result of the division.functionpercentDivDown(uint256 x, uint256 percentage) internalpurereturns (uint256 y) {
// 1. Division by 0 if// percentage == 0// 2. Overflow if// x * PERCENTAGE_FACTOR > type(uint256).max// <=> x > type(uint256).max / PERCENTAGE_FACTORassembly {
ifiszero(mul(percentage, lt(x, add(div(MAX_UINT256, PERCENTAGE_FACTOR), 1)))) { revert(0, 0) }
y :=div(mul(PERCENTAGE_FACTOR, x), percentage)
}
}
/// @notice Executes the bps-based division (x / p), rounded up./// @param x The value to divide by the percentage./// @param percentage The percentage of the value to divide./// @return y The result of the division.functionpercentDivUp(uint256 x, uint256 percentage) internalpurereturns (uint256 y) {
// 1. Division by 0 if// percentage == 0// 2. Overflow if// x * PERCENTAGE_FACTOR + (percentage - 1) > type(uint256).max// <=> x > (type(uint256).max - (percentage - 1)) / PERCENTAGE_FACTORassembly {
y :=sub(percentage, 1) // Temporary assignment to save gas.ifiszero(mul(percentage, iszero(gt(x, div(sub(MAX_UINT256, y), PERCENTAGE_FACTOR))))) { revert(0, 0) }
y :=div(add(mul(PERCENTAGE_FACTOR, x), y), percentage)
}
}
/// @notice Executes the bps-based weighted average (x * (1 - p) + y * p), rounded half up./// @param x The first value, with a weight of 1 - percentage./// @param y The second value, with a weight of percentage./// @param percentage The weight of y, and complement of the weight of x (in bps)./// @return z The result of the bps-based weighted average.functionweightedAvg(uint256 x, uint256 y, uint256 percentage) internalpurereturns (uint256 z) {
// 1. Underflow if// percentage > PERCENTAGE_FACTOR// 2. Overflow if// y * percentage + HALF_PERCENTAGE_FACTOR > type(uint256).max// <=> percentage > 0 and y > (type(uint256).max - HALF_PERCENTAGE_FACTOR) / percentage// 3. Overflow if// x * (PERCENTAGE_FACTOR - percentage) + y * percentage + HALF_PERCENTAGE_FACTOR > type(uint256).max// <=> x * (PERCENTAGE_FACTOR - percentage) > type(uint256).max - HALF_PERCENTAGE_FACTOR - y * percentage// <=> PERCENTAGE_FACTOR > percentage and x > (type(uint256).max - HALF_PERCENTAGE_FACTOR - y * percentage) / (PERCENTAGE_FACTOR - percentage)assembly {
z :=sub(PERCENTAGE_FACTOR, percentage) // Temporary assignment to save gas.ifor(
gt(percentage, PERCENTAGE_FACTOR),
or(
mul(percentage, gt(y, div(MAX_UINT256_MINUS_HALF_PERCENTAGE_FACTOR, percentage))),
mul(z, gt(x, div(sub(MAX_UINT256_MINUS_HALF_PERCENTAGE_FACTOR, mul(y, percentage)), z)))
)
) { revert(0, 0) }
z :=div(add(add(mul(x, z), mul(y, percentage)), HALF_PERCENTAGE_FACTOR), PERCENTAGE_FACTOR)
}
}
}
Código Fuente del Contrato
Archivo 10 de 11: SafeTransferLib.sol
// SPDX-License-Identifier: AGPL-3.0-onlypragmasolidity >=0.8.0;import {ERC20} from"../tokens/ERC20.sol";
/// @notice Safe ETH and ERC20 transfer library that gracefully handles missing return values./// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/SafeTransferLib.sol)/// @dev Use with caution! Some functions in this library knowingly create dirty bits at the destination of the free memory pointer./// @dev Note that none of the functions in this library check that a token has code at all! That responsibility is delegated to the caller.librarySafeTransferLib{
/*//////////////////////////////////////////////////////////////
ETH OPERATIONS
//////////////////////////////////////////////////////////////*/functionsafeTransferETH(address to, uint256 amount) internal{
bool success;
/// @solidity memory-safe-assemblyassembly {
// Transfer the ETH and store if it succeeded or not.
success :=call(gas(), to, amount, 0, 0, 0, 0)
}
require(success, "ETH_TRANSFER_FAILED");
}
/*//////////////////////////////////////////////////////////////
ERC20 OPERATIONS
//////////////////////////////////////////////////////////////*/functionsafeTransferFrom(
ERC20 token,
addressfrom,
address to,
uint256 amount
) internal{
bool success;
/// @solidity memory-safe-assemblyassembly {
// Get a pointer to some free memory.let freeMemoryPointer :=mload(0x40)
// Write the abi-encoded calldata into memory, beginning with the function selector.mstore(freeMemoryPointer, 0x23b872dd00000000000000000000000000000000000000000000000000000000)
mstore(add(freeMemoryPointer, 4), and(from, 0xffffffffffffffffffffffffffffffffffffffff)) // Append and mask the "from" argument.mstore(add(freeMemoryPointer, 36), and(to, 0xffffffffffffffffffffffffffffffffffffffff)) // Append and mask the "to" argument.mstore(add(freeMemoryPointer, 68), amount) // Append the "amount" argument. Masking not required as it's a full 32 byte type.
success :=and(
// Set success to whether the call reverted, if not we check it either// returned exactly 1 (can't just be non-zero data), or had no return data.or(and(eq(mload(0), 1), gt(returndatasize(), 31)), iszero(returndatasize())),
// We use 100 because the length of our calldata totals up like so: 4 + 32 * 3.// We use 0 and 32 to copy up to 32 bytes of return data into the scratch space.// Counterintuitively, this call must be positioned second to the or() call in the// surrounding and() call or else returndatasize() will be zero during the computation.call(gas(), token, 0, freeMemoryPointer, 100, 0, 32)
)
}
require(success, "TRANSFER_FROM_FAILED");
}
functionsafeTransfer(
ERC20 token,
address to,
uint256 amount
) internal{
bool success;
/// @solidity memory-safe-assemblyassembly {
// Get a pointer to some free memory.let freeMemoryPointer :=mload(0x40)
// Write the abi-encoded calldata into memory, beginning with the function selector.mstore(freeMemoryPointer, 0xa9059cbb00000000000000000000000000000000000000000000000000000000)
mstore(add(freeMemoryPointer, 4), and(to, 0xffffffffffffffffffffffffffffffffffffffff)) // Append and mask the "to" argument.mstore(add(freeMemoryPointer, 36), amount) // Append the "amount" argument. Masking not required as it's a full 32 byte type.
success :=and(
// Set success to whether the call reverted, if not we check it either// returned exactly 1 (can't just be non-zero data), or had no return data.or(and(eq(mload(0), 1), gt(returndatasize(), 31)), iszero(returndatasize())),
// We use 68 because the length of our calldata totals up like so: 4 + 32 * 2.// We use 0 and 32 to copy up to 32 bytes of return data into the scratch space.// Counterintuitively, this call must be positioned second to the or() call in the// surrounding and() call or else returndatasize() will be zero during the computation.call(gas(), token, 0, freeMemoryPointer, 68, 0, 32)
)
}
require(success, "TRANSFER_FAILED");
}
functionsafeApprove(
ERC20 token,
address to,
uint256 amount
) internal{
bool success;
/// @solidity memory-safe-assemblyassembly {
// Get a pointer to some free memory.let freeMemoryPointer :=mload(0x40)
// Write the abi-encoded calldata into memory, beginning with the function selector.mstore(freeMemoryPointer, 0x095ea7b300000000000000000000000000000000000000000000000000000000)
mstore(add(freeMemoryPointer, 4), and(to, 0xffffffffffffffffffffffffffffffffffffffff)) // Append and mask the "to" argument.mstore(add(freeMemoryPointer, 36), amount) // Append the "amount" argument. Masking not required as it's a full 32 byte type.
success :=and(
// Set success to whether the call reverted, if not we check it either// returned exactly 1 (can't just be non-zero data), or had no return data.or(and(eq(mload(0), 1), gt(returndatasize(), 31)), iszero(returndatasize())),
// We use 68 because the length of our calldata totals up like so: 4 + 32 * 2.// We use 0 and 32 to copy up to 32 bytes of return data into the scratch space.// Counterintuitively, this call must be positioned second to the or() call in the// surrounding and() call or else returndatasize() will be zero during the computation.call(gas(), token, 0, freeMemoryPointer, 68, 0, 32)
)
}
require(success, "APPROVE_FAILED");
}
}
Código Fuente del Contrato
Archivo 11 de 11: WagmiCompetition.sol
// SPDX-License-Identifier: MIT//// $$\ $$\ $$$$$$\ $$$$$$\ $$\ $$\ $$$$$$\// $$ | $\ $$ |$$ __$$\ $$ __$$\ $$$\ $$$ |\_$$ _|// $$ |$$$\ $$ |$$ / $$ |$$ / \__|$$$$\ $$$$ | $$ |// $$ $$ $$\$$ |$$$$$$$$ |$$ |$$$$\ $$\$$\$$ $$ | $$ |// $$$$ _$$$$ |$$ __$$ |$$ |\_$$ |$$ \$$$ $$ | $$ |// $$$ / \$$$ |$$ | $$ |$$ | $$ |$$ |\$ /$$ | $$ |// $$ / \$$ |$$ | $$ |\$$$$$$ |$$ | \_/ $$ |$$$$$$\// \__/ \__|\__| \__| \______/ \__| \__|\______|//pragmasolidity ^0.8.20;import {IWagmiCompetition} from"./interfaces/IWagmiCompetition.sol";
import {ISupraRouter} from"./interfaces/ISupraRouter.sol";
import {Math} from"./libraries/Math.sol";
import {PercentageMath} from"./libraries/PercentageMath.sol";
import {SafeTransferLib} from"solmate/src/utils/SafeTransferLib.sol";
import {Ownable} from"@openzeppelin/contracts/access/Ownable.sol";
import {Pausable} from"@openzeppelin/contracts/utils/Pausable.sol";
contractWagmiCompetitionisIWagmiCompetition, Ownable, Pausable{
usingMathforuint256;
usingPercentageMathforuint256;
// Game settingsuint256privateconstant MAX_X =5_000;
uint256privateconstant MAX_Y =5_000;
uint256privateconstant CENTER_MAX_SHIFT =194;
uint256privateconstant CENTER_RANGE = CENTER_MAX_SHIFT *2;
// Investment & Sales settingsuint256privateconstant TIME_AFTER_FUNDED =2days;
// uint256 private constant MAX_TICKETS_PER_PLAYER = 150;uint256privateconstant MAX_FREE_TICKETS =5;
uint256privateconstant REFERRAL_FACTOR_BPS =95_00;
uint256privateconstant REFERRAL_REWARDS_BPS =60_00;
uint256privateconstant INVESTOR_DISTRIBUTION_BPS =60_00;
uint256privateimmutable INVESTMENT_GOAL;
uint256privateimmutable REFERRAL_THRESHOLD;
// Game setupbytes32privateimmutable COMMIT;
uint256public commitX;
uint256public commitY;
// Accountingmapping(address=>uint256) public salesOf;
mapping(address=>uint256) public ticketsOf;
mapping(address=>uint256) public investmentOf;
mapping(bytes32=> Coupon) public couponFromHash;
uint256public claimableInvestment; // Total amount dedicated for distribution over investmentOfuint256public totalSales;
uint256public totalInvestment;
uint256public endTimestamp;
// Supra VRFuint8privateconstant NB_WORDS =1;
uint256privateconstant MIN_REQ_CONFIRMATIONS =2;
uint32privateconstant CALLBACK_GAS_LIMIT =100_000;
bytes32privateimmutable KEY_HASH;
uint64privateimmutable SUBSCRIPTION_ID; // Chainlink VRF subscription ID// address supraAddr;// address supraClientAddress;
ISupraRouter internal ISUPRA_ROUTER; // Supra coordinatoraddressinternal CLIENT_ADDRESS; // Supra client addressuint256private centerRequestId;
uint256private winnerRequestId;
uint256public centerX;
uint256public centerY;
// Game state
Bet[] public bets;
Prize[] internal _prizes;
Bet public winningBet;
State public state;
uint256[] public closestBetIds;
modifieratState(State requiredState) {
require(state == requiredState, "Inappropriate state");
_;
}
modifieratStates(State state1, State state2) {
require(state == state1 || state == state2, "Inappropriate state");
_;
}
constructor(address coordinator,
bytes32 keyHash,
bytes32 commit,
uint64 subscriptionId,
Prize[] memory prizes,
uint256 maxPrizePrice,
uint256 referralThreshold,
address clientAddress
) Ownable(msg.sender) {
COMMIT = commit;
SUBSCRIPTION_ID = subscriptionId;
ISUPRA_ROUTER = ISupraRouter(coordinator);
CLIENT_ADDRESS = clientAddress;
KEY_HASH = keyHash;
INVESTMENT_GOAL = maxPrizePrice;
REFERRAL_THRESHOLD = referralThreshold;
uint256 nbPrizes = prizes.length;
for (uint256 i; i < nbPrizes; ++i) {
_prizes.push(prizes[i]);
}
state = State.Initial;
}
/// HOUSE ///functionsetCouponPrizeId(bytes32[] calldata couponHashes,
Coupon[] calldata coupons
) externalonlyOwneratStates(State.Initial, State.InvestmentReached) {
uint256 nbCoupons = coupons.length;
require(
couponHashes.length== nbCoupons,
"Incompatible nb of hashes & coupons"
);
for (uint256 i; i < nbCoupons; ++i) {
Coupon memory coupon = coupons[i];
uint256 prizeId = coupon.prizeId;
require(prizeId <= _prizes.length, "Invalid prize ID");
couponFromHash[couponHashes[i]] = coupon;
}
}
functionsetIsPaused(bool isPaused) externalonlyOwner{
if (isPaused) _pause();
else _unpause();
}
functionfinishGame(uint256 x,
uint256 y
) externalonlyOwneratState(State.InvestmentReached) {
require(COMMIT ==keccak256(abi.encode(x, y)), "Commit does not match");
require(
block.timestamp> endTimestamp,
"Finish game after timeout only "
);
commitX = x % MAX_X;
commitY = y % MAX_Y;
centerRequestId = ISUPRA_ROUTER.generateRequest(
"rawFulfillRandomWords(uint256,uint256[])",
NB_WORDS,
MIN_REQ_CONFIRMATIONS,
CLIENT_ADDRESS
);
state = State.GameFinished;
emit GameFinished(totalInvestment, totalSales);
}
functionfindWinner(uint256[] calldata _closestBetIds
) externalonlyOwneratState(State.RandomCoordinatesExecuted) {
require(_closestBetIds.length>0, "At least 1 candidate required");
closestBetIds = _closestBetIds;
if (closestBetIds.length==1) return _setRandomWinner(0);
winnerRequestId = ISUPRA_ROUTER.generateRequest(
"rawFulfillRandomWords(uint256,uint256[])",
NB_WORDS,
MIN_REQ_CONFIRMATIONS,
CLIENT_ADDRESS
);
state = State.FindWinnerExecuted;
}
functionemergencyTerminate() externalonlyOwner{
state = State.EmergencyTerminated;
emit GameTerminated(totalInvestment, totalSales);
}
functionwithdrawPrizePurchaseFunds(uint256 amount
) externalonlyOwneratState(State.WinnerFound) {
amount = Math.min(amount, wonPrize().price);
uint256 salesProfit = totalSales.zeroFloorSub(amount);
uint256 houseProfit;
if (salesProfit >0) {
// Sales cover the prize purchase: investors are in profit.uint256 investmentProfit = (salesProfit.percentMul(
INVESTOR_DISTRIBUTION_BPS
) * totalInvestment) / INVESTMENT_GOAL;
houseProfit = salesProfit - investmentProfit;
claimableInvestment = totalInvestment + investmentProfit;
} else {
// Sales don't cover the prize purchase: investors are in loss.
claimableInvestment = totalInvestment + totalSales - amount;
}
state = State.FundsWithdrawn;
emit PurchasePrizeFundsWitdhrawn(amount, houseProfit);
SafeTransferLib.safeTransferETH(msg.sender, amount + houseProfit);
}
/// EXTERNAL ////// @notice Used by investors to invest in the competition.functioninvest() externalpayableatState(State.Initial) whenNotPaused{
uint256 max = INVESTMENT_GOAL
.zeroFloorSub(totalInvestment)
.zeroFloorSub(totalSales);
require(max >0||msg.sender== owner(), "Investment goal reached");
uint256 amount =msg.value;
if (amount >= max) {
amount = max;
state = State.InvestmentReached;
endTimestamp =block.timestamp+ TIME_AFTER_FUNDED;
}
totalInvestment += amount;
investmentOf[msg.sender] += amount;
emit Invested(msg.sender, amount);
}
/// @notice Used by players to place bets on the location of the commit./// @dev Player must send the total price of tickets along with the tx.functionplaceBets(
Bet[] calldata userBets,
address referral
)
externalpayableatStates(State.Initial, State.InvestmentReached)
whenNotPaused{
require(userBets.length>0, "At least 1 bet required");
require(
referral !=msg.sender,
"Referal and referee cannot be the same"
);
require(
state == State.Initial || endTimestamp >block.timestamp,
"Game stopped after timeout"
);
if (prize(userBets[0].prizeId).ticketPrice ==0) {
require(
(ticketsOf[msg.sender] + userBets.length<= MAX_FREE_TICKETS),
"Too many bets"
);
}
uint256 totalPrice;
uint256 nbBets = userBets.length;
for (uint256 i; i < nbBets; ++i) {
Bet memory bet = userBets[i];
uint256 x = bet.x;
uint256 y = bet.y;
require((x >0|| y >0), "x == 0 && y == 0");
require(x <= MAX_X, "X > max");
require(y <= MAX_Y, "Y > max");
bytes32 couponCode = bet.couponCode;
uint256 prizeId = bet.prizeId;
if (couponCode !=bytes32(0)) {
bytes32 couponHash =keccak256(abi.encode(couponCode));
Coupon storage coupon = couponFromHash[couponHash];
uint128 remaining = coupon.remaining;
require(remaining >0, "No remaining coupon usage");
uint256 couponPrizeId = coupon.prizeId;
require(
couponPrizeId >0&& couponPrizeId == prizeId,
"Coupon used with incorrect prize"
);
coupon.remaining = remaining -1;
} else {
totalPrice += prize(prizeId).ticketPrice;
}
bet.player =msg.sender;
bets.push(bet);
emit BetPlaced(msg.sender, prizeId, x, y);
}
uint256 referralReward;
if (isReferralValid(referral)) {
referralReward = totalPrice.percentMul(REFERRAL_REWARDS_BPS);
totalPrice = totalPrice.percentMul(REFERRAL_FACTOR_BPS);
}
require(msg.value== totalPrice, "Invalid value");
ticketsOf[msg.sender] += nbBets;
emit TotalBetPlaced(msg.sender, nbBets);
if (totalPrice >0) {
uint256 sales = totalPrice - referralReward;
salesOf[msg.sender] += sales;
totalSales += sales;
}
if (referralReward >0) {
emit ReferralRewarded(referral, msg.sender, referralReward);
SafeTransferLib.safeTransferETH(referral, referralReward);
}
}
functionclaimInvestment() externalatState(State.FundsWithdrawn) {
require(investmentOf[msg.sender] !=0, "Nothing to claim");
uint256 profit = (claimableInvestment * investmentOf[msg.sender]) /
totalInvestment;
investmentOf[msg.sender] =0;
emit InvestmentClaimed(msg.sender, profit);
if (profit >0) SafeTransferLib.safeTransferETH(msg.sender, profit);
}
/// @notice Claims investment in case the game was terminated in emergency.functionclaimInvestmentEmergency()
externalatState(State.EmergencyTerminated)
{
uint256 claim = investmentOf[msg.sender];
require(claim >0, "Nothing to claim");
investmentOf[msg.sender] =0;
emit InvestmentClaimedEmergency(msg.sender, claim);
SafeTransferLib.safeTransferETH(msg.sender, claim);
}
/// @notice Claims tickets refund in case the game was terminated in emergency.functionclaimTicketsEmergency()
externalatState(State.EmergencyTerminated)
{
uint256 claim = salesOf[msg.sender];
require(claim >0, "Nothing to claim");
salesOf[msg.sender] =0;
emit TicketsClaimedEmergency(msg.sender, claim);
SafeTransferLib.safeTransferETH(msg.sender, claim);
}
/// @dev Supra VRF's callback.functionrawFulfillRandomWords(uint256 requestId,
uint256[] calldata randomWords
) external{
require(
msg.sender==address(ISUPRA_ROUTER),
"Unauthorized coordinator"
);
if (requestId == winnerRequestId) {
return _setRandomWinner(randomWords[0]);
}
if (requestId == centerRequestId) {
return _setRandomCenter(randomWords[0]);
}
}
/// PUBLIC ///functionprize(uint256 prizeId) publicviewreturns (Prize memory) {
require(prizeId >0&& prizeId <= _prizes.length, "Invalid prize ID");
return _prizes[prizeId -1];
}
functionallBets() publicviewreturns (Bet[] memory) {
return bets;
}
functionwonPrize() publicviewreturns (Prize memory) {
return prize(winningBet.prizeId);
}
functionisReferralValid(address referral) publicviewreturns (bool) {
if (referral !=address(0)) {
returntrue;
//uint256 totalInvested = salesOf[referral];//totalInvested += investmentOf[referral];//return (totalInvested >= REFERRAL_THRESHOLD);
}
returnfalse;
}
functionisClaimInvestmentAllowed() publicviewreturns (bool) {
return state == State.FundsWithdrawn;
}
functionisInvestmentAllowed() publicviewreturns (bool) {
return state == State.Initial;
}
functionisFinishGameExecuted() publicviewreturns (bool) {
return
state == State.RandomCoordinatesExecuted ||
state == State.FindWinnerExecuted ||
state == State.WinnerFound ||
state == State.FundsWithdrawn;
}
functionisWinnerFound() publicviewreturns (bool) {
return state == State.WinnerFound || state == State.FundsWithdrawn;
}
functionisPlacingBetsAllowed() publicviewreturns (bool) {
if (state == State.Initial) {
returntrue;
}
if (state == State.InvestmentReached) {
return endTimestamp >block.timestamp;
}
returnfalse;
}
functionisGameTerminated() publicviewreturns (bool) {
return state == State.EmergencyTerminated;
}
/// INTERNAL ///function_setRandomCenter(uint256 random) internal{
uint256 xShift = random % CENTER_RANGE;
uint256 yShift = (random >>16) % CENTER_RANGE;
if (xShift >= CENTER_MAX_SHIFT) {
xShift -= CENTER_MAX_SHIFT;
centerX = ((commitX + xShift) > MAX_X) ? MAX_X : (commitX + xShift);
} else {
centerX = (commitX < xShift) ? 0 : (commitX - xShift);
}
if (yShift >= CENTER_MAX_SHIFT) {
yShift -= CENTER_MAX_SHIFT;
centerY = ((commitY + yShift) > MAX_Y) ? MAX_Y : (commitY + yShift);
} else {
centerY = (commitY < yShift) ? 0 : (commitY - yShift);
}
state = State.RandomCoordinatesExecuted;
emit BallPositionFound(centerX, centerY);
}
function_setRandomWinner(uint256 random) private{
winningBet = bets[closestBetIds[random % closestBetIds.length]];
state = State.WinnerFound;
emit WinningBetFound(winningBet);
}
}