// SPDX-License-Identifier: AGPL-3.0-onlypragmasolidity >=0.8.0;/// @notice Provides a flexible and updatable auth pattern which is completely separate from application logic./// @author Solmate (https://github.com/Rari-Capital/solmate/blob/main/src/auth/Auth.sol)/// @author Modified from Dappsys (https://github.com/dapphub/ds-auth/blob/master/src/auth.sol)abstractcontractAuth{
eventOwnerUpdated(addressindexed user, addressindexed newOwner);
eventAuthorityUpdated(addressindexed user, Authority indexed newAuthority);
addresspublic owner;
Authority public authority;
constructor(address _owner, Authority _authority) {
owner = _owner;
authority = _authority;
emit OwnerUpdated(msg.sender, _owner);
emit AuthorityUpdated(msg.sender, _authority);
}
modifierrequiresAuth() {
require(isAuthorized(msg.sender, msg.sig), "UNAUTHORIZED");
_;
}
functionisAuthorized(address user, bytes4 functionSig) internalviewvirtualreturns (bool) {
Authority auth = authority; // Memoizing authority saves us a warm SLOAD, around 100 gas.// Checking if the caller is the owner only after calling the authority saves gas in most cases, but be// aware that this makes protected functions uncallable even to the owner if the authority is out of order.return (address(auth) !=address(0) && auth.canCall(user, address(this), functionSig)) || user == owner;
}
functionsetAuthority(Authority newAuthority) publicvirtual{
// We check if the caller is the owner first because we want to ensure they can// always swap out the authority even if it's reverting or using up a lot of gas.require(msg.sender== owner || authority.canCall(msg.sender, address(this), msg.sig));
authority = newAuthority;
emit AuthorityUpdated(msg.sender, newAuthority);
}
functionsetOwner(address newOwner) publicvirtualrequiresAuth{
owner = newOwner;
emit OwnerUpdated(msg.sender, newOwner);
}
}
/// @notice A generic interface for a contract which provides authorization data to an Auth instance./// @author Solmate (https://github.com/Rari-Capital/solmate/blob/main/src/auth/Auth.sol)/// @author Modified from Dappsys (https://github.com/dapphub/ds-auth/blob/master/src/auth.sol)interfaceAuthority{
functioncanCall(address user,
address target,
bytes4 functionSig
) externalviewreturns (bool);
}
Contract Source Code
File 2 of 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/Rari-Capital/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
//////////////////////////////////////////////////////////////*/bytes32publicconstant PERMIT_TYPEHASH =keccak256("Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)");
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 {
bytes32 digest =keccak256(
abi.encodePacked(
"\x19\x01",
DOMAIN_SEPARATOR(),
keccak256(abi.encode(PERMIT_TYPEHASH, owner, spender, value, nonces[owner]++, deadline))
)
);
address recoveredAddress =ecrecover(digest, 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);
}
}
Contract Source Code
File 3 of 11: ERC2981.sol
// SPDX-License-Identifier: AGPL-3.0-onlypragmasolidity >=0.8.0;abstractcontractERC2981{
/// @dev one global fee for all royalties.uint256internal _royaltyFee;
/// @dev one global recipient for all royalties.addressinternal _royaltyRecipient;
functionroyaltyInfo(uint256 tokenId, uint256 salePrice) publicviewvirtualreturns (address receiver,
uint256 royaltyAmount
) {
receiver = _royaltyRecipient;
royaltyAmount = (salePrice * _royaltyFee) /10000;
}
functionsupportsInterface(bytes4 interfaceId) publicpurevirtualreturns (bool) {
return
interfaceId ==0x01ffc9a7||// ERC165 Interface ID for ERC165
interfaceId ==0x2a55205a; // ERC165 Interface ID for ERC2981
}
}
Contract Source Code
File 4 of 11: ERC721.sol
// SPDX-License-Identifier: AGPL-3.0-onlypragmasolidity >=0.8.0;/// @notice Modern, minimalist, and gas efficient ERC-721 implementation./// @author Solmate (https://github.com/Rari-Capital/solmate/blob/main/src/tokens/ERC721.sol)/// @dev Note that balanceOf does not revert if passed the zero address, in defiance of the ERC.abstractcontractERC721{
/*///////////////////////////////////////////////////////////////
EVENTS
//////////////////////////////////////////////////////////////*/eventTransfer(addressindexedfrom, addressindexed to, uint256indexed id);
eventApproval(addressindexed owner, addressindexed spender, uint256indexed id);
eventApprovalForAll(addressindexed owner, addressindexed operator, bool approved);
/*///////////////////////////////////////////////////////////////
METADATA STORAGE/LOGIC
//////////////////////////////////////////////////////////////*/stringpublic name;
stringpublic symbol;
functiontokenURI(uint256 id) publicviewvirtualreturns (stringmemory);
/*///////////////////////////////////////////////////////////////
ERC721 STORAGE
//////////////////////////////////////////////////////////////*/mapping(address=>uint256) public balanceOf;
mapping(uint256=>address) public ownerOf;
mapping(uint256=>address) public getApproved;
mapping(address=>mapping(address=>bool)) public isApprovedForAll;
/*///////////////////////////////////////////////////////////////
CONSTRUCTOR
//////////////////////////////////////////////////////////////*/constructor(stringmemory _name, stringmemory _symbol) {
name = _name;
symbol = _symbol;
}
/*///////////////////////////////////////////////////////////////
ERC721 LOGIC
//////////////////////////////////////////////////////////////*/functionapprove(address spender, uint256 id) publicvirtual{
address owner = ownerOf[id];
require(msg.sender== owner || isApprovedForAll[owner][msg.sender], "NOT_AUTHORIZED");
getApproved[id] = spender;
emit Approval(owner, spender, id);
}
functionsetApprovalForAll(address operator, bool approved) publicvirtual{
isApprovedForAll[msg.sender][operator] = approved;
emit ApprovalForAll(msg.sender, operator, approved);
}
functiontransferFrom(addressfrom,
address to,
uint256 id
) publicvirtual{
require(from== ownerOf[id], "WRONG_FROM");
require(to !=address(0), "INVALID_RECIPIENT");
require(
msg.sender==from||msg.sender== getApproved[id] || isApprovedForAll[from][msg.sender],
"NOT_AUTHORIZED"
);
// Underflow of the sender's balance is impossible because we check for// ownership above and the recipient's balance can't realistically overflow.unchecked {
balanceOf[from]--;
balanceOf[to]++;
}
ownerOf[id] = to;
delete getApproved[id];
emit Transfer(from, to, id);
}
functionsafeTransferFrom(addressfrom,
address to,
uint256 id
) publicvirtual{
transferFrom(from, to, id);
require(
to.code.length==0||
ERC721TokenReceiver(to).onERC721Received(msg.sender, from, id, "") ==
ERC721TokenReceiver.onERC721Received.selector,
"UNSAFE_RECIPIENT"
);
}
functionsafeTransferFrom(addressfrom,
address to,
uint256 id,
bytesmemory data
) publicvirtual{
transferFrom(from, to, id);
require(
to.code.length==0||
ERC721TokenReceiver(to).onERC721Received(msg.sender, from, id, data) ==
ERC721TokenReceiver.onERC721Received.selector,
"UNSAFE_RECIPIENT"
);
}
/*///////////////////////////////////////////////////////////////
ERC165 LOGIC
//////////////////////////////////////////////////////////////*/functionsupportsInterface(bytes4 interfaceId) publicpurevirtualreturns (bool) {
return
interfaceId ==0x01ffc9a7||// ERC165 Interface ID for ERC165
interfaceId ==0x80ac58cd||// ERC165 Interface ID for ERC721
interfaceId ==0x5b5e139f; // ERC165 Interface ID for ERC721Metadata
}
/*///////////////////////////////////////////////////////////////
INTERNAL MINT/BURN LOGIC
//////////////////////////////////////////////////////////////*/function_mint(address to, uint256 id) internalvirtual{
require(to !=address(0), "INVALID_RECIPIENT");
require(ownerOf[id] ==address(0), "ALREADY_MINTED");
// Counter overflow is incredibly unrealistic.unchecked {
balanceOf[to]++;
}
ownerOf[id] = to;
emit Transfer(address(0), to, id);
}
function_burn(uint256 id) internalvirtual{
address owner = ownerOf[id];
require(ownerOf[id] !=address(0), "NOT_MINTED");
// Ownership check above ensures no underflow.unchecked {
balanceOf[owner]--;
}
delete ownerOf[id];
delete getApproved[id];
emit Transfer(owner, address(0), id);
}
/*///////////////////////////////////////////////////////////////
INTERNAL SAFE MINT LOGIC
//////////////////////////////////////////////////////////////*/function_safeMint(address to, uint256 id) internalvirtual{
_mint(to, id);
require(
to.code.length==0||
ERC721TokenReceiver(to).onERC721Received(msg.sender, address(0), id, "") ==
ERC721TokenReceiver.onERC721Received.selector,
"UNSAFE_RECIPIENT"
);
}
function_safeMint(address to,
uint256 id,
bytesmemory data
) internalvirtual{
_mint(to, id);
require(
to.code.length==0||
ERC721TokenReceiver(to).onERC721Received(msg.sender, address(0), id, data) ==
ERC721TokenReceiver.onERC721Received.selector,
"UNSAFE_RECIPIENT"
);
}
}
/// @notice A generic interface for a contract which properly accepts ERC721 tokens./// @author Solmate (https://github.com/Rari-Capital/solmate/blob/main/src/tokens/ERC721.sol)interfaceERC721TokenReceiver{
functiononERC721Received(address operator,
addressfrom,
uint256 id,
bytescalldata data
) externalreturns (bytes4);
}
// 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/Rari-Capital/solmate/blob/main/src/utils/SafeTransferLib.sol)/// @author Modified from Gnosis (https://github.com/gnosis/gp-v2-contracts/blob/main/src/contracts/libraries/GPv2SafeERC20.sol)/// @dev Use with caution! Some functions in this library knowingly create dirty bits at the destination of the free memory pointer.librarySafeTransferLib{
/*///////////////////////////////////////////////////////////////
ETH OPERATIONS
//////////////////////////////////////////////////////////////*/functionsafeTransferETH(address to, uint256 amount) internal{
bool callStatus;
assembly {
// Transfer the ETH and store if it succeeded or not.
callStatus :=call(gas(), to, amount, 0, 0, 0, 0)
}
require(callStatus, "ETH_TRANSFER_FAILED");
}
/*///////////////////////////////////////////////////////////////
ERC20 OPERATIONS
//////////////////////////////////////////////////////////////*/functionsafeTransferFrom(
ERC20 token,
addressfrom,
address to,
uint256 amount
) internal{
bool callStatus;
assembly {
// Get a pointer to some free memory.let freeMemoryPointer :=mload(0x40)
// Write the abi-encoded calldata to memory piece by piece:mstore(freeMemoryPointer, 0x23b872dd00000000000000000000000000000000000000000000000000000000) // Begin with the function selector.mstore(add(freeMemoryPointer, 4), and(from, 0xffffffffffffffffffffffffffffffffffffffff)) // Mask and append the "from" argument.mstore(add(freeMemoryPointer, 36), and(to, 0xffffffffffffffffffffffffffffffffffffffff)) // Mask and append the "to" argument.mstore(add(freeMemoryPointer, 68), amount) // Finally append the "amount" argument. No mask as it's a full 32 byte value.// Call the token and store if it succeeded or not.// We use 100 because the calldata length is 4 + 32 * 3.
callStatus :=call(gas(), token, 0, freeMemoryPointer, 100, 0, 0)
}
require(didLastOptionalReturnCallSucceed(callStatus), "TRANSFER_FROM_FAILED");
}
functionsafeTransfer(
ERC20 token,
address to,
uint256 amount
) internal{
bool callStatus;
assembly {
// Get a pointer to some free memory.let freeMemoryPointer :=mload(0x40)
// Write the abi-encoded calldata to memory piece by piece:mstore(freeMemoryPointer, 0xa9059cbb00000000000000000000000000000000000000000000000000000000) // Begin with the function selector.mstore(add(freeMemoryPointer, 4), and(to, 0xffffffffffffffffffffffffffffffffffffffff)) // Mask and append the "to" argument.mstore(add(freeMemoryPointer, 36), amount) // Finally append the "amount" argument. No mask as it's a full 32 byte value.// Call the token and store if it succeeded or not.// We use 68 because the calldata length is 4 + 32 * 2.
callStatus :=call(gas(), token, 0, freeMemoryPointer, 68, 0, 0)
}
require(didLastOptionalReturnCallSucceed(callStatus), "TRANSFER_FAILED");
}
functionsafeApprove(
ERC20 token,
address to,
uint256 amount
) internal{
bool callStatus;
assembly {
// Get a pointer to some free memory.let freeMemoryPointer :=mload(0x40)
// Write the abi-encoded calldata to memory piece by piece:mstore(freeMemoryPointer, 0x095ea7b300000000000000000000000000000000000000000000000000000000) // Begin with the function selector.mstore(add(freeMemoryPointer, 4), and(to, 0xffffffffffffffffffffffffffffffffffffffff)) // Mask and append the "to" argument.mstore(add(freeMemoryPointer, 36), amount) // Finally append the "amount" argument. No mask as it's a full 32 byte value.// Call the token and store if it succeeded or not.// We use 68 because the calldata length is 4 + 32 * 2.
callStatus :=call(gas(), token, 0, freeMemoryPointer, 68, 0, 0)
}
require(didLastOptionalReturnCallSucceed(callStatus), "APPROVE_FAILED");
}
/*///////////////////////////////////////////////////////////////
INTERNAL HELPER LOGIC
//////////////////////////////////////////////////////////////*/functiondidLastOptionalReturnCallSucceed(bool callStatus) privatepurereturns (bool success) {
assembly {
// Get how many bytes the call returned.let returnDataSize :=returndatasize()
// If the call reverted:ifiszero(callStatus) {
// Copy the revert message into memory.returndatacopy(0, 0, returnDataSize)
// Revert with the same message.revert(0, returnDataSize)
}
switch returnDataSize
case32 {
// Copy the return data into memory.returndatacopy(0, 0, returnDataSize)
// Set success to whether it returned true.
success :=iszero(iszero(mload(0)))
}
case0 {
// There was no return data.
success :=1
}
default {
// It returned some malformed input.
success :=0
}
}
}
}
Contract Source Code
File 8 of 11: Strings.sol
// SPDX-License-Identifier: MIT// OpenZeppelin Contracts v4.4.1 (utils/Strings.sol)pragmasolidity ^0.8.0;/**
* @dev String operations.
*/libraryStrings{
bytes16privateconstant _HEX_SYMBOLS ="0123456789abcdef";
/**
* @dev Converts a `uint256` to its ASCII `string` decimal representation.
*/functiontoString(uint256 value) internalpurereturns (stringmemory) {
// Inspired by OraclizeAPI's implementation - MIT licence// https://github.com/oraclize/ethereum-api/blob/b42146b063c7d6ee1358846c198246239e9360e8/oraclizeAPI_0.4.25.solif (value ==0) {
return"0";
}
uint256 temp = value;
uint256 digits;
while (temp !=0) {
digits++;
temp /=10;
}
bytesmemory buffer =newbytes(digits);
while (value !=0) {
digits -=1;
buffer[digits] =bytes1(uint8(48+uint256(value %10)));
value /=10;
}
returnstring(buffer);
}
/**
* @dev Converts a `uint256` to its ASCII `string` hexadecimal representation.
*/functiontoHexString(uint256 value) internalpurereturns (stringmemory) {
if (value ==0) {
return"0x00";
}
uint256 temp = value;
uint256 length =0;
while (temp !=0) {
length++;
temp >>=8;
}
return toHexString(value, length);
}
/**
* @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length.
*/functiontoHexString(uint256 value, uint256 length) internalpurereturns (stringmemory) {
bytesmemory buffer =newbytes(2* length +2);
buffer[0] ="0";
buffer[1] ="x";
for (uint256 i =2* length +1; i >1; --i) {
buffer[i] = _HEX_SYMBOLS[value &0xf];
value >>=4;
}
require(value ==0, "Strings: hex length insufficient");
returnstring(buffer);
}
}
Contract Source Code
File 9 of 11: VRFConsumer.sol
pragmasolidity ^0.8.12;import"@chainlink/contracts/src/v0.8/interfaces/LinkTokenInterface.sol";
import"@chainlink/contracts/src/v0.8/interfaces/VRFCoordinatorV2Interface.sol";
import"@chainlink/contracts/src/v0.8/VRFConsumerBaseV2.sol";
import {Auth} from"@rari-capital/solmate/src/auth/Auth.sol";
abstractcontractVRFv2ConsumerisVRFConsumerBaseV2, Auth{
VRFCoordinatorV2Interface COORDINATOR;
LinkTokenInterface LINKTOKEN;
// Your subscription ID.uint64 s_subscriptionId;
// The gas lane to use, which specifies the maximum gas price to bump to.// For a list of available gas lanes on each network,// see https://docs.chain.link/docs/vrf-contracts/#configurationsbytes32 keyHash;
// Depends on the number of requested values that you want sent to the// fulfillRandomWords() function. Storing each word costs about 20,000 gas,// so 100,000 is a safe default for this example contract. Test and adjust// this limit based on the network that you select, the size of the request,// and the processing of the callback request in the fulfillRandomWords()// function.uint32 callbackGasLimit =100000;
// The default is 3, but you can set this higher.uint16 requestConfirmations =3;
uint256public s_requestId;
constructor(uint64 subscriptionId,
address vrfCoordinator,
address link,
bytes32 keyHash_
) VRFConsumerBaseV2(vrfCoordinator) {
COORDINATOR = VRFCoordinatorV2Interface(vrfCoordinator);
LINKTOKEN = LinkTokenInterface(link);
keyHash = keyHash_;
s_subscriptionId = subscriptionId;
}
// Assumes the subscription is funded sufficiently.functionrequestRandomWords() externalrequiresAuth{
// Will revert if subscription is not set and funded.
s_requestId = COORDINATOR.requestRandomWords(
keyHash,
s_subscriptionId,
requestConfirmations,
callbackGasLimit,
1
);
}
}
Contract Source Code
File 10 of 11: VRFConsumerBaseV2.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).
*/abstractcontractVRFConsumerBaseV2{
errorOnlyCoordinatorCanFulfill(address have, address want);
addressprivateimmutable vrfCoordinator;
/**
* @param _vrfCoordinator address of VRFCoordinator contract
*/constructor(address _vrfCoordinator) {
vrfCoordinator = _vrfCoordinator;
}
/**
* @notice fulfillRandomness handles the VRF response. Your contract must
* @notice implement it. See "SECURITY CONSIDERATIONS" above for important
* @notice principles to keep in mind when implementing your fulfillRandomness
* @notice method.
*
* @dev VRFConsumerBaseV2 expects its subcontracts to have a method with this
* @dev signature, and will call it once it has verified the proof
* @dev associated with the randomness. (It is triggered via a call to
* @dev rawFulfillRandomness, below.)
*
* @param requestId The Id initially returned by requestRandomness
* @param randomWords the VRF output expanded to the requested number of words
*/functionfulfillRandomWords(uint256 requestId, uint256[] memory randomWords) internalvirtual;
// rawFulfillRandomness is called by VRFCoordinator when it receives a valid VRF// proof. rawFulfillRandomness then calls fulfillRandomness, after validating// the origin of the callfunctionrawFulfillRandomWords(uint256 requestId, uint256[] memory randomWords) external{
if (msg.sender!= vrfCoordinator) {
revert OnlyCoordinatorCanFulfill(msg.sender, vrfCoordinator);
}
fulfillRandomWords(requestId, randomWords);
}
}
Contract Source Code
File 11 of 11: VRFCoordinatorV2Interface.sol
// SPDX-License-Identifier: MITpragmasolidity ^0.8.0;interfaceVRFCoordinatorV2Interface{
/**
* @notice Get configuration relevant for making requests
* @return minimumRequestConfirmations global min for request confirmations
* @return maxGasLimit global max for request gas limit
* @return s_provingKeyHashes list of registered key hashes
*/functiongetRequestConfig()
externalviewreturns (uint16,
uint32,
bytes32[] memory);
/**
* @notice Request a set of random words.
* @param keyHash - Corresponds to a particular oracle job which uses
* that key for generating the VRF proof. Different keyHash's have different gas price
* ceilings, so you can select a specific one to bound your maximum per request cost.
* @param subId - The ID of the VRF subscription. Must be funded
* with the minimum subscription balance required for the selected keyHash.
* @param minimumRequestConfirmations - How many blocks you'd like the
* oracle to wait before responding to the request. See SECURITY CONSIDERATIONS
* for why you may want to request more. The acceptable range is
* [minimumRequestBlockConfirmations, 200].
* @param callbackGasLimit - How much gas you'd like to receive in your
* fulfillRandomWords callback. Note that gasleft() inside fulfillRandomWords
* may be slightly less than this amount because of gas used calling the function
* (argument decoding etc.), so you may need to request slightly more than you expect
* to have inside fulfillRandomWords. The acceptable range is
* [0, maxGasLimit]
* @param numWords - The number of uint256 random values you'd like to receive
* in your fulfillRandomWords callback. Note these numbers are expanded in a
* secure way by the VRFCoordinator from a single random value supplied by the oracle.
* @return requestId - A unique identifier of the request. Can be used to match
* a request to a response in fulfillRandomWords.
*/functionrequestRandomWords(bytes32 keyHash,
uint64 subId,
uint16 minimumRequestConfirmations,
uint32 callbackGasLimit,
uint32 numWords
) externalreturns (uint256 requestId);
/**
* @notice Create a VRF subscription.
* @return subId - A unique subscription id.
* @dev You can manage the consumer set dynamically with addConsumer/removeConsumer.
* @dev Note to fund the subscription, use transferAndCall. For example
* @dev LINKTOKEN.transferAndCall(
* @dev address(COORDINATOR),
* @dev amount,
* @dev abi.encode(subId));
*/functioncreateSubscription() externalreturns (uint64 subId);
/**
* @notice Get a VRF subscription.
* @param subId - ID of the subscription
* @return balance - LINK balance of the subscription in juels.
* @return reqCount - number of requests for this subscription, determines fee tier.
* @return owner - owner of the subscription.
* @return consumers - list of consumer address which are able to use this subscription.
*/functiongetSubscription(uint64 subId)
externalviewreturns (uint96 balance,
uint64 reqCount,
address owner,
address[] memory consumers
);
/**
* @notice Request subscription owner transfer.
* @param subId - ID of the subscription
* @param newOwner - proposed new owner of the subscription
*/functionrequestSubscriptionOwnerTransfer(uint64 subId, address newOwner) external;
/**
* @notice Request subscription owner transfer.
* @param subId - ID of the subscription
* @dev will revert if original owner of subId has
* not requested that msg.sender become the new owner.
*/functionacceptSubscriptionOwnerTransfer(uint64 subId) external;
/**
* @notice Add a consumer to a VRF subscription.
* @param subId - ID of the subscription
* @param consumer - New consumer which can use the subscription
*/functionaddConsumer(uint64 subId, address consumer) external;
/**
* @notice Remove a consumer from a VRF subscription.
* @param subId - ID of the subscription
* @param consumer - Consumer to remove from the subscription
*/functionremoveConsumer(uint64 subId, address consumer) external;
/**
* @notice Cancel a subscription
* @param subId - ID of the subscription
* @param to - Where to send the remaining LINK to
*/functioncancelSubscription(uint64 subId, address to) external;
}