编译器
0.8.23+commit.f704f362
文件 1 的 52:AggregatorV3Interface.sol
pragma solidity ^0.8.0;
interface AggregatorV3Interface {
function decimals() external view returns (uint8);
function description() external view returns (string memory);
function version() external view returns (uint256);
function getRoundData(
uint80 _roundId
) external view returns (uint80 roundId, int256 answer, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound);
function latestRoundData()
external
view
returns (uint80 roundId, int256 answer, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound);
}
文件 2 的 52:ArbGasInfo.sol
pragma solidity >=0.4.21 <0.9.0;
interface ArbGasInfo {
function getPricesInWeiWithAggregator(address aggregator) external view returns (uint, uint, uint, uint, uint, uint);
function getPricesInWei() external view returns (uint, uint, uint, uint, uint, uint);
function getPricesInArbGasWithAggregator(address aggregator) external view returns (uint, uint, uint);
function getPricesInArbGas() external view returns (uint, uint, uint);
function getGasAccountingParams() external view returns (uint, uint, uint);
function getL1GasPriceEstimate() external view returns(uint);
function setL1GasPriceEstimate(uint priceInWei) external;
function getCurrentTxL1GasFees() external view returns(uint);
}
文件 3 的 52:ArbSys.sol
pragma solidity >=0.4.21 <0.9.0;
interface ArbSys {
function arbBlockNumber() external view returns (uint256);
function arbBlockHash(uint256 arbBlockNum) external view returns (bytes32);
function arbChainID() external view returns (uint256);
function arbOSVersion() external view returns (uint256);
function getStorageGasAvailable() external view returns (uint256);
function isTopLevelCall() external view returns (bool);
function mapL1SenderContractAddressToL2Alias(address sender, address unused)
external
pure
returns (address);
function wasMyCallersAddressAliased() external view returns (bool);
function myCallersAddressWithoutAliasing() external view returns (address);
function withdrawEth(address destination)
external
payable
returns (uint256);
function sendTxToL1(address destination, bytes calldata data)
external
payable
returns (uint256);
function sendMerkleTreeState()
external
view
returns (
uint256 size,
bytes32 root,
bytes32[] memory partials
);
event L2ToL1Tx(
address caller,
address indexed destination,
uint256 indexed hash,
uint256 indexed position,
uint256 arbBlockNum,
uint256 ethBlockNum,
uint256 timestamp,
uint256 callvalue,
bytes data
);
event L2ToL1Transaction(
address caller,
address indexed destination,
uint256 indexed uniqueId,
uint256 indexed batchNumber,
uint256 indexInBatch,
uint256 arbBlockNum,
uint256 ethBlockNum,
uint256 timestamp,
uint256 callvalue,
bytes data
);
event SendMerkleUpdate(
uint256 indexed reserved,
bytes32 indexed hash,
uint256 indexed position
);
}
文件 4 的 52:Base64.sol
pragma solidity ^0.8.20;
library Base64 {
string internal constant _TABLE = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
function encode(bytes memory data) internal pure returns (string memory) {
if (data.length == 0) return "";
string memory table = _TABLE;
string memory result = new string(4 * ((data.length + 2) / 3));
assembly {
let tablePtr := add(table, 1)
let resultPtr := add(result, 32)
for {
let dataPtr := data
let endPtr := add(data, mload(data))
} lt(dataPtr, endPtr) {
} {
dataPtr := add(dataPtr, 3)
let input := mload(dataPtr)
mstore8(resultPtr, mload(add(tablePtr, and(shr(18, input), 0x3F))))
resultPtr := add(resultPtr, 1)
mstore8(resultPtr, mload(add(tablePtr, and(shr(12, input), 0x3F))))
resultPtr := add(resultPtr, 1)
mstore8(resultPtr, mload(add(tablePtr, and(shr(6, input), 0x3F))))
resultPtr := add(resultPtr, 1)
mstore8(resultPtr, mload(add(tablePtr, and(input, 0x3F))))
resultPtr := add(resultPtr, 1)
}
switch mod(mload(data), 3)
case 1 {
mstore8(sub(resultPtr, 1), 0x3d)
mstore8(sub(resultPtr, 2), 0x3d)
}
case 2 {
mstore8(sub(resultPtr, 1), 0x3d)
}
}
return result;
}
}
文件 5 的 52:BlockhashStoreInterface.sol
pragma solidity ^0.8.0;
interface BlockhashStoreInterface {
function getBlockhash(uint256 number) external view returns (bytes32);
}
文件 6 的 52:ChainSpecificUtil.sol
pragma solidity ^0.8.4;
import {ArbSys} from "./vendor/@arbitrum/nitro-contracts/src/precompiles/ArbSys.sol";
import {ArbGasInfo} from "./vendor/@arbitrum/nitro-contracts/src/precompiles/ArbGasInfo.sol";
library ChainSpecificUtil {
address private constant ARBSYS_ADDR = address(0x0000000000000000000000000000000000000064);
ArbSys private constant ARBSYS = ArbSys(ARBSYS_ADDR);
address private constant ARBGAS_ADDR = address(0x000000000000000000000000000000000000006C);
ArbGasInfo private constant ARBGAS = ArbGasInfo(ARBGAS_ADDR);
uint256 private constant ARB_MAINNET_CHAIN_ID = 42161;
uint256 private constant ARB_GOERLI_TESTNET_CHAIN_ID = 421613;
uint256 private constant ARB_SEPOLIA_TESTNET_CHAIN_ID = 421614;
function getBlockhash(uint64 blockNumber) internal view returns (bytes32) {
uint256 chainid = block.chainid;
if (
chainid == ARB_MAINNET_CHAIN_ID ||
chainid == ARB_GOERLI_TESTNET_CHAIN_ID ||
chainid == ARB_SEPOLIA_TESTNET_CHAIN_ID
) {
if ((getBlockNumber() - blockNumber) > 256 || blockNumber >= getBlockNumber()) {
return "";
}
return ARBSYS.arbBlockHash(blockNumber);
}
return blockhash(blockNumber);
}
function getBlockNumber() internal view returns (uint256) {
uint256 chainid = block.chainid;
if (chainid == ARB_MAINNET_CHAIN_ID || chainid == ARB_GOERLI_TESTNET_CHAIN_ID) {
return ARBSYS.arbBlockNumber();
}
return block.number;
}
function getCurrentTxL1GasFees() internal view returns (uint256) {
uint256 chainid = block.chainid;
if (chainid == ARB_MAINNET_CHAIN_ID || chainid == ARB_GOERLI_TESTNET_CHAIN_ID) {
return ARBGAS.getCurrentTxL1GasFees();
}
return 0;
}
function getL1CalldataGasCost(uint256 calldataSizeBytes) internal view returns (uint256) {
uint256 chainid = block.chainid;
if (chainid == ARB_MAINNET_CHAIN_ID || chainid == ARB_GOERLI_TESTNET_CHAIN_ID) {
(, uint256 l1PricePerByte, , , , ) = ARBGAS.getPricesInWei();
return l1PricePerByte * (calldataSizeBytes + 140);
}
return 0;
}
}
文件 7 的 52:ConfirmedOwner.sol
pragma solidity ^0.8.0;
import "./ConfirmedOwnerWithProposal.sol";
contract ConfirmedOwner is ConfirmedOwnerWithProposal {
constructor(address newOwner) ConfirmedOwnerWithProposal(newOwner, address(0)) {}
}
文件 8 的 52:ConfirmedOwnerWithProposal.sol
pragma solidity ^0.8.0;
import "../interfaces/IOwnable.sol";
contract ConfirmedOwnerWithProposal is IOwnable {
address private s_owner;
address private s_pendingOwner;
event OwnershipTransferRequested(address indexed from, address indexed to);
event OwnershipTransferred(address indexed from, address indexed to);
constructor(address newOwner, address pendingOwner) {
require(newOwner != address(0), "Cannot set owner to zero");
s_owner = newOwner;
if (pendingOwner != address(0)) {
_transferOwnership(pendingOwner);
}
}
function transferOwnership(address to) public override onlyOwner {
_transferOwnership(to);
}
function acceptOwnership() external override {
require(msg.sender == s_pendingOwner, "Must be proposed owner");
address oldOwner = s_owner;
s_owner = msg.sender;
s_pendingOwner = address(0);
emit OwnershipTransferred(oldOwner, msg.sender);
}
function owner() public view override returns (address) {
return s_owner;
}
function _transferOwnership(address to) private {
require(to != msg.sender, "Cannot transfer to self");
s_pendingOwner = to;
emit OwnershipTransferRequested(s_owner, to);
}
function _validateOwnership() internal view {
require(msg.sender == s_owner, "Only callable by owner");
}
modifier onlyOwner() {
_validateOwnership();
_;
}
}
文件 9 的 52:Context.sol
pragma solidity ^0.8.20;
abstract contract Context {
function _msgSender() internal view virtual returns (address) {
return msg.sender;
}
function _msgData() internal view virtual returns (bytes calldata) {
return msg.data;
}
function _contextSuffixLength() internal view virtual returns (uint256) {
return 0;
}
}
文件 10 的 52:ERC165.sol
pragma solidity ^0.8.20;
import {IERC165} from "./IERC165.sol";
abstract contract ERC165 is IERC165 {
function supportsInterface(bytes4 interfaceId) public view virtual returns (bool) {
return interfaceId == type(IERC165).interfaceId;
}
}
文件 11 的 52: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";
abstract contract ERC721 is Context, ERC165, IERC721, IERC721Metadata, IERC721Errors {
using Strings for uint256;
string private _name;
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;
constructor(string memory name_, string memory symbol_) {
_name = name_;
_symbol = symbol_;
}
function supportsInterface(bytes4 interfaceId) public view virtual override(ERC165, IERC165) returns (bool) {
return
interfaceId == type(IERC721).interfaceId ||
interfaceId == type(IERC721Metadata).interfaceId ||
super.supportsInterface(interfaceId);
}
function balanceOf(address owner) public view virtual returns (uint256) {
if (owner == address(0)) {
revert ERC721InvalidOwner(address(0));
}
return _balances[owner];
}
function ownerOf(uint256 tokenId) public view virtual returns (address) {
return _requireOwned(tokenId);
}
function name() public view virtual returns (string memory) {
return _name;
}
function symbol() public view virtual returns (string memory) {
return _symbol;
}
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()) : "";
}
function _baseURI() internal view virtual returns (string memory) {
return "";
}
function approve(address to, uint256 tokenId) public virtual {
_approve(to, tokenId, _msgSender());
}
function getApproved(uint256 tokenId) public view virtual returns (address) {
_requireOwned(tokenId);
return _getApproved(tokenId);
}
function setApprovalForAll(address operator, bool approved) public virtual {
_setApprovalForAll(_msgSender(), operator, approved);
}
function isApprovedForAll(address owner, address operator) public view virtual returns (bool) {
return _operatorApprovals[owner][operator];
}
function transferFrom(address from, address to, uint256 tokenId) public virtual {
if (to == address(0)) {
revert ERC721InvalidReceiver(address(0));
}
address previousOwner = _update(to, tokenId, _msgSender());
if (previousOwner != from) {
revert ERC721IncorrectOwner(from, tokenId, previousOwner);
}
}
function safeTransferFrom(address from, address to, uint256 tokenId) public {
safeTransferFrom(from, to, tokenId, "");
}
function safeTransferFrom(address from, address to, uint256 tokenId, bytes memory data) public virtual {
transferFrom(from, to, tokenId);
_checkOnERC721Received(from, to, tokenId, data);
}
function _ownerOf(uint256 tokenId) internal view virtual returns (address) {
return _owners[tokenId];
}
function _getApproved(uint256 tokenId) internal view virtual returns (address) {
return _tokenApprovals[tokenId];
}
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);
}
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);
}
}
}
function _increaseBalance(address account, uint128 value) internal virtual {
unchecked {
_balances[account] += value;
}
}
function _update(address to, uint256 tokenId, address auth) internal virtual returns (address) {
address from = _ownerOf(tokenId);
if (auth != address(0)) {
_checkAuthorized(from, auth, tokenId);
}
if (from != address(0)) {
_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;
}
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));
}
}
function _safeMint(address to, uint256 tokenId) internal {
_safeMint(to, tokenId, "");
}
function _safeMint(address to, uint256 tokenId, bytes memory data) internal virtual {
_mint(to, tokenId);
_checkOnERC721Received(address(0), to, tokenId, data);
}
function _burn(uint256 tokenId) internal {
address previousOwner = _update(address(0), tokenId, address(0));
if (previousOwner == address(0)) {
revert ERC721NonexistentToken(tokenId);
}
}
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);
}
}
function _safeTransfer(address from, address to, uint256 tokenId) internal {
_safeTransfer(from, to, tokenId, "");
}
function _safeTransfer(address from, address to, uint256 tokenId, bytes memory data) internal virtual {
_transfer(from, to, tokenId);
_checkOnERC721Received(from, to, tokenId, data);
}
function _approve(address to, uint256 tokenId, address auth) internal {
_approve(to, tokenId, auth, true);
}
function _approve(address to, uint256 tokenId, address auth, bool emitEvent) internal virtual {
if (emitEvent || auth != address(0)) {
address owner = _requireOwned(tokenId);
if (auth != address(0) && owner != auth && !isApprovedForAll(owner, auth)) {
revert ERC721InvalidApprover(auth);
}
if (emitEvent) {
emit Approval(owner, to, tokenId);
}
}
_tokenApprovals[tokenId] = to;
}
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);
}
function _requireOwned(uint256 tokenId) internal view returns (address) {
address owner = _ownerOf(tokenId);
if (owner == address(0)) {
revert ERC721NonexistentToken(tokenId);
}
return owner;
}
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 {
assembly {
revert(add(32, reason), mload(reason))
}
}
}
}
}
}
文件 12 的 52:Excavator.sol
pragma solidity >=0.7.0 <0.9.0;
import "@openzeppelin/contracts/access/Ownable.sol";
import "@chainlink/contracts/src/v0.8/shared/interfaces/LinkTokenInterface.sol";
import "@chainlink/contracts/src/v0.8/vrf/VRFCoordinatorV2.sol";
import "@chainlink/contracts/src/v0.8/vrf/VRFConsumerBaseV2.sol";
import "./utils/ExecutorManager.sol";
import "./utils/TransferHelper.sol";
import "./interfaces/ISwapRouter.sol";
import "./interfaces/IWETH.sol";
import "./interfaces/ITetraSpektra.sol";
contract Excavator is Ownable, ExecutorManager, VRFConsumerBaseV2 {
ISwapRouter public immutable uniswapRouter;
VRFCoordinatorV2 public immutable vrfCoordinator;
address public immutable WETH;
address public immutable LINK;
bytes32 public immutable KEY_HASH;
uint64 public vrfSubscriptionId;
ITetraSpektra immutable tetraSpektra;
uint32 _gasFeeLimit = 420_000;
constructor(
address executor,
address tetraSpektraAddress_,
address uniswapRouterAddress_,
address vrfCoordinatorAddress_,
bytes32 vrfKeyHash_,
address wethTokenAddress_,
address linkTokenAddress_
)
Ownable(msg.sender)
ExecutorManager(executor)
VRFConsumerBaseV2(vrfCoordinatorAddress_)
{
tetraSpektra = ITetraSpektra(tetraSpektraAddress_);
uniswapRouter = ISwapRouter(uniswapRouterAddress_);
vrfCoordinator = VRFCoordinatorV2(vrfCoordinatorAddress_);
WETH = wethTokenAddress_;
LINK = linkTokenAddress_;
KEY_HASH = vrfKeyHash_;
TransferHelper.safeApprove(
WETH,
address(uniswapRouter),
type(uint256).max
);
LinkTokenInterface(LINK).approve(
address(vrfCoordinator),
type(uint256).max
);
}
function initialize() public payable onlyOwner {
vrfSubscriptionId = vrfCoordinator.createSubscription();
require(isInitialized(), "0");
vrfCoordinator.addConsumer(vrfSubscriptionId, address(this));
IWETH(WETH).deposit{value: msg.value}();
uint256 outputAmount = _swapWethForLink(msg.value, address(this));
LinkTokenInterface(LINK).transferAndCall(
address(vrfCoordinator),
outputAmount,
abi.encode(vrfSubscriptionId)
);
}
function fund() external payable {
require(isInitialized(), "0");
IWETH(WETH).deposit{value: msg.value}();
uint256 outputAmount = _swapWethForLink(msg.value, address(this));
LinkTokenInterface(LINK).transferAndCall(
address(vrfCoordinator),
outputAmount,
abi.encode(vrfSubscriptionId)
);
}
function fundWithWeth(uint256 amount) external {
require(isInitialized(), "0");
require(amount > 0, "1");
require(IERC20(WETH).transferFrom(msg.sender, address(this), amount));
uint256 fundAmount = _swapWethForLink(amount, address(this));
require(fundAmount > 0, "2");
LinkTokenInterface(LINK).transferAndCall(
address(vrfCoordinator),
fundAmount,
abi.encode(vrfSubscriptionId)
);
}
function fundWithLink(uint256 amount) external {
require(isInitialized(), "0");
require(amount > 0, "1");
require(IERC20(LINK).transferFrom(msg.sender, address(this), amount));
uint256 fundAmount = IERC20(LINK).balanceOf(address(this));
LinkTokenInterface(LINK).transferAndCall(
address(vrfCoordinator),
fundAmount,
abi.encode(vrfSubscriptionId)
);
}
function isInitialized() public view returns (bool) {
return vrfSubscriptionId != 0;
}
function getLinkPrice() external view returns (uint256) {
(, int answer, , , ) = vrfCoordinator.LINK_ETH_FEED().latestRoundData();
uint256 linkPrice = (answer <= 0)
? 6_500_000_000_000_000
: uint256(answer);
return linkPrice;
}
function _swapWethForLink(
uint256 amountIn,
address recipient
) private returns (uint256) {
return
uniswapRouter.exactInputSingle(
ISwapRouter.ExactInputSingleParams({
tokenIn: WETH,
tokenOut: LINK,
fee: 3_000,
recipient: recipient,
amountIn: amountIn,
amountOutMinimum: 2,
sqrtPriceLimitX96: 0
})
);
}
function beginExcavation() public onlyOwner returns (uint256) {
uint256 excavationId = vrfCoordinator.requestRandomWords(
KEY_HASH,
vrfSubscriptionId,
3,
_gasFeeLimit,
1
);
return excavationId;
}
function updateGasFeeLimit(uint32 gasFeeLimit) external onlyExecutor {
require(gasFeeLimit > 330_000, "TOO_LOW");
_gasFeeLimit = gasFeeLimit;
}
function fulfillRandomWords(
uint256 excavationId,
uint256[] memory values
) internal override {
tetraSpektra.excavate(excavationId, values[0]);
}
function forceExcavate(
uint256 excavationId,
uint256 value
) public onlyOwner {
tetraSpektra.excavate(excavationId, value);
}
}
文件 13 的 52:ExecutorManager.sol
pragma solidity >=0.7.0 <0.9.0;
abstract contract ExecutorManager {
error NotExecutor(address attempted);
error CannotRemoveSelf();
mapping(address => bool) public executors;
constructor(address initialExecutor) {
_addExecutor(initialExecutor);
}
modifier onlyExecutor() {
if (isExecutor(msg.sender) != true) revert NotExecutor(msg.sender);
_;
}
function isExecutor(address _executor) public view returns (bool) {
return (executors[_executor] == true);
}
function _addExecutor(address _toAdd) internal {
executors[_toAdd] = true;
}
function addExecutor(address _toAdd) external virtual onlyExecutor {
_addExecutor(_toAdd);
}
function _removeExecutor(address _toRemove) internal {
if (_toRemove == msg.sender) revert CannotRemoveSelf();
executors[_toRemove] = false;
}
function removeExecutor(address _toRemove) external virtual onlyExecutor {
_removeExecutor(_toRemove);
}
}
文件 14 的 52:IBountyVault.sol
pragma solidity >=0.7.0 <0.9.0;
interface IBountyVault {
function distribute(
address recipient,
uint256 amount,
uint256 amountForCreator
) external;
function claimBounty(address tokenOwner, uint256 tokenId) external;
function isVaultUnlocked() external returns (bool);
}
文件 15 的 52:IClaimsManager.sol
pragma solidity >=0.7.0 <0.9.0;
interface IClaimsManager {
function isClaimsSessionExpired() external view returns (bool);
function getTimeRemainingForClaimsSession() external view returns (uint256);
function getClaimsSessionId() external view returns (uint256);
function enable() external;
function claim(address recipient, uint256 tokenId) external;
}
文件 16 的 52:IERC165.sol
pragma solidity ^0.8.20;
interface IERC165 {
function supportsInterface(bytes4 interfaceId) external view returns (bool);
}
文件 17 的 52:IERC20.sol
pragma solidity ^0.8.20;
interface IERC20 {
event Transfer(address indexed from, address indexed to, uint256 value);
event Approval(address indexed owner, address indexed spender, uint256 value);
function totalSupply() external view returns (uint256);
function balanceOf(address account) external view returns (uint256);
function transfer(address to, uint256 value) external returns (bool);
function allowance(address owner, address spender) external view returns (uint256);
function approve(address spender, uint256 value) external returns (bool);
function transferFrom(address from, address to, uint256 value) external returns (bool);
}
文件 18 的 52:IERC677Receiver.sol
pragma solidity ^0.8.6;
interface IERC677Receiver {
function onTokenTransfer(address sender, uint256 amount, bytes calldata data) external;
}
文件 19 的 52:IERC721.sol
pragma solidity ^0.8.20;
import {IERC165} from "../../utils/introspection/IERC165.sol";
interface IERC721 is IERC165 {
event Transfer(address indexed from, address indexed to, uint256 indexed tokenId);
event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId);
event ApprovalForAll(address indexed owner, address indexed operator, bool approved);
function balanceOf(address owner) external view returns (uint256 balance);
function ownerOf(uint256 tokenId) external view returns (address owner);
function safeTransferFrom(address from, address to, uint256 tokenId, bytes calldata data) external;
function safeTransferFrom(address from, address to, uint256 tokenId) external;
function transferFrom(address from, address to, uint256 tokenId) external;
function approve(address to, uint256 tokenId) external;
function setApprovalForAll(address operator, bool approved) external;
function getApproved(uint256 tokenId) external view returns (address operator);
function isApprovedForAll(address owner, address operator) external view returns (bool);
}
文件 20 的 52:IERC721Metadata.sol
pragma solidity ^0.8.20;
import {IERC721} from "../IERC721.sol";
interface IERC721Metadata is IERC721 {
function name() external view returns (string memory);
function symbol() external view returns (string memory);
function tokenURI(uint256 tokenId) external view returns (string memory);
}
文件 21 的 52:IERC721Receiver.sol
pragma solidity ^0.8.20;
interface IERC721Receiver {
function onERC721Received(
address operator,
address from,
uint256 tokenId,
bytes calldata data
) external returns (bytes4);
}
文件 22 的 52:IOwnable.sol
pragma solidity ^0.8.0;
interface IOwnable {
function owner() external returns (address);
function transferOwnership(address recipient) external;
function acceptOwnership() external;
}
文件 23 的 52:IPriceOracle.sol
pragma solidity >=0.7.0 <0.9.0;
interface IPriceOracle {
function getPrice(
address token0Address,
address token1Address,
uint128 amount,
uint24 poolFee
) external view returns (uint256 priceU18);
function getBasePrice(
address token0Address,
address token1Address,
uint24 poolFee
) external view returns (uint256 priceU18);
}
文件 24 的 52:IProspektorFundManager.sol
pragma solidity >=0.7.0 <0.9.0;
interface IProspektorFundManager {
function getTick() external view returns (int24);
function collectLPFees() external returns (uint256, uint256);
function addLiquidity(
uint token0Amount,
uint token1Amount
)
external
returns (uint128 liquidity, uint256 token0Value, uint256 token1Value);
}
文件 25 的 52:IRandomizer.sol
pragma solidity >=0.7.0 <0.9.0;
import "@openzeppelin/contracts/access/Ownable.sol";
interface IRandomizer {
function random() external returns (uint256);
}
文件 26 的 52:ISwapRouter.sol
pragma solidity >=0.7.0 <0.9.0;
interface ISwapRouter {
struct ExactInputSingleParams {
address tokenIn;
address tokenOut;
uint24 fee;
address recipient;
uint amountIn;
uint amountOutMinimum;
uint160 sqrtPriceLimitX96;
}
function exactInputSingle(
ExactInputSingleParams calldata params
) external payable returns (uint amountOut);
struct ExactInputParams {
bytes path;
address recipient;
uint amountIn;
uint amountOutMinimum;
}
function exactInput(
ExactInputParams calldata params
) external payable returns (uint amountOut);
}
文件 27 的 52:ITetraSpektra.sol
pragma solidity >=0.7.0 <0.9.0;
import "@openzeppelin/contracts/token/ERC721/ERC721.sol";
interface ITetraSpektra is IERC721 {
function excavate(uint256 excavationId, uint256 value) external;
function exchange(uint256[4] memory tokenIds) external;
function getGlyphKeys(uint256 tokenId) external returns (bytes32[3] memory);
}
文件 28 的 52:IUniswapV3Pool.sol
pragma solidity >=0.5.0;
import './pool/IUniswapV3PoolImmutables.sol';
import './pool/IUniswapV3PoolState.sol';
import './pool/IUniswapV3PoolDerivedState.sol';
import './pool/IUniswapV3PoolActions.sol';
import './pool/IUniswapV3PoolOwnerActions.sol';
import './pool/IUniswapV3PoolEvents.sol';
interface IUniswapV3Pool is
IUniswapV3PoolImmutables,
IUniswapV3PoolState,
IUniswapV3PoolDerivedState,
IUniswapV3PoolActions,
IUniswapV3PoolOwnerActions,
IUniswapV3PoolEvents
{
}
文件 29 的 52:IUniswapV3PoolActions.sol
pragma solidity >=0.5.0;
interface IUniswapV3PoolActions {
function initialize(uint160 sqrtPriceX96) external;
function mint(
address recipient,
int24 tickLower,
int24 tickUpper,
uint128 amount,
bytes calldata data
) external returns (uint256 amount0, uint256 amount1);
function collect(
address recipient,
int24 tickLower,
int24 tickUpper,
uint128 amount0Requested,
uint128 amount1Requested
) external returns (uint128 amount0, uint128 amount1);
function burn(
int24 tickLower,
int24 tickUpper,
uint128 amount
) external returns (uint256 amount0, uint256 amount1);
function swap(
address recipient,
bool zeroForOne,
int256 amountSpecified,
uint160 sqrtPriceLimitX96,
bytes calldata data
) external returns (int256 amount0, int256 amount1);
function flash(
address recipient,
uint256 amount0,
uint256 amount1,
bytes calldata data
) external;
function increaseObservationCardinalityNext(uint16 observationCardinalityNext) external;
}
文件 30 的 52:IUniswapV3PoolDerivedState.sol
pragma solidity >=0.5.0;
interface IUniswapV3PoolDerivedState {
function observe(uint32[] calldata secondsAgos)
external
view
returns (int56[] memory tickCumulatives, uint160[] memory secondsPerLiquidityCumulativeX128s);
function snapshotCumulativesInside(int24 tickLower, int24 tickUpper)
external
view
returns (
int56 tickCumulativeInside,
uint160 secondsPerLiquidityInsideX128,
uint32 secondsInside
);
}
文件 31 的 52:IUniswapV3PoolEvents.sol
pragma solidity >=0.5.0;
interface IUniswapV3PoolEvents {
event Initialize(uint160 sqrtPriceX96, int24 tick);
event Mint(
address sender,
address indexed owner,
int24 indexed tickLower,
int24 indexed tickUpper,
uint128 amount,
uint256 amount0,
uint256 amount1
);
event Collect(
address indexed owner,
address recipient,
int24 indexed tickLower,
int24 indexed tickUpper,
uint128 amount0,
uint128 amount1
);
event Burn(
address indexed owner,
int24 indexed tickLower,
int24 indexed tickUpper,
uint128 amount,
uint256 amount0,
uint256 amount1
);
event Swap(
address indexed sender,
address indexed recipient,
int256 amount0,
int256 amount1,
uint160 sqrtPriceX96,
uint128 liquidity,
int24 tick
);
event Flash(
address indexed sender,
address indexed recipient,
uint256 amount0,
uint256 amount1,
uint256 paid0,
uint256 paid1
);
event IncreaseObservationCardinalityNext(
uint16 observationCardinalityNextOld,
uint16 observationCardinalityNextNew
);
event SetFeeProtocol(uint8 feeProtocol0Old, uint8 feeProtocol1Old, uint8 feeProtocol0New, uint8 feeProtocol1New);
event CollectProtocol(address indexed sender, address indexed recipient, uint128 amount0, uint128 amount1);
}
文件 32 的 52:IUniswapV3PoolImmutables.sol
pragma solidity >=0.5.0;
interface IUniswapV3PoolImmutables {
function factory() external view returns (address);
function token0() external view returns (address);
function token1() external view returns (address);
function fee() external view returns (uint24);
function tickSpacing() external view returns (int24);
function maxLiquidityPerTick() external view returns (uint128);
}
文件 33 的 52:IUniswapV3PoolOwnerActions.sol
pragma solidity >=0.5.0;
interface IUniswapV3PoolOwnerActions {
function setFeeProtocol(uint8 feeProtocol0, uint8 feeProtocol1) external;
function collectProtocol(
address recipient,
uint128 amount0Requested,
uint128 amount1Requested
) external returns (uint128 amount0, uint128 amount1);
}
文件 34 的 52:IUniswapV3PoolState.sol
pragma solidity >=0.5.0;
interface IUniswapV3PoolState {
function slot0()
external
view
returns (
uint160 sqrtPriceX96,
int24 tick,
uint16 observationIndex,
uint16 observationCardinality,
uint16 observationCardinalityNext,
uint8 feeProtocol,
bool unlocked
);
function feeGrowthGlobal0X128() external view returns (uint256);
function feeGrowthGlobal1X128() external view returns (uint256);
function protocolFees() external view returns (uint128 token0, uint128 token1);
function liquidity() external view returns (uint128);
function ticks(int24 tick)
external
view
returns (
uint128 liquidityGross,
int128 liquidityNet,
uint256 feeGrowthOutside0X128,
uint256 feeGrowthOutside1X128,
int56 tickCumulativeOutside,
uint160 secondsPerLiquidityOutsideX128,
uint32 secondsOutside,
bool initialized
);
function tickBitmap(int16 wordPosition) external view returns (uint256);
function positions(bytes32 key)
external
view
returns (
uint128 _liquidity,
uint256 feeGrowthInside0LastX128,
uint256 feeGrowthInside1LastX128,
uint128 tokensOwed0,
uint128 tokensOwed1
);
function observations(uint256 index)
external
view
returns (
uint32 blockTimestamp,
int56 tickCumulative,
uint160 secondsPerLiquidityCumulativeX128,
bool initialized
);
}
文件 35 的 52:IWETH.sol
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
pragma solidity >=0.7.0 <0.9.0;
interface IWETH is IERC20 {
function deposit() external payable;
function withdraw(uint amount) external;
}
文件 36 的 52:LinkTokenInterface.sol
pragma solidity ^0.8.0;
interface LinkTokenInterface {
function allowance(address owner, address spender) external view returns (uint256 remaining);
function approve(address spender, uint256 value) external returns (bool success);
function balanceOf(address owner) external view returns (uint256 balance);
function decimals() external view returns (uint8 decimalPlaces);
function decreaseApproval(address spender, uint256 addedValue) external returns (bool success);
function increaseApproval(address spender, uint256 subtractedValue) external;
function name() external view returns (string memory tokenName);
function symbol() external view returns (string memory tokenSymbol);
function totalSupply() external view returns (uint256 totalTokensIssued);
function transfer(address to, uint256 value) external returns (bool success);
function transferAndCall(address to, uint256 value, bytes calldata data) external returns (bool success);
function transferFrom(address from, address to, uint256 value) external returns (bool success);
}
文件 37 的 52:Math.sol
pragma solidity ^0.8.20;
library Math {
error MathOverflowedMulDiv();
enum Rounding {
Floor,
Ceil,
Trunc,
Expand
}
function tryAdd(uint256 a, uint256 b) internal pure returns (bool, uint256) {
unchecked {
uint256 c = a + b;
if (c < a) return (false, 0);
return (true, c);
}
}
function trySub(uint256 a, uint256 b) internal pure returns (bool, uint256) {
unchecked {
if (b > a) return (false, 0);
return (true, a - b);
}
}
function tryMul(uint256 a, uint256 b) internal pure returns (bool, uint256) {
unchecked {
if (a == 0) return (true, 0);
uint256 c = a * b;
if (c / a != b) return (false, 0);
return (true, c);
}
}
function tryDiv(uint256 a, uint256 b) internal pure returns (bool, uint256) {
unchecked {
if (b == 0) return (false, 0);
return (true, a / b);
}
}
function tryMod(uint256 a, uint256 b) internal pure returns (bool, uint256) {
unchecked {
if (b == 0) return (false, 0);
return (true, a % b);
}
}
function max(uint256 a, uint256 b) internal pure returns (uint256) {
return a > b ? a : b;
}
function min(uint256 a, uint256 b) internal pure returns (uint256) {
return a < b ? a : b;
}
function average(uint256 a, uint256 b) internal pure returns (uint256) {
return (a & b) + (a ^ b) / 2;
}
function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {
if (b == 0) {
return a / b;
}
return a == 0 ? 0 : (a - 1) / b + 1;
}
function mulDiv(uint256 x, uint256 y, uint256 denominator) internal pure returns (uint256 result) {
unchecked {
uint256 prod0 = x * y;
uint256 prod1;
assembly {
let mm := mulmod(x, y, not(0))
prod1 := sub(sub(mm, prod0), lt(mm, prod0))
}
if (prod1 == 0) {
return prod0 / denominator;
}
if (denominator <= prod1) {
revert MathOverflowedMulDiv();
}
uint256 remainder;
assembly {
remainder := mulmod(x, y, denominator)
prod1 := sub(prod1, gt(remainder, prod0))
prod0 := sub(prod0, remainder)
}
uint256 twos = denominator & (0 - denominator);
assembly {
denominator := div(denominator, twos)
prod0 := div(prod0, twos)
twos := add(div(sub(0, twos), twos), 1)
}
prod0 |= prod1 * twos;
uint256 inverse = (3 * denominator) ^ 2;
inverse *= 2 - denominator * inverse;
inverse *= 2 - denominator * inverse;
inverse *= 2 - denominator * inverse;
inverse *= 2 - denominator * inverse;
inverse *= 2 - denominator * inverse;
inverse *= 2 - denominator * inverse;
result = prod0 * inverse;
return result;
}
}
function mulDiv(uint256 x, uint256 y, uint256 denominator, Rounding rounding) internal pure returns (uint256) {
uint256 result = mulDiv(x, y, denominator);
if (unsignedRoundsUp(rounding) && mulmod(x, y, denominator) > 0) {
result += 1;
}
return result;
}
function sqrt(uint256 a) internal pure returns (uint256) {
if (a == 0) {
return 0;
}
uint256 result = 1 << (log2(a) >> 1);
unchecked {
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
return min(result, a / result);
}
}
function sqrt(uint256 a, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = sqrt(a);
return result + (unsignedRoundsUp(rounding) && result * result < a ? 1 : 0);
}
}
function log2(uint256 value) internal pure returns (uint256) {
uint256 result = 0;
unchecked {
if (value >> 128 > 0) {
value >>= 128;
result += 128;
}
if (value >> 64 > 0) {
value >>= 64;
result += 64;
}
if (value >> 32 > 0) {
value >>= 32;
result += 32;
}
if (value >> 16 > 0) {
value >>= 16;
result += 16;
}
if (value >> 8 > 0) {
value >>= 8;
result += 8;
}
if (value >> 4 > 0) {
value >>= 4;
result += 4;
}
if (value >> 2 > 0) {
value >>= 2;
result += 2;
}
if (value >> 1 > 0) {
result += 1;
}
}
return result;
}
function log2(uint256 value, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = log2(value);
return result + (unsignedRoundsUp(rounding) && 1 << result < value ? 1 : 0);
}
}
function log10(uint256 value) internal pure returns (uint256) {
uint256 result = 0;
unchecked {
if (value >= 10 ** 64) {
value /= 10 ** 64;
result += 64;
}
if (value >= 10 ** 32) {
value /= 10 ** 32;
result += 32;
}
if (value >= 10 ** 16) {
value /= 10 ** 16;
result += 16;
}
if (value >= 10 ** 8) {
value /= 10 ** 8;
result += 8;
}
if (value >= 10 ** 4) {
value /= 10 ** 4;
result += 4;
}
if (value >= 10 ** 2) {
value /= 10 ** 2;
result += 2;
}
if (value >= 10 ** 1) {
result += 1;
}
}
return result;
}
function log10(uint256 value, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = log10(value);
return result + (unsignedRoundsUp(rounding) && 10 ** result < value ? 1 : 0);
}
}
function log256(uint256 value) internal pure returns (uint256) {
uint256 result = 0;
unchecked {
if (value >> 128 > 0) {
value >>= 128;
result += 16;
}
if (value >> 64 > 0) {
value >>= 64;
result += 8;
}
if (value >> 32 > 0) {
value >>= 32;
result += 4;
}
if (value >> 16 > 0) {
value >>= 16;
result += 2;
}
if (value >> 8 > 0) {
result += 1;
}
}
return result;
}
function log256(uint256 value, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = log256(value);
return result + (unsignedRoundsUp(rounding) && 1 << (result << 3) < value ? 1 : 0);
}
}
function unsignedRoundsUp(Rounding rounding) internal pure returns (bool) {
return uint8(rounding) % 2 == 1;
}
}
文件 38 的 52:Ownable.sol
pragma solidity ^0.8.20;
import {Context} from "../utils/Context.sol";
abstract contract Ownable is Context {
address private _owner;
error OwnableUnauthorizedAccount(address account);
error OwnableInvalidOwner(address owner);
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
constructor(address initialOwner) {
if (initialOwner == address(0)) {
revert OwnableInvalidOwner(address(0));
}
_transferOwnership(initialOwner);
}
modifier onlyOwner() {
_checkOwner();
_;
}
function owner() public view virtual returns (address) {
return _owner;
}
function _checkOwner() internal view virtual {
if (owner() != _msgSender()) {
revert OwnableUnauthorizedAccount(_msgSender());
}
}
function renounceOwnership() public virtual onlyOwner {
_transferOwnership(address(0));
}
function transferOwnership(address newOwner) public virtual onlyOwner {
if (newOwner == address(0)) {
revert OwnableInvalidOwner(address(0));
}
_transferOwnership(newOwner);
}
function _transferOwnership(address newOwner) internal virtual {
address oldOwner = _owner;
_owner = newOwner;
emit OwnershipTransferred(oldOwner, newOwner);
}
}
文件 39 的 52:Pausable.sol
pragma solidity ^0.8.20;
import {Context} from "../utils/Context.sol";
abstract contract Pausable is Context {
bool private _paused;
event Paused(address account);
event Unpaused(address account);
error EnforcedPause();
error ExpectedPause();
constructor() {
_paused = false;
}
modifier whenNotPaused() {
_requireNotPaused();
_;
}
modifier whenPaused() {
_requirePaused();
_;
}
function paused() public view virtual returns (bool) {
return _paused;
}
function _requireNotPaused() internal view virtual {
if (paused()) {
revert EnforcedPause();
}
}
function _requirePaused() internal view virtual {
if (!paused()) {
revert ExpectedPause();
}
}
function _pause() internal virtual whenNotPaused {
_paused = true;
emit Paused(_msgSender());
}
function _unpause() internal virtual whenPaused {
_paused = false;
emit Unpaused(_msgSender());
}
}
文件 40 的 52:ReentrancyGuard.sol
pragma solidity ^0.8.20;
abstract contract ReentrancyGuard {
uint256 private constant NOT_ENTERED = 1;
uint256 private constant ENTERED = 2;
uint256 private _status;
error ReentrancyGuardReentrantCall();
constructor() {
_status = NOT_ENTERED;
}
modifier nonReentrant() {
_nonReentrantBefore();
_;
_nonReentrantAfter();
}
function _nonReentrantBefore() private {
if (_status == ENTERED) {
revert ReentrancyGuardReentrantCall();
}
_status = ENTERED;
}
function _nonReentrantAfter() private {
_status = NOT_ENTERED;
}
function _reentrancyGuardEntered() internal view returns (bool) {
return _status == ENTERED;
}
}
文件 41 的 52:RenderHelper.sol
pragma solidity >=0.7.0 <0.9.0;
import "@openzeppelin/contracts/utils/Base64.sol";
import "@openzeppelin/contracts/utils/Strings.sol";
import "./SvgHelper.sol";
library RenderHelper {
function getJson(
uint256 tokenId,
uint8[7] memory tetra,
string[] memory colorNames,
string[] memory colorHexCodes
) public pure returns (string memory) {
bytes memory dataURI = abi.encodePacked(
"{",
_prop("description", "TetraSpektra"),
_prop("name", string.concat("Tetra #", Strings.toString(tokenId))),
'"attributes": [',
string(_getAttributes(tetra, colorNames)),
"],",
_prop(
"image",
_getBase64Image(_render(tetra, colorHexCodes)),
false
),
"}"
);
return
string(
abi.encodePacked(
"data:application/json;base64,",
Base64.encode(dataURI)
)
);
}
function _getTrait(
string memory traitType,
string memory traitValue
) private pure returns (string memory) {
return _getTrait(traitType, traitValue, true);
}
function _getTrait(
string memory traitType,
string memory traitValue,
bool addComma
) private pure returns (string memory) {
return
string.concat(
"{",
_prop("trait_type", traitType),
_prop("value", traitValue, false),
"}",
addComma ? "," : ""
);
}
function _prop(
string memory name,
string memory value
) private pure returns (string memory) {
return _prop(name, value, false, true);
}
function _prop(
string memory name,
string memory value,
bool addComma
) private pure returns (string memory) {
return _prop(name, value, false, addComma);
}
function _prop(
string memory name,
string memory value,
bool isNumber,
bool addComma
) private pure returns (string memory) {
return
string.concat(
' "',
name,
'": ',
isNumber ? "" : '"',
value,
isNumber ? "" : '"',
addComma ? ", " : " "
);
}
function _getBase64Image(
bytes memory data
) private pure returns (string memory) {
return
string(
abi.encodePacked(
"data:image/svg+xml;base64,",
Base64.encode(data)
)
);
}
function _getAttributes(
uint8[7] memory tetra,
string[] memory colorNames
) private pure returns (bytes memory) {
return
abi.encodePacked(
_getTrait("Inner Kore #1", colorNames[tetra[0]]),
_getTrait("Inner Kore #2", colorNames[tetra[1]]),
_getTrait("Inner Kore #3", colorNames[tetra[2]]),
_getTrait("Inner Kore #4", colorNames[tetra[3]]),
_getTrait("Outer Kore", colorNames[tetra[4]]),
_getTrait("Mantle", colorNames[tetra[5]]),
_getTrait("Krust", colorNames[tetra[6]], false)
);
}
function _renderUnexcavated() private pure returns (bytes memory) {
return
abi.encodePacked(
"<svg xmlns='http://www.w3.org/2000/svg' preserveAspectRatio='xMinYMin meet' viewBox='0 0 15 15' width='100%' height='auto' shape-rendering='crispEdges' >",
"<style type='text/css'> @media screen and (-webkit-min-device-pixel-ratio: 0) and (min-resolution: 0.001dpcm) {.static { background: repeating-radial-gradient(#000 0 0.0001%,#fff 0 0.0002%) 50% 0/2500px 2500px,repeating-conic-gradient(#000 0 0.0001%,#fff 0 0.0002%) 60% 60%/2500px 2500px; background-blend-mode: difference; animation: st .2s infinite alternate; mix-blend-mode: multiply;} @keyframes st{ 100% {background-position: 20% 0, 20% 50%} } @media screen and (-webkit-min-device-pixel-ratio: 0) { _::-webkit-full-page-media, .static { background:none; }}</style>",
"<filter id='g'><feGaussianBlur stdDeviation='.14' result='cb' /><feMerge><feMergeNode in='cb' /><feMergeNode in='SourceGraphic' /></feMerge></filter><rect x='0' y='0' height='100%' width='100%' fill='black' /><g transform='translate(.5 .5)'><rect x='6.125' y='6.125' height='.75' width='.75' stroke='#FFFFFF' stroke-width='.01'><animate attributeName='opacity' values='1;0;1' dur='4s' repeatCount='indefinite' /></rect><rect x='7.125' y='6.125' height='.75' width='.75' stroke='#FFFFFF' stroke-width='.01'><animate attributeName='opacity' values='1;0;1' dur='2s' repeatCount='indefinite' /></rect><rect x='6.125' y='7.125' height='.75' width='.75' stroke='#FFFFFF' stroke-width='.01'><animate attributeName='opacity' values='1;0;1' dur='3s' repeatCount='indefinite' /></rect><rect x='7.125' y='7.125' height='.75' width='.75' stroke='#FFFFFF' stroke-width='.01'><animate attributeName='opacity' values='1;0;1' dur='1s' repeatCount='indefinite' /></rect></g><g transform='translate(.5 .5)'><g><circle cx='7' cy='7' r='2' fill='none' opacity='1' stroke='#FFFFFF' stroke-width='0.1' filter='' ><animate attributeName='r' values='2.5;6.9;' dur='6s' repeatCount='indefinite' /><animate attributeName='stroke-width' values='0;.1;' dur='6s' repeatCount='indefinite' /><animate attributeName='opacity' values='0;1;0;' dur='6s' repeatCount='indefinite' /></circle></g></g>",
"<foreignObject class='static' x='0' y='0' width='100' height='100'><div class='logoGradient' xmlns='http://www.w3.org/1999/xhtml'></div></foreignObject></svg>"
);
}
function _renderKrust(
uint8[7] memory tetra,
string[] memory colorHexCodes
) private pure returns (string memory) {
return
string.concat(
SvgHelper.rectangle(
"0",
"0",
"6",
".1",
colorHexCodes[tetra[6]]
),
SvgHelper.rectangle(
"0",
"0",
".1",
"7",
colorHexCodes[tetra[6]]
),
SvgHelper.rectangle(
"0",
"7",
"7",
".1",
colorHexCodes[tetra[6]]
),
SvgHelper.rectangle(
"0",
"13.9",
".1",
"6",
colorHexCodes[tetra[6]]
),
SvgHelper.rectangle(
"7",
"13.9",
".1",
"7",
colorHexCodes[tetra[6]]
),
SvgHelper.rectangle(
"13.9",
"8",
"6",
".1",
colorHexCodes[tetra[6]]
),
SvgHelper.rectangle(
"13.9",
"0",
"7",
".1",
colorHexCodes[tetra[6]]
),
SvgHelper.rectangle(
"8",
"0",
".1",
"6",
colorHexCodes[tetra[6]]
)
);
}
function _renderMantle(
uint8[7] memory tetra,
string[] memory colorHexCodes
) private pure returns (string memory) {
return
string.concat(
SvgHelper.rectangle(
"2",
"2",
".1",
"7",
colorHexCodes[tetra[5]]
),
SvgHelper.rectangle(
"2",
"2",
"1",
".1",
colorHexCodes[tetra[5]]
),
SvgHelper.rectangle(
"2",
"5",
"7",
".1",
colorHexCodes[tetra[5]]
),
SvgHelper.rectangle(
"2",
"11.9",
".1",
"1",
colorHexCodes[tetra[5]]
),
SvgHelper.rectangle(
"5",
"11.9",
".1",
"7",
colorHexCodes[tetra[5]]
),
SvgHelper.rectangle(
"11.9",
"10.9",
"1",
".1",
colorHexCodes[tetra[5]]
),
SvgHelper.rectangle(
"11.9",
"2",
"7",
".1",
colorHexCodes[tetra[5]]
),
SvgHelper.rectangle(
"10.9",
"2",
".1",
"1",
colorHexCodes[tetra[5]]
)
);
}
function _renderNucleus(
uint8[7] memory tetra,
string[] memory colorHexCodes
) private pure returns (string memory) {
return
string.concat(
SvgHelper.square(
"6.125",
"6.125",
".75",
colorHexCodes[tetra[0]],
SvgHelper.animate("opacity", "1;0.2;1;", "3s")
),
SvgHelper.square(
"7.125",
"6.125",
".75",
colorHexCodes[tetra[1]],
SvgHelper.animate("opacity", "1;0.2;1;", "2s")
),
SvgHelper.square(
"6.125",
"7.125",
".75",
colorHexCodes[tetra[2]],
SvgHelper.animate("opacity", "1;0.2;1;", "4s")
),
SvgHelper.square(
"7.125",
"7.125",
".75",
colorHexCodes[tetra[3]],
SvgHelper.animate("opacity", "1;0.2;1;", "5s")
)
);
}
function _render(
uint8[7] memory tetra,
string[] memory colorHexCodes
) private pure returns (bytes memory) {
if (tetra[0] == 0) return _renderUnexcavated();
string memory str = string.concat(
"<g filter='url(#g)'>",
_renderKrust(tetra, colorHexCodes),
_renderMantle(tetra, colorHexCodes),
SvgHelper.circle(
"7",
"7",
"2.5",
"1",
"none",
colorHexCodes[tetra[4]],
"0.1"
),
"</g>",
"<g>",
SvgHelper.circle(
"7",
"7",
"2",
"1",
"none",
colorHexCodes[tetra[4]],
"0.1",
SvgHelper.getStandardAnimationGroup("8s")
),
SvgHelper.circle(
"7",
"7",
"2",
"1",
"none",
colorHexCodes[tetra[6]],
"0.1",
SvgHelper.getStandardAnimationGroup("6s")
),
SvgHelper.circle(
"7",
"7",
"2",
"1",
"none",
colorHexCodes[tetra[5]],
"0.1",
SvgHelper.getStandardAnimationGroup("4s")
),
SvgHelper.circle(
"7",
"7",
"2",
"1",
"none",
colorHexCodes[tetra[4]],
"0.1",
SvgHelper.getStandardAnimationGroup("2s")
),
"</g>",
_renderNucleus(tetra, colorHexCodes)
);
return
abi.encodePacked(
"<svg xmlns='http://www.w3.org/2000/svg' preserveAspectRatio='xMinYMin meet' viewBox='0 0 15 15' width='100%' height='auto' shape-rendering='crispEdges'><filter id='g'><feGaussianBlur stdDeviation='.14' result='cb' /><feMerge><feMergeNode in='cb'/><feMergeNode in='SourceGraphic' /></feMerge></filter>",
"<style type='text/css'> @media screen and (-webkit-min-device-pixel-ratio: 0) and (min-resolution: 0.001dpcm) {.static { background: repeating-radial-gradient(#000 0 0.0001%,#fff 0 0.0002%) 50% 0/2500px 2500px,repeating-conic-gradient(#000 0 0.0001%,#fff 0 0.0002%) 60% 60%/2500px 2500px; background-blend-mode: difference; animation: st .2s infinite alternate; mix-blend-mode: multiply;} @keyframes st{ 100% {background-position: 20% 0, 20% 50%} } @media screen and (-webkit-min-device-pixel-ratio: 0) { _::-webkit-full-page-media, .static { background:none; }}</style>",
SvgHelper.rectangle("0", "0", "100%", "100%", colorHexCodes[0]),
"<g transform='translate(.5 .5)'>",
str,
"</g><foreignObject class='static' x='0' y='0' width='100' height='100'><div class='logoGradient' xmlns='http://www.w3.org/1999/xhtml'></div></foreignObject></svg>"
);
}
}
文件 42 的 52:SignedMath.sol
pragma solidity ^0.8.20;
library SignedMath {
function max(int256 a, int256 b) internal pure returns (int256) {
return a > b ? a : b;
}
function min(int256 a, int256 b) internal pure returns (int256) {
return a < b ? a : b;
}
function average(int256 a, int256 b) internal pure returns (int256) {
int256 x = (a & b) + ((a ^ b) >> 1);
return x + (int256(uint256(x) >> 255) & (a ^ b));
}
function abs(int256 n) internal pure returns (uint256) {
unchecked {
return uint256(n >= 0 ? n : -n);
}
}
}
文件 43 的 52:Strings.sol
pragma solidity ^0.8.20;
import {Math} from "./math/Math.sol";
import {SignedMath} from "./math/SignedMath.sol";
library Strings {
bytes16 private constant HEX_DIGITS = "0123456789abcdef";
uint8 private constant ADDRESS_LENGTH = 20;
error StringsInsufficientHexLength(uint256 value, uint256 length);
function toString(uint256 value) internal pure returns (string memory) {
unchecked {
uint256 length = Math.log10(value) + 1;
string memory buffer = new string(length);
uint256 ptr;
assembly {
ptr := add(buffer, add(32, length))
}
while (true) {
ptr--;
assembly {
mstore8(ptr, byte(mod(value, 10), HEX_DIGITS))
}
value /= 10;
if (value == 0) break;
}
return buffer;
}
}
function toStringSigned(int256 value) internal pure returns (string memory) {
return string.concat(value < 0 ? "-" : "", toString(SignedMath.abs(value)));
}
function toHexString(uint256 value) internal pure returns (string memory) {
unchecked {
return toHexString(value, Math.log256(value) + 1);
}
}
function toHexString(uint256 value, uint256 length) internal pure returns (string memory) {
uint256 localValue = value;
bytes memory buffer = new bytes(2 * length + 2);
buffer[0] = "0";
buffer[1] = "x";
for (uint256 i = 2 * length + 1; i > 1; --i) {
buffer[i] = HEX_DIGITS[localValue & 0xf];
localValue >>= 4;
}
if (localValue != 0) {
revert StringsInsufficientHexLength(value, length);
}
return string(buffer);
}
function toHexString(address addr) internal pure returns (string memory) {
return toHexString(uint256(uint160(addr)), ADDRESS_LENGTH);
}
function equal(string memory a, string memory b) internal pure returns (bool) {
return bytes(a).length == bytes(b).length && keccak256(bytes(a)) == keccak256(bytes(b));
}
}
文件 44 的 52:SvgHelper.sol
pragma solidity >=0.7.0 <0.9.0;
import "@openzeppelin/contracts/utils/Strings.sol";
library SvgHelper {
function circle(
string memory x,
string memory y,
string memory r,
string memory color
) public pure returns (string memory) {
return circle(x, y, r, "1", color, "", "0");
}
function circle(
uint256 x,
uint256 y,
uint256 r,
string memory opacity,
string memory color,
string memory strokeColor,
string memory strokeWidth
) public pure returns (string memory) {
return
circle(
Strings.toString(x),
Strings.toString(y),
Strings.toString(r),
opacity,
color,
strokeColor,
strokeWidth,
""
);
}
function circle(
string memory x,
string memory y,
string memory r,
string memory opacity,
string memory color,
string memory strokeColor,
string memory strokeWidth
) public pure returns (string memory) {
return circle(x, y, r, opacity, color, strokeColor, strokeWidth, "");
}
function circle(
string memory x,
string memory y,
string memory r,
string memory opacity,
string memory color,
string memory strokeColor,
string memory strokeWidth,
string memory animation
) public pure returns (string memory) {
return
circle(
x,
y,
r,
opacity,
color,
strokeColor,
strokeWidth,
animation,
""
);
}
function circle(
string memory x,
string memory y,
string memory r,
string memory opacity,
string memory color,
string memory strokeColor,
string memory strokeWidth,
string memory animation,
string memory filter
) public pure returns (string memory) {
return
string.concat(
"<circle cx='",
x,
"' cy='",
y,
"' r='",
r,
"' fill='",
color,
"' opacity='",
opacity,
"' stroke='",
keccak256(abi.encodePacked(strokeColor)) != ""
? strokeColor
: "",
"' stroke-width='",
keccak256(abi.encodePacked(strokeColor)) != ""
? strokeWidth
: "",
"'",
keccak256(abi.encodePacked(filter)) == ""
? ""
: string.concat(" filter='", filter, "'"),
keccak256(abi.encodePacked((animation))) == ""
? "/>"
: string.concat(" >", animation, "</circle>")
);
}
function triangle(
string memory x0,
string memory y0,
string memory x1,
string memory y1,
string memory x2,
string memory y2,
string memory color
) public pure returns (string memory) {
return
string.concat(
"<polygon points='",
x0,
" ",
y0,
", ",
x1,
" ",
y1,
", ",
x2,
" ",
y2,
"' fill='",
color,
"'/>"
);
}
function square(
string memory x,
string memory y,
string memory size,
string memory color
) public pure returns (string memory) {
return square(x, y, size, color, "");
}
function square(
string memory x,
string memory y,
string memory size,
string memory color,
string memory animation
) public pure returns (string memory) {
return rectangle(x, y, size, size, color, "", "", animation);
}
function animate(
string memory attributeName,
string memory values,
string memory duration
) public pure returns (string memory) {
return
string.concat(
"<animate attributeName='",
attributeName,
"' values='",
values,
"' dur='",
duration,
"' repeatCount='",
"indefinite",
"' />"
);
}
function rectangle(
string memory x,
string memory y,
string memory h,
string memory w,
string memory color
) public pure returns (string memory) {
return rectangle(x, y, h, w, color, "", "", "");
}
function rectangle(
string memory x,
string memory y,
string memory h,
string memory w,
string memory color,
string memory stroke,
string memory strokeWidth,
string memory animation
) public pure returns (string memory) {
return
string.concat(
"<rect x='",
x,
"' y='",
y,
"' height='",
h,
"' width='",
w,
"' fill='",
color,
"' stroke='",
stroke,
"' stroke-width='",
strokeWidth,
"'",
keccak256(abi.encodePacked((animation))) == ""
? "/>"
: string.concat(" >", animation, "</rect>")
);
}
function getStandardAnimationGroup(
string memory s
) public pure returns (string memory) {
return
string.concat(
animate("r", "2.5;6.9;", s),
animate("stroke-width", "0;.1;", s),
animate("opacity", "1;0;", s)
);
}
}
文件 45 的 52:TetraSpectra.sol
pragma solidity >=0.7.0 <0.9.0;
import "@openzeppelin/contracts/utils/Strings.sol";
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/token/ERC721/ERC721.sol";
import "@openzeppelin/contracts/token/ERC721/IERC721Receiver.sol";
import "@openzeppelin/contracts/utils/ReentrancyGuard.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/utils/Pausable.sol";
import {IUniswapV3Pool} from "@uniswap/v3-core/contracts/interfaces/IUniswapV3Pool.sol";
import {Excavator} from "./Excavator.sol";
import "./interfaces/ISwapRouter.sol";
import "./interfaces/IClaimsManager.sol";
import "./interfaces/IRandomizer.sol";
import "./interfaces/IPriceOracle.sol";
import "./interfaces/IBountyVault.sol";
import "./interfaces/IWETH.sol";
import "./interfaces/IProspektorFundManager.sol";
import "./utils/TransferHelper.sol";
import "./utils/RenderHelper.sol";
import "./utils/ExecutorManager.sol";
enum CreatorBountyTier {
None,
Low,
Medium,
High,
Ultra
}
enum BountyTier {
Silver,
Gold,
Platinum
}
enum DiscoveryType {
Prospekted,
Reforged,
Refined
}
struct BountySwapConfig {
uint256 tokenCount;
uint256 innerCount;
uint256 outerCount;
}
struct TetraSpektraConfig {
uint256 maxSupply;
uint256 prospektPrice;
uint256 refreshPrice;
uint256 flashBountyValue;
uint8 excavatorFundingPercentage;
uint16 maxSilver2Swaps;
uint8 silver2UnlockCadence;
uint8 silver2UnlockAmount;
string[] colorHexCodes;
string[] colorNames;
}
struct AddressConfig {
address randomizerAddress;
address claimsManagerAddress;
address priceOracleAddress;
address bountyVaultAddress;
address uniswapRouterAddress;
}
struct ColorConfig {
string[] colorHexCodes;
string[] colorNames;
uint8 maxColors;
}
struct ExcavatorConfig {
address vrfCoordinatorAddress;
bytes32 vrfKeyHash;
address linkTokenAddress;
}
struct TokenConfig {
address wethTokenAddress;
address porkTokenAddress;
}
contract TetraSpektra is
ERC721,
IERC721Receiver,
ExecutorManager,
Pausable,
ReentrancyGuard
{
error InsufficientPayment();
error NotSoldOut();
error InvalidTokenOwner();
error InvalidToken();
error NotMatched();
error InvalidTokenCompare();
error TooManySilver2Swaps();
error InvalidTokenCount();
error ExcavationInProgress();
error ExcavationGroupNotReady();
event Swapped(
address indexed prospektor,
uint256 indexed tier,
uint256 bountyValue,
uint256 tokenId_0,
uint256 tokenId_1,
uint256 tokenId_2,
uint256 tokenId_3
);
event Discovery(
address indexed prospektor,
uint256 indexed tokenId,
DiscoveryType discoveryType
);
event Excavated(
address indexed prospektor,
uint256 indexed tokenId,
bytes32 silverTetraKey,
bytes32 goldTetraKey,
bytes32 platinumTetraKey
);
event AddedLiquidityForBounty(
address indexed prospektor,
uint256 interactionCount,
uint256 porkAmount,
uint256 wethAmount,
uint256 canAddLiquidityAfter
);
event CollectedFeesForBounty(
address indexed prospektor,
uint256 interactionCount,
uint256 porkAmount,
uint256 canCollectFeesAfter
);
event ExcavationComplete(
uint256 indexed excavationId,
uint256 indexed value
);
event ExcavationContracted(
uint256 excavationId,
uint256 tokenId_0,
uint256 tokenId_1,
uint256 tokenId_2,
uint256 tokenId_3,
uint256 tokenId_4
);
event ExcavatedFlashBounty(
bytes32 silverTetraKey,
bytes32 goldTetraKey,
bytes32 platinumTetraKey
);
event ClaimedFlashBounty(
address indexed prospektor,
uint256 indexed tokenId,
bytes32 tetraKey,
uint256 bountyValue
);
event Claimed(
address indexed prospektor,
uint256 claimsSessionId,
uint256 claimedTokenId,
uint256 tokenId
);
IProspektorFundManager public prospektorFundManager;
IClaimsManager public claimsManager;
IRandomizer public randomizer;
IPriceOracle public priceOracle;
IBountyVault public bountyVault;
Excavator public excavator;
IUniswapV3Pool public uniswapPool;
ISwapRouter public uniswapRouter;
address public immutable WETH;
address public immutable PORK;
uint256 public immutable maxSupply;
uint256 public constant MIN_WETH_AMOUNT = 1_000_000_000_000_000;
uint256 public constant MIN_PORK_AMOUNT = 100_000_000;
bytes32 public immutable BASE_TETRA_KEY = getTetraKey(0, BountyTier.Silver);
uint24 public constant PORK_WETH_POOL_FEE = 10_000;
uint24 public constant DEFAULT_POOL_FEE = 3_000;
uint8 public constant MAX_TO_PROSPEKT = 5;
uint8 private constant CADENCE = 5;
uint8 public constant SWAP_PERCENTAGE_INCREMENT = 8;
uint8 public fundingPercentage;
uint16 public maxSilver2Swaps;
uint8 public silver2UnlockCadence;
uint8 public silver2UnlockAmount;
uint256 public silver2Swaps = 0;
uint256 public canCollectFeesAfter = 20;
uint256 public feeCollectorBounty = 20_000_000_000_000_000;
uint256 public feeCollectorCadence = 20;
uint256 public canAddLiquidityAfter = 10;
uint256 public liquidityAdderBounty = 25_000_000_000_000_000;
uint256 public liquidityAdderCadence = 12;
uint256 private _prospektCount = 1;
uint256 public interactionCount = 0;
uint256 public swapPercentage = 55;
uint256 public prospektPrice;
uint256 public refreshPrice;
uint256 public refreshCount = 0;
bool public isInitialized;
uint256 public flashBountyValue;
bool public flashBountyEnabled;
mapping(uint256 => uint256) private _tetraSeeds;
mapping(uint256 => uint256) private _excavationResults;
mapping(uint256 => uint256) public tetraExcavationIds;
mapping(uint256 => uint256) public excavationTracker;
uint256 public excavationGroup;
uint256[3][3] private _bounties;
ColorConfig public colorConfig;
uint24[5][2] private _priceHistory;
uint8 private _priceHistoryIndex;
constructor(
AddressConfig memory addressConfig,
TokenConfig memory tokenConfig,
ExcavatorConfig memory excavatorConfig,
TetraSpektraConfig memory tetraSpektraConfig,
uint256[3][3] memory bounties
) payable ExecutorManager(msg.sender) ERC721("TetraSpektra", "TETRA") {
require(
tetraSpektraConfig.colorHexCodes.length ==
tetraSpektraConfig.colorNames.length,
"0"
);
claimsManager = IClaimsManager(addressConfig.claimsManagerAddress);
randomizer = IRandomizer(addressConfig.randomizerAddress);
priceOracle = IPriceOracle(addressConfig.priceOracleAddress);
bountyVault = IBountyVault(addressConfig.bountyVaultAddress);
uniswapRouter = ISwapRouter(addressConfig.uniswapRouterAddress);
WETH = tokenConfig.wethTokenAddress;
PORK = tokenConfig.porkTokenAddress;
maxSupply = tetraSpektraConfig.maxSupply;
prospektPrice = tetraSpektraConfig.prospektPrice;
refreshPrice = tetraSpektraConfig.refreshPrice;
flashBountyValue = tetraSpektraConfig.flashBountyValue == 0
? _percentage(tetraSpektraConfig.prospektPrice, 70) * 10
: tetraSpektraConfig.flashBountyValue;
fundingPercentage = tetraSpektraConfig.excavatorFundingPercentage;
maxSilver2Swaps = tetraSpektraConfig.maxSilver2Swaps;
silver2UnlockCadence = tetraSpektraConfig.silver2UnlockCadence;
silver2UnlockAmount = tetraSpektraConfig.silver2UnlockAmount;
colorConfig = ColorConfig({
colorHexCodes: tetraSpektraConfig.colorHexCodes,
colorNames: tetraSpektraConfig.colorNames,
maxColors: uint8(tetraSpektraConfig.colorNames.length)
});
excavator = new Excavator(
msg.sender,
address(this),
address(uniswapRouter),
excavatorConfig.vrfCoordinatorAddress,
excavatorConfig.vrfKeyHash,
WETH,
excavatorConfig.linkTokenAddress
);
TransferHelper.safeApprove(WETH, address(excavator), type(uint256).max);
IWETH(WETH).deposit{value: _percentage(msg.value, 20)}();
excavator.initialize{value: _percentage(msg.value, 80)}();
_bounties = bounties;
}
function _unsafeIncrement(uint256 x) private pure returns (uint256) {
unchecked {
return x + 1;
}
}
modifier mustOwnToken(address addr, uint256 tokenId) {
if (ownerOf(tokenId) != addr) revert InvalidTokenOwner();
_;
}
modifier checkPayment(uint256 value) {
if (msg.value < value) revert InsufficientPayment();
_;
}
modifier mustBeSoldOut() {
if (totalSupply() != maxSupply) revert NotSoldOut();
_;
}
function getBounties() public view returns (uint256[3][3] memory) {
return _bounties;
}
function totalSupply() public view returns (uint256) {
return _prospektCount - 1;
}
function getTetrasRemaining() public view returns (uint256) {
return maxSupply - totalSupply();
}
function claimVaultBounty(
uint256 tokenId
) public mustOwnToken(msg.sender, tokenId) {
bountyVault.claimBounty(msg.sender, tokenId);
}
function getTotalBounty(
uint256 tierIndex,
uint256 countIndex
) private view returns (uint256) {
return
address(this).balance >= _bounties[tierIndex][countIndex]
? _bounties[tierIndex][countIndex]
: address(this).balance;
}
function isFlashBountyEnabled() public view returns (bool) {
return flashBountyEnabled && isExcavated(0);
}
function isExcavated(uint256 tokenId) public view returns (bool) {
return getTetraKey(tokenId, BountyTier.Silver) != BASE_TETRA_KEY;
}
function _percentage(
uint256 value,
uint256 percentage
) private pure returns (uint256) {
return (value * percentage) / 100;
}
function _fundExcavatorWhenReady() internal {
if (
fundingPercentage > 0 &&
interactionCount > 0 &&
interactionCount % CADENCE == 0
) {
uint256 fundAmount = (_percentage(
excavator.getLinkPrice(),
fundingPercentage
) * CADENCE);
require(
IERC20(WETH).balanceOf(address(this)) > fundAmount,
"INVALID_WETH"
);
excavator.fundWithWeth(fundAmount);
}
}
function updateExcavatorFundingPercentage(
uint8 percentage
) public onlyExecutor {
require(percentage >= 0 && percentage <= 100, "0");
fundingPercentage = percentage;
}
function prospekt(
uint256 count
)
external
payable
whenNotPaused
nonReentrant
checkPayment(prospektPrice * count)
returns (uint256[MAX_TO_PROSPEKT] memory tokenIds)
{
require(count > 0 && count <= MAX_TO_PROSPEKT, "1");
require(totalSupply() + count <= maxSupply, "2");
for (uint256 i = 0; i < count; i = _unsafeIncrement(i))
tokenIds[i] = _prospekt(msg.sender);
IWETH(WETH).deposit{value: msg.value}();
_fundExcavatorWhenReady();
uint256 wethToSwap = IERC20(WETH).balanceOf(address(this));
if (wethToSwap / liquidityAdderBounty == 1)
wethToSwap -= liquidityAdderBounty;
if (wethToSwap > 0)
_swapWethForToken(
PORK,
_percentage(wethToSwap, swapPercentage),
address(this)
);
return tokenIds;
}
function refine(
uint256[] memory tokenIds
)
external
payable
whenNotPaused
nonReentrant
checkPayment(refreshPrice * tokenIds.length)
mustBeSoldOut
{
IWETH(WETH).deposit{value: msg.value}();
for (uint256 i = 0; i < tokenIds.length; i = _unsafeIncrement(i)) {
if (ownerOf(tokenIds[i]) != address(this))
revert InvalidTokenOwner();
_transfer(address(this), msg.sender, tokenIds[i]);
_processRefresh(DiscoveryType.Refined, tokenIds[i]);
}
uint256 wethToSwap = IERC20(WETH).balanceOf(address(this));
if (wethToSwap / liquidityAdderBounty == 1)
wethToSwap -= liquidityAdderBounty;
if (wethToSwap > 0)
_swapWethForToken(
PORK,
_percentage(wethToSwap, swapPercentage),
address(this)
);
}
function reforge(
uint256[] memory tokenIds
)
external
payable
whenNotPaused
nonReentrant
checkPayment(refreshPrice * tokenIds.length)
mustBeSoldOut
{
IWETH(WETH).deposit{value: msg.value}();
for (uint256 i = 0; i < tokenIds.length; i = _unsafeIncrement(i)) {
if (ownerOf(tokenIds[i]) != msg.sender) revert InvalidTokenOwner();
_resetTetra(tokenIds[i]);
_processRefresh(DiscoveryType.Reforged, tokenIds[i]);
}
uint256 wethToSwap = IERC20(WETH).balanceOf(address(this));
if (wethToSwap / liquidityAdderBounty == 1)
wethToSwap -= liquidityAdderBounty;
if (wethToSwap > 0)
_swapWethForToken(
PORK,
_percentage(wethToSwap, swapPercentage),
address(this)
);
}
function swapForBounty(
uint256[4] memory tokenIds,
CreatorBountyTier creatorBountyTier
) external whenNotPaused nonReentrant {
BountySwapConfig memory bountySwapConfig;
bountySwapConfig.tokenCount = 0;
uint8[7] memory tetra0 = _getTetra(tokenIds[0]);
for (uint256 i = 0; i < tokenIds.length; i = _unsafeIncrement(i)) {
if (tokenIds[i] == 0) break;
else bountySwapConfig.tokenCount++;
if (ownerOf(tokenIds[i]) != msg.sender) revert InvalidTokenOwner();
uint8[7] memory currentTetra = _getTetra(tokenIds[i]);
if (i < tokenIds.length - 1)
for (
uint256 k = i + 1;
k < tokenIds.length;
k = _unsafeIncrement(k)
) {
if (tokenIds[k] == 0) break;
if (tokenIds[i] == tokenIds[k])
revert InvalidTokenCompare();
if (
getTetraKey(tokenIds[i], BountyTier.Silver) !=
getTetraKey(tokenIds[k], BountyTier.Silver)
) revert NotMatched();
}
if (i != 0) {
if (
tetra0[5] != 0 &&
currentTetra[5] != 0 &&
tetra0[5] == currentTetra[5]
)
bountySwapConfig.innerCount = _unsafeIncrement(
bountySwapConfig.innerCount
);
if (
tetra0[6] != 0 &&
currentTetra[6] != 0 &&
tetra0[6] == currentTetra[6]
)
bountySwapConfig.outerCount = _unsafeIncrement(
bountySwapConfig.outerCount
);
}
}
if (bountySwapConfig.tokenCount < 2) revert InvalidTokenCount();
uint256 tierIndex = 0;
if (bountySwapConfig.innerCount == bountySwapConfig.tokenCount - 1) {
tierIndex = _unsafeIncrement(tierIndex);
if (bountySwapConfig.outerCount == bountySwapConfig.tokenCount - 1)
tierIndex = _unsafeIncrement(tierIndex);
}
if (tierIndex == 0 && bountySwapConfig.tokenCount == 2) {
if (silver2Swaps == maxSilver2Swaps) revert TooManySilver2Swaps();
silver2Swaps = _unsafeIncrement(silver2Swaps);
}
for (
uint256 i = 0;
i < bountySwapConfig.tokenCount;
i = _unsafeIncrement(i)
) {
_resetTetra(tokenIds[i]);
safeTransferFrom(msg.sender, address(this), tokenIds[i]);
}
uint256 totalValueEth = getTotalBounty(
tierIndex,
bountySwapConfig.tokenCount - 2
);
uint256 bountyValue = priceOracle.getPrice(
WETH,
PORK,
uint128(totalValueEth),
PORK_WETH_POOL_FEE
);
require(
IERC20(PORK).balanceOf(address(bountyVault)) > bountyValue,
"NOT_ENOUGH_BALANCE"
);
uint256 creatorBountyPercentage = uint256(
creatorBountyTier == CreatorBountyTier.Low
? 5
: creatorBountyTier == CreatorBountyTier.Medium
? 10
: creatorBountyTier == CreatorBountyTier.High
? 20
: creatorBountyTier == CreatorBountyTier.Ultra
? 40
: 0
);
bountyVault.distribute(
msg.sender,
_percentage(bountyValue, 100 - creatorBountyPercentage),
_percentage(bountyValue, creatorBountyPercentage)
);
emit Swapped(
msg.sender,
tierIndex,
bountyValue,
tokenIds[0],
tokenIds[1],
tokenIds[2],
bountySwapConfig.tokenCount > 3 ? tokenIds[3] : 0
);
}
function _processRefresh(
DiscoveryType discoveryType,
uint256 tokenId
) private {
_excavateWhenReady(tokenId);
_fundExcavatorWhenReady();
if (
++refreshCount % silver2UnlockCadence == 0 &&
silver2Swaps > silver2UnlockAmount
) silver2Swaps -= silver2UnlockAmount;
emit Discovery(msg.sender, tokenId, discoveryType);
}
function _swapWethForToken(
address token,
uint256 ethAmount,
address recipient
) internal returns (uint256 amount) {
uint256 currWethBalance = IWETH(WETH).balanceOf(address(this));
if (ethAmount > currWethBalance) ethAmount = currWethBalance;
if (ethAmount > 0) {
amount = _swapTokens(WETH, token, ethAmount, recipient);
}
}
function _resetTetra(uint256 tokenId) private {
delete _tetraSeeds[tokenId];
delete tetraExcavationIds[tokenId];
}
function _beginGroupExcavation() private {
require(excavator.isInitialized(), "NO_SUBSCRIPTION");
uint256 excavationId = excavator.beginExcavation();
require(excavationId != 0, "EXCAVATION_FAILED");
excavationTracker[excavationId] = excavationGroup;
for (uint256 i = 0; i < CADENCE; i = _unsafeIncrement(i))
tetraExcavationIds[
uint256(uint16(excavationTracker[excavationId] >> (16 * i)))
] = excavationId;
emit ExcavationContracted(
excavationId,
uint256(uint16(excavationGroup >> 0)),
uint256(uint16(excavationGroup >> 16)),
uint256(uint16(excavationGroup >> 32)),
uint256(uint16(excavationGroup >> 48)),
uint256(uint16(excavationGroup >> 64))
);
delete excavationGroup;
}
function excavate(uint256 excavationId, uint256 value) external {
require(
msg.sender == address(excavator.vrfCoordinator()) ||
msg.sender == address(excavator),
"INVALID_SENDER"
);
_excavate(excavationId, value);
emit ExcavationComplete(excavationId, value);
}
function forceExcavate(uint256 excavationId) public onlyExecutor {
for (uint256 i = 0; i < CADENCE; i = _unsafeIncrement(i)) {
uint256 tokenId = uint256(
uint16(excavationTracker[excavationId] >> (16 * i))
);
if (tokenId == 0) revert ExcavationGroupNotReady();
}
excavator.forceExcavate(excavationId, randomizer.random());
}
function _prospekt(address recipient) private returns (uint256) {
require(totalSupply() < maxSupply);
uint256 tokenId = _prospektCount;
_prospektCount = _unsafeIncrement(_prospektCount);
_safeMint(recipient, tokenId);
_excavateWhenReady(tokenId);
emit Discovery(msg.sender, tokenId, DiscoveryType.Prospekted);
return tokenId;
}
function collectFeesForBounty() external nonReentrant {
require(balanceOf(msg.sender) > 0, "NOT_TETRA_OWNER");
require(canCollectFees(), "NOT_READY");
uint256 preBalance = IERC20(PORK).balanceOf(address(bountyVault));
prospektorFundManager.collectLPFees();
uint256 postBalance = IERC20(PORK).balanceOf(address(bountyVault));
emit CollectedFeesForBounty(
msg.sender,
interactionCount,
postBalance - preBalance,
canCollectFeesAfter
);
canCollectFeesAfter = interactionCount + feeCollectorCadence;
IERC20(WETH).approve(msg.sender, feeCollectorBounty);
IERC20(WETH).transfer(msg.sender, feeCollectorBounty);
}
function addLiquidityForBounty() external nonReentrant {
require(balanceOf(msg.sender) > 0, "NOT_TETRA_OWNER");
require(canAddLiquidity(), "NOT_READY");
(uint256 addedPorkAmount, uint256 addedWethAmount) = _addLiquidity();
canAddLiquidityAfter = interactionCount + liquidityAdderCadence;
emit AddedLiquidityForBounty(
msg.sender,
interactionCount,
addedPorkAmount,
addedWethAmount,
canAddLiquidityAfter
);
IERC20(WETH).approve(msg.sender, liquidityAdderBounty);
IERC20(WETH).transfer(msg.sender, liquidityAdderBounty);
}
function canAddLiquidity() public view returns (bool) {
return
interactionCount >= canAddLiquidityAfter &&
IERC20(WETH).balanceOf(address(this)) > liquidityAdderBounty;
}
function canCollectFees() public view returns (bool) {
return
interactionCount >= canCollectFeesAfter &&
IERC20(WETH).balanceOf(address(this)) > feeCollectorBounty;
}
function _addLiquidity()
private
returns (uint256 addedPorkAmount, uint256 addedWethAmount)
{
uint256 initialPorkBalance = _getBalanceMinusSaver(PORK);
uint256 initialWethBalance = _getBalanceMinusSaver(WETH) -
liquidityAdderBounty;
(, addedPorkAmount, addedWethAmount) = prospektorFundManager
.addLiquidity(initialPorkBalance, initialWethBalance);
uint256 remainingPorkAmount = initialPorkBalance - addedPorkAmount;
uint256 remainingWethAmount = initialWethBalance - addedWethAmount;
uint256 porkWethValue = priceOracle.getPrice(
PORK,
WETH,
uint128(remainingPorkAmount),
PORK_WETH_POOL_FEE
);
if (
porkWethValue > remainingWethAmount &&
swapPercentage - SWAP_PERCENTAGE_INCREMENT > 0
) swapPercentage -= SWAP_PERCENTAGE_INCREMENT;
if (
porkWethValue < remainingWethAmount &&
swapPercentage + SWAP_PERCENTAGE_INCREMENT < 90
) swapPercentage += SWAP_PERCENTAGE_INCREMENT;
}
function _getBalanceMinusSaver(
address erc20Address
) internal view returns (uint256 balance) {
balance = IERC20(erc20Address).balanceOf(address(this));
if (erc20Address == WETH) balance -= MIN_WETH_AMOUNT;
else if (erc20Address == PORK) balance -= MIN_PORK_AMOUNT;
}
function getTetraExcavationId(
uint256 tokenId
) external view returns (uint256) {
return tetraExcavationIds[tokenId];
}
function isExcavating(uint256 tokenId) public view returns (bool) {
return
tetraExcavationIds[tokenId] != 0 &&
_excavationResults[tetraExcavationIds[tokenId]] == 0;
}
function _excavateWhenReady(uint256 tokenId) internal {
if (isExcavating(tokenId)) revert ExcavationInProgress();
if (interactionCount % CADENCE == 0) excavationGroup = tokenId;
else excavationGroup |= tokenId << (16 * (interactionCount % CADENCE));
tetraExcavationIds[tokenId] = 1;
_createTetraSeed(tokenId);
if ((++interactionCount) % CADENCE == 0) _beginGroupExcavation();
}
function enableFlashBounty() public onlyExecutor {
require(!isFlashBountyEnabled());
flashBountyEnabled = true;
_createTetraSeed(0);
_excavate(0, randomizer.random());
}
function _disableFlashBounty() private {
require(isFlashBountyEnabled(), "NOT_ENABLED");
flashBountyEnabled = false;
delete _excavationResults[0];
}
function isFlashBountyMatch(uint256 tokenId) public view returns (bool) {
return
flashBountyEnabled
? getTetraKey(0, BountyTier.Silver) ==
getTetraKey(tokenId, BountyTier.Silver)
: false;
}
function claimFlashBounty(
uint256 tokenId
) public nonReentrant mustOwnToken(msg.sender, tokenId) {
require(flashBountyEnabled && isFlashBountyMatch(tokenId), "0");
_disableFlashBounty();
uint256 bountyValue = priceOracle.getPrice(
WETH,
PORK,
uint128(flashBountyValue),
PORK_WETH_POOL_FEE
);
bountyVault.distribute(msg.sender, bountyValue, 0);
emit ClaimedFlashBounty(
msg.sender,
tokenId,
getTetraKey(tokenId, BountyTier.Silver),
bountyValue
);
}
function claim(uint256 claimTokenId) public nonReentrant returns (uint256) {
claimsManager.claim(msg.sender, claimTokenId);
uint256 tokenId = _prospekt(msg.sender);
emit Claimed(
msg.sender,
claimsManager.getClaimsSessionId(),
claimTokenId,
tokenId
);
return tokenId;
}
function getTetraKey(
uint256 tokenId,
BountyTier tier
) public view returns (bytes32) {
if (tier == BountyTier.Silver)
return _getTetraKey(tokenId, false, false);
if (tier == BountyTier.Gold) {
return _getTetraKey(tokenId, true, false);
}
if (tier == BountyTier.Platinum)
return _getTetraKey(tokenId, true, true);
return 0;
}
function getTetraKeys(
uint256 tokenId
) public view returns (bytes32[3] memory) {
bytes32[3] memory tetraKeys = [
getTetraKey(tokenId, BountyTier.Silver),
getTetraKey(tokenId, BountyTier.Gold),
getTetraKey(tokenId, BountyTier.Platinum)
];
return tetraKeys;
}
function _getTetra(uint256 tokenId) public view returns (uint8[7] memory) {
if (
isExcavating(tokenId) ||
(tokenId == 0 && !flashBountyEnabled) ||
uint256(uint16(_tetraSeeds[tokenId] >> 16)) == 0
) return [0, 0, 0, 0, 0, 0, 0];
return [
_modToColorIndex(
_excavationResults[tetraExcavationIds[tokenId]],
uint256(uint16(_tetraSeeds[tokenId] >> 16))
),
_modToColorIndex(
_excavationResults[tetraExcavationIds[tokenId]],
uint256(uint16(_tetraSeeds[tokenId] >> 32))
),
_modToColorIndex(
_excavationResults[tetraExcavationIds[tokenId]],
uint256(uint16(_tetraSeeds[tokenId] >> 48))
),
_modToColorIndex(
_excavationResults[tetraExcavationIds[tokenId]],
uint256(uint16(_tetraSeeds[tokenId] >> 64))
),
_modToColorIndex(
_excavationResults[tetraExcavationIds[tokenId]],
uint256(uint16(_tetraSeeds[tokenId] >> 80))
),
_modToColorIndex(
_excavationResults[tetraExcavationIds[tokenId]],
uint256(uint16(_tetraSeeds[tokenId] >> 96))
),
_modToColorIndex(
_excavationResults[tetraExcavationIds[tokenId]],
uint256(uint16(_tetraSeeds[tokenId] >> 112))
)
];
}
function _getTetraKey(
uint256 tokenId,
bool includeMantle,
bool includeKrust
) private view returns (bytes32) {
uint8[7] memory tetra = _getTetra(tokenId);
return
bytes32(
(uint256(tetra[0]) + 1) *
10 ** 12 +
(uint256(tetra[1]) + 1) *
10 ** 10 +
(uint256(tetra[2]) + 1) *
10 ** 8 +
(uint256(tetra[3]) + 1) *
10 ** 6 +
(uint256(tetra[4]) + 1) *
10 ** 4 +
(includeMantle ? (uint256(tetra[5]) + 1) * 10 ** 2 : 0) +
(includeKrust ? (uint256(tetra[6]) + 1) : 0)
);
}
function tokenURI(
uint256 tokenId
) public view override returns (string memory) {
return
RenderHelper.getJson(
tokenId,
_getTetra(tokenId),
colorConfig.colorNames,
colorConfig.colorHexCodes
);
}
function _modToColorIndex(
uint256 value,
uint256 seed
) public view returns (uint8) {
unchecked {
return uint8((value / seed) % (colorConfig.maxColors - 1)) + 1;
}
}
function _createTetraSeed(uint256 tokenId) private {
uint256 tetra;
uint256 rand = randomizer.random();
for (uint256 i = 0; i < 7; i = _unsafeIncrement(i)) tetra |= rand << 16;
_tetraSeeds[tokenId] = tetra;
}
function _excavate(uint256 excavationId, uint256 value) internal {
_excavationResults[excavationId] = value;
if (excavationId == 0) {
emit ExcavatedFlashBounty(
getTetraKey(0, BountyTier.Silver),
getTetraKey(0, BountyTier.Gold),
getTetraKey(0, BountyTier.Platinum)
);
} else {
for (uint256 i = 0; i < CADENCE; i = _unsafeIncrement(i)) {
uint256 tokenId = uint256(
uint16(excavationTracker[excavationId] >> (16 * i))
);
emit Excavated(
ownerOf(tokenId),
tokenId,
getTetraKey(tokenId, BountyTier.Silver),
getTetraKey(tokenId, BountyTier.Gold),
getTetraKey(tokenId, BountyTier.Platinum)
);
}
delete excavationTracker[excavationId];
}
}
function _swapTokens(
address fromToken,
address toToken,
uint256 amountIn,
address recipient
) internal returns (uint256) {
uint24 _poolFee = fromToken == PORK || toToken == PORK
? PORK_WETH_POOL_FEE
: DEFAULT_POOL_FEE;
return
uniswapRouter.exactInputSingle(
ISwapRouter.ExactInputSingleParams({
tokenIn: fromToken,
tokenOut: toToken,
fee: _poolFee,
recipient: recipient,
amountIn: amountIn,
amountOutMinimum: 0,
sqrtPriceLimitX96: 0
})
);
}
function initialize(
address prospektorFundManagerAddress
) public onlyExecutor {
require(!isInitialized, "ALREADY_INITIALIZED");
isInitialized = true;
prospektorFundManager = IProspektorFundManager(
prospektorFundManagerAddress
);
TransferHelper.safeApprove(
WETH,
prospektorFundManagerAddress,
type(uint256).max
);
TransferHelper.safeApprove(
PORK,
prospektorFundManagerAddress,
type(uint256).max
);
TransferHelper.safeApprove(
WETH,
address(uniswapRouter),
type(uint256).max
);
TransferHelper.safeApprove(
PORK,
address(uniswapRouter),
type(uint256).max
);
}
function pause() public onlyExecutor {
_pause();
}
function unpause() public onlyExecutor {
_unpause();
}
function onERC721Received(
address,
address,
uint256,
bytes calldata
) external pure returns (bytes4) {
return IERC721Receiver.onERC721Received.selector;
}
}
文件 46 的 52:TransferHelper.sol
pragma solidity >=0.6.0;
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
library TransferHelper {
function safeTransferFrom(
address token,
address from,
address to,
uint256 value
) internal {
(bool success, bytes memory data) = token.call(
abi.encodeWithSelector(
IERC20.transferFrom.selector,
from,
to,
value
)
);
require(
success && (data.length == 0 || abi.decode(data, (bool))),
"STF"
);
}
function safeTransfer(address token, address to, uint256 value) internal {
(bool success, bytes memory data) = token.call(
abi.encodeWithSelector(IERC20.transfer.selector, to, value)
);
require(
success && (data.length == 0 || abi.decode(data, (bool))),
"ST"
);
}
function safeApprove(address token, address to, uint256 value) internal {
(bool success, bytes memory data) = token.call(
abi.encodeWithSelector(IERC20.approve.selector, to, value)
);
require(
success && (data.length == 0 || abi.decode(data, (bool))),
"SA"
);
}
function safeTransferETH(address to, uint256 value) internal {
(bool success, ) = to.call{value: value}(new bytes(0));
require(success, "STE");
}
}
文件 47 的 52:TypeAndVersionInterface.sol
pragma solidity ^0.8.0;
abstract contract TypeAndVersionInterface {
function typeAndVersion() external pure virtual returns (string memory);
}
文件 48 的 52:VRF.sol
pragma solidity ^0.8.0;
contract VRF {
uint256 private constant GROUP_ORDER = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141;
uint256 private constant FIELD_SIZE =
0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F;
uint256 private constant WORD_LENGTH_BYTES = 0x20;
function bigModExp(uint256 base, uint256 exponent) internal view returns (uint256 exponentiation) {
uint256 callResult;
uint256[6] memory bigModExpContractInputs;
bigModExpContractInputs[0] = WORD_LENGTH_BYTES;
bigModExpContractInputs[1] = WORD_LENGTH_BYTES;
bigModExpContractInputs[2] = WORD_LENGTH_BYTES;
bigModExpContractInputs[3] = base;
bigModExpContractInputs[4] = exponent;
bigModExpContractInputs[5] = FIELD_SIZE;
uint256[1] memory output;
assembly {
callResult := staticcall(
not(0),
0x05,
bigModExpContractInputs,
0xc0,
output,
0x20
)
}
if (callResult == 0) {
revert("bigModExp failure!");
}
return output[0];
}
uint256 private constant SQRT_POWER = (FIELD_SIZE + 1) >> 2;
function squareRoot(uint256 x) internal view returns (uint256) {
return bigModExp(x, SQRT_POWER);
}
function ySquared(uint256 x) internal pure returns (uint256) {
uint256 xCubed = mulmod(x, mulmod(x, x, FIELD_SIZE), FIELD_SIZE);
return addmod(xCubed, 7, FIELD_SIZE);
}
function isOnCurve(uint256[2] memory p) internal pure returns (bool) {
require(p[0] < FIELD_SIZE, "invalid x-ordinate");
require(p[1] < FIELD_SIZE, "invalid y-ordinate");
return ySquared(p[0]) == mulmod(p[1], p[1], FIELD_SIZE);
}
function fieldHash(bytes memory b) internal pure returns (uint256 x_) {
x_ = uint256(keccak256(b));
while (x_ >= FIELD_SIZE) {
x_ = uint256(keccak256(abi.encodePacked(x_)));
}
}
function newCandidateSecp256k1Point(bytes memory b) internal view returns (uint256[2] memory p) {
unchecked {
p[0] = fieldHash(b);
p[1] = squareRoot(ySquared(p[0]));
if (p[1] % 2 == 1) {
p[1] = FIELD_SIZE - p[1];
}
}
}
uint256 internal constant HASH_TO_CURVE_HASH_PREFIX = 1;
function hashToCurve(uint256[2] memory pk, uint256 input) internal view returns (uint256[2] memory rv) {
rv = newCandidateSecp256k1Point(abi.encodePacked(HASH_TO_CURVE_HASH_PREFIX, pk, input));
while (!isOnCurve(rv)) {
rv = newCandidateSecp256k1Point(abi.encodePacked(rv[0]));
}
}
function ecmulVerify(
uint256[2] memory multiplicand,
uint256 scalar,
uint256[2] memory product
) internal pure returns (bool verifies) {
require(scalar != 0, "zero scalar");
uint256 x = multiplicand[0];
uint8 v = multiplicand[1] % 2 == 0 ? 27 : 28;
bytes32 scalarTimesX = bytes32(mulmod(scalar, x, GROUP_ORDER));
address actual = ecrecover(bytes32(0), v, bytes32(x), scalarTimesX);
address expected = address(uint160(uint256(keccak256(abi.encodePacked(product)))));
return (actual == expected);
}
function projectiveSub(
uint256 x1,
uint256 z1,
uint256 x2,
uint256 z2
) internal pure returns (uint256 x3, uint256 z3) {
unchecked {
uint256 num1 = mulmod(z2, x1, FIELD_SIZE);
uint256 num2 = mulmod(FIELD_SIZE - x2, z1, FIELD_SIZE);
(x3, z3) = (addmod(num1, num2, FIELD_SIZE), mulmod(z1, z2, FIELD_SIZE));
}
}
function projectiveMul(
uint256 x1,
uint256 z1,
uint256 x2,
uint256 z2
) internal pure returns (uint256 x3, uint256 z3) {
(x3, z3) = (mulmod(x1, x2, FIELD_SIZE), mulmod(z1, z2, FIELD_SIZE));
}
function projectiveECAdd(
uint256 px,
uint256 py,
uint256 qx,
uint256 qy
) internal pure returns (uint256 sx, uint256 sy, uint256 sz) {
unchecked {
(uint256 z1, uint256 z2) = (1, 1);
uint256 lx = addmod(qy, FIELD_SIZE - py, FIELD_SIZE);
uint256 lz = addmod(qx, FIELD_SIZE - px, FIELD_SIZE);
uint256 dx;
(sx, dx) = projectiveMul(lx, lz, lx, lz);
(sx, dx) = projectiveSub(sx, dx, px, z1);
(sx, dx) = projectiveSub(sx, dx, qx, z2);
uint256 dy;
(sy, dy) = projectiveSub(px, z1, sx, dx);
(sy, dy) = projectiveMul(sy, dy, lx, lz);
(sy, dy) = projectiveSub(sy, dy, py, z1);
if (dx != dy) {
sx = mulmod(sx, dy, FIELD_SIZE);
sy = mulmod(sy, dx, FIELD_SIZE);
sz = mulmod(dx, dy, FIELD_SIZE);
} else {
sz = dx;
}
}
}
function affineECAdd(
uint256[2] memory p1,
uint256[2] memory p2,
uint256 invZ
) internal pure returns (uint256[2] memory) {
uint256 x;
uint256 y;
uint256 z;
(x, y, z) = projectiveECAdd(p1[0], p1[1], p2[0], p2[1]);
require(mulmod(z, invZ, FIELD_SIZE) == 1, "invZ must be inverse of z");
return [mulmod(x, invZ, FIELD_SIZE), mulmod(y, invZ, FIELD_SIZE)];
}
function verifyLinearCombinationWithGenerator(
uint256 c,
uint256[2] memory p,
uint256 s,
address lcWitness
) internal pure returns (bool) {
unchecked {
require(lcWitness != address(0), "bad witness");
uint8 v = (p[1] % 2 == 0) ? 27 : 28;
bytes32 pseudoHash = bytes32(GROUP_ORDER - mulmod(p[0], s, GROUP_ORDER));
bytes32 pseudoSignature = bytes32(mulmod(c, p[0], GROUP_ORDER));
address computed = ecrecover(pseudoHash, v, bytes32(p[0]), pseudoSignature);
return computed == lcWitness;
}
}
function linearCombination(
uint256 c,
uint256[2] memory p1,
uint256[2] memory cp1Witness,
uint256 s,
uint256[2] memory p2,
uint256[2] memory sp2Witness,
uint256 zInv
) internal pure returns (uint256[2] memory) {
unchecked {
require((cp1Witness[0] % FIELD_SIZE) != (sp2Witness[0] % FIELD_SIZE), "points in sum must be distinct");
require(ecmulVerify(p1, c, cp1Witness), "First mul check failed");
require(ecmulVerify(p2, s, sp2Witness), "Second mul check failed");
return affineECAdd(cp1Witness, sp2Witness, zInv);
}
}
uint256 internal constant SCALAR_FROM_CURVE_POINTS_HASH_PREFIX = 2;
function scalarFromCurvePoints(
uint256[2] memory hash,
uint256[2] memory pk,
uint256[2] memory gamma,
address uWitness,
uint256[2] memory v
) internal pure returns (uint256 s) {
return uint256(keccak256(abi.encodePacked(SCALAR_FROM_CURVE_POINTS_HASH_PREFIX, hash, pk, gamma, v, uWitness)));
}
function verifyVRFProof(
uint256[2] memory pk,
uint256[2] memory gamma,
uint256 c,
uint256 s,
uint256 seed,
address uWitness,
uint256[2] memory cGammaWitness,
uint256[2] memory sHashWitness,
uint256 zInv
) internal view {
unchecked {
require(isOnCurve(pk), "public key is not on curve");
require(isOnCurve(gamma), "gamma is not on curve");
require(isOnCurve(cGammaWitness), "cGammaWitness is not on curve");
require(isOnCurve(sHashWitness), "sHashWitness is not on curve");
require(verifyLinearCombinationWithGenerator(c, pk, s, uWitness), "addr(c*pk+s*g)!=_uWitness");
uint256[2] memory hash = hashToCurve(pk, seed);
uint256[2] memory v = linearCombination(c, gamma, cGammaWitness, s, hash, sHashWitness, zInv);
uint256 derivedC = scalarFromCurvePoints(hash, pk, gamma, uWitness, v);
require(c == derivedC, "invalid proof");
}
}
uint256 internal constant VRF_RANDOM_OUTPUT_HASH_PREFIX = 3;
struct Proof {
uint256[2] pk;
uint256[2] gamma;
uint256 c;
uint256 s;
uint256 seed;
address uWitness;
uint256[2] cGammaWitness;
uint256[2] sHashWitness;
uint256 zInv;
}
function randomValueFromVRFProof(Proof memory proof, uint256 seed) internal view returns (uint256 output) {
verifyVRFProof(
proof.pk,
proof.gamma,
proof.c,
proof.s,
seed,
proof.uWitness,
proof.cGammaWitness,
proof.sHashWitness,
proof.zInv
);
output = uint256(keccak256(abi.encode(VRF_RANDOM_OUTPUT_HASH_PREFIX, proof.gamma)));
}
}
文件 49 的 52:VRFConsumerBaseV2.sol
pragma solidity ^0.8.4;
abstract contract VRFConsumerBaseV2 {
error OnlyCoordinatorCanFulfill(address have, address want);
address private immutable vrfCoordinator;
constructor(address _vrfCoordinator) {
vrfCoordinator = _vrfCoordinator;
}
function fulfillRandomWords(uint256 requestId, uint256[] memory randomWords) internal virtual;
function rawFulfillRandomWords(uint256 requestId, uint256[] memory randomWords) external {
if (msg.sender != vrfCoordinator) {
revert OnlyCoordinatorCanFulfill(msg.sender, vrfCoordinator);
}
fulfillRandomWords(requestId, randomWords);
}
}
文件 50 的 52:VRFCoordinatorV2.sol
pragma solidity ^0.8.4;
import "../shared/interfaces/LinkTokenInterface.sol";
import "../interfaces/BlockhashStoreInterface.sol";
import "../interfaces/AggregatorV3Interface.sol";
import "../interfaces/VRFCoordinatorV2Interface.sol";
import "../interfaces/TypeAndVersionInterface.sol";
import "../shared/interfaces/IERC677Receiver.sol";
import "./VRF.sol";
import "../shared/access/ConfirmedOwner.sol";
import "./VRFConsumerBaseV2.sol";
import "../ChainSpecificUtil.sol";
contract VRFCoordinatorV2 is VRF, ConfirmedOwner, TypeAndVersionInterface, VRFCoordinatorV2Interface, IERC677Receiver {
LinkTokenInterface public immutable LINK;
AggregatorV3Interface public immutable LINK_ETH_FEED;
BlockhashStoreInterface public immutable BLOCKHASH_STORE;
uint16 public constant MAX_CONSUMERS = 100;
error TooManyConsumers();
error InsufficientBalance();
error InvalidConsumer(uint64 subId, address consumer);
error InvalidSubscription();
error OnlyCallableFromLink();
error InvalidCalldata();
error MustBeSubOwner(address owner);
error PendingRequestExists();
error MustBeRequestedOwner(address proposedOwner);
error BalanceInvariantViolated(uint256 internalBalance, uint256 externalBalance);
event FundsRecovered(address to, uint256 amount);
struct Subscription {
uint96 balance;
uint64 reqCount;
}
struct SubscriptionConfig {
address owner;
address requestedOwner;
address[] consumers;
}
mapping(address => mapping(uint64 => uint64)) private s_consumers;
mapping(uint64 => SubscriptionConfig) private s_subscriptionConfigs;
mapping(uint64 => Subscription) private s_subscriptions;
uint64 private s_currentSubId;
uint96 private s_totalBalance;
event SubscriptionCreated(uint64 indexed subId, address owner);
event SubscriptionFunded(uint64 indexed subId, uint256 oldBalance, uint256 newBalance);
event SubscriptionConsumerAdded(uint64 indexed subId, address consumer);
event SubscriptionConsumerRemoved(uint64 indexed subId, address consumer);
event SubscriptionCanceled(uint64 indexed subId, address to, uint256 amount);
event SubscriptionOwnerTransferRequested(uint64 indexed subId, address from, address to);
event SubscriptionOwnerTransferred(uint64 indexed subId, address from, address to);
uint16 public constant MAX_REQUEST_CONFIRMATIONS = 200;
uint32 public constant MAX_NUM_WORDS = 500;
uint256 private constant GAS_FOR_CALL_EXACT_CHECK = 5_000;
error InvalidRequestConfirmations(uint16 have, uint16 min, uint16 max);
error GasLimitTooBig(uint32 have, uint32 want);
error NumWordsTooBig(uint32 have, uint32 want);
error ProvingKeyAlreadyRegistered(bytes32 keyHash);
error NoSuchProvingKey(bytes32 keyHash);
error InvalidLinkWeiPrice(int256 linkWei);
error InsufficientGasForConsumer(uint256 have, uint256 want);
error NoCorrespondingRequest();
error IncorrectCommitment();
error BlockhashNotInStore(uint256 blockNum);
error PaymentTooLarge();
error Reentrant();
struct RequestCommitment {
uint64 blockNum;
uint64 subId;
uint32 callbackGasLimit;
uint32 numWords;
address sender;
}
mapping(bytes32 => address) private s_provingKeys;
bytes32[] private s_provingKeyHashes;
mapping(address => uint96) private s_withdrawableTokens;
mapping(uint256 => bytes32) private s_requestCommitments;
event ProvingKeyRegistered(bytes32 keyHash, address indexed oracle);
event ProvingKeyDeregistered(bytes32 keyHash, address indexed oracle);
event RandomWordsRequested(
bytes32 indexed keyHash,
uint256 requestId,
uint256 preSeed,
uint64 indexed subId,
uint16 minimumRequestConfirmations,
uint32 callbackGasLimit,
uint32 numWords,
address indexed sender
);
event RandomWordsFulfilled(uint256 indexed requestId, uint256 outputSeed, uint96 payment, bool success);
struct Config {
uint16 minimumRequestConfirmations;
uint32 maxGasLimit;
bool reentrancyLock;
uint32 stalenessSeconds;
uint32 gasAfterPaymentCalculation;
}
int256 private s_fallbackWeiPerUnitLink;
Config private s_config;
FeeConfig private s_feeConfig;
struct FeeConfig {
uint32 fulfillmentFlatFeeLinkPPMTier1;
uint32 fulfillmentFlatFeeLinkPPMTier2;
uint32 fulfillmentFlatFeeLinkPPMTier3;
uint32 fulfillmentFlatFeeLinkPPMTier4;
uint32 fulfillmentFlatFeeLinkPPMTier5;
uint24 reqsForTier2;
uint24 reqsForTier3;
uint24 reqsForTier4;
uint24 reqsForTier5;
}
event ConfigSet(
uint16 minimumRequestConfirmations,
uint32 maxGasLimit,
uint32 stalenessSeconds,
uint32 gasAfterPaymentCalculation,
int256 fallbackWeiPerUnitLink,
FeeConfig feeConfig
);
constructor(address link, address blockhashStore, address linkEthFeed) ConfirmedOwner(msg.sender) {
LINK = LinkTokenInterface(link);
LINK_ETH_FEED = AggregatorV3Interface(linkEthFeed);
BLOCKHASH_STORE = BlockhashStoreInterface(blockhashStore);
}
function registerProvingKey(address oracle, uint256[2] calldata publicProvingKey) external onlyOwner {
bytes32 kh = hashOfKey(publicProvingKey);
if (s_provingKeys[kh] != address(0)) {
revert ProvingKeyAlreadyRegistered(kh);
}
s_provingKeys[kh] = oracle;
s_provingKeyHashes.push(kh);
emit ProvingKeyRegistered(kh, oracle);
}
function deregisterProvingKey(uint256[2] calldata publicProvingKey) external onlyOwner {
bytes32 kh = hashOfKey(publicProvingKey);
address oracle = s_provingKeys[kh];
if (oracle == address(0)) {
revert NoSuchProvingKey(kh);
}
delete s_provingKeys[kh];
for (uint256 i = 0; i < s_provingKeyHashes.length; i++) {
if (s_provingKeyHashes[i] == kh) {
bytes32 last = s_provingKeyHashes[s_provingKeyHashes.length - 1];
s_provingKeyHashes[i] = last;
s_provingKeyHashes.pop();
}
}
emit ProvingKeyDeregistered(kh, oracle);
}
function hashOfKey(uint256[2] memory publicKey) public pure returns (bytes32) {
return keccak256(abi.encode(publicKey));
}
function setConfig(
uint16 minimumRequestConfirmations,
uint32 maxGasLimit,
uint32 stalenessSeconds,
uint32 gasAfterPaymentCalculation,
int256 fallbackWeiPerUnitLink,
FeeConfig memory feeConfig
) external onlyOwner {
if (minimumRequestConfirmations > MAX_REQUEST_CONFIRMATIONS) {
revert InvalidRequestConfirmations(
minimumRequestConfirmations,
minimumRequestConfirmations,
MAX_REQUEST_CONFIRMATIONS
);
}
if (fallbackWeiPerUnitLink <= 0) {
revert InvalidLinkWeiPrice(fallbackWeiPerUnitLink);
}
s_config = Config({
minimumRequestConfirmations: minimumRequestConfirmations,
maxGasLimit: maxGasLimit,
stalenessSeconds: stalenessSeconds,
gasAfterPaymentCalculation: gasAfterPaymentCalculation,
reentrancyLock: false
});
s_feeConfig = feeConfig;
s_fallbackWeiPerUnitLink = fallbackWeiPerUnitLink;
emit ConfigSet(
minimumRequestConfirmations,
maxGasLimit,
stalenessSeconds,
gasAfterPaymentCalculation,
fallbackWeiPerUnitLink,
s_feeConfig
);
}
function getConfig()
external
view
returns (
uint16 minimumRequestConfirmations,
uint32 maxGasLimit,
uint32 stalenessSeconds,
uint32 gasAfterPaymentCalculation
)
{
return (
s_config.minimumRequestConfirmations,
s_config.maxGasLimit,
s_config.stalenessSeconds,
s_config.gasAfterPaymentCalculation
);
}
function getFeeConfig()
external
view
returns (
uint32 fulfillmentFlatFeeLinkPPMTier1,
uint32 fulfillmentFlatFeeLinkPPMTier2,
uint32 fulfillmentFlatFeeLinkPPMTier3,
uint32 fulfillmentFlatFeeLinkPPMTier4,
uint32 fulfillmentFlatFeeLinkPPMTier5,
uint24 reqsForTier2,
uint24 reqsForTier3,
uint24 reqsForTier4,
uint24 reqsForTier5
)
{
return (
s_feeConfig.fulfillmentFlatFeeLinkPPMTier1,
s_feeConfig.fulfillmentFlatFeeLinkPPMTier2,
s_feeConfig.fulfillmentFlatFeeLinkPPMTier3,
s_feeConfig.fulfillmentFlatFeeLinkPPMTier4,
s_feeConfig.fulfillmentFlatFeeLinkPPMTier5,
s_feeConfig.reqsForTier2,
s_feeConfig.reqsForTier3,
s_feeConfig.reqsForTier4,
s_feeConfig.reqsForTier5
);
}
function getTotalBalance() external view returns (uint256) {
return s_totalBalance;
}
function getFallbackWeiPerUnitLink() external view returns (int256) {
return s_fallbackWeiPerUnitLink;
}
function ownerCancelSubscription(uint64 subId) external onlyOwner {
if (s_subscriptionConfigs[subId].owner == address(0)) {
revert InvalidSubscription();
}
cancelSubscriptionHelper(subId, s_subscriptionConfigs[subId].owner);
}
function recoverFunds(address to) external onlyOwner {
uint256 externalBalance = LINK.balanceOf(address(this));
uint256 internalBalance = uint256(s_totalBalance);
if (internalBalance > externalBalance) {
revert BalanceInvariantViolated(internalBalance, externalBalance);
}
if (internalBalance < externalBalance) {
uint256 amount = externalBalance - internalBalance;
LINK.transfer(to, amount);
emit FundsRecovered(to, amount);
}
}
function getRequestConfig() external view override returns (uint16, uint32, bytes32[] memory) {
return (s_config.minimumRequestConfirmations, s_config.maxGasLimit, s_provingKeyHashes);
}
function requestRandomWords(
bytes32 keyHash,
uint64 subId,
uint16 requestConfirmations,
uint32 callbackGasLimit,
uint32 numWords
) external override nonReentrant returns (uint256) {
if (s_subscriptionConfigs[subId].owner == address(0)) {
revert InvalidSubscription();
}
uint64 currentNonce = s_consumers[msg.sender][subId];
if (currentNonce == 0) {
revert InvalidConsumer(subId, msg.sender);
}
if (
requestConfirmations < s_config.minimumRequestConfirmations || requestConfirmations > MAX_REQUEST_CONFIRMATIONS
) {
revert InvalidRequestConfirmations(
requestConfirmations,
s_config.minimumRequestConfirmations,
MAX_REQUEST_CONFIRMATIONS
);
}
if (callbackGasLimit > s_config.maxGasLimit) {
revert GasLimitTooBig(callbackGasLimit, s_config.maxGasLimit);
}
if (numWords > MAX_NUM_WORDS) {
revert NumWordsTooBig(numWords, MAX_NUM_WORDS);
}
uint64 nonce = currentNonce + 1;
(uint256 requestId, uint256 preSeed) = computeRequestId(keyHash, msg.sender, subId, nonce);
s_requestCommitments[requestId] = keccak256(
abi.encode(requestId, ChainSpecificUtil.getBlockNumber(), subId, callbackGasLimit, numWords, msg.sender)
);
emit RandomWordsRequested(
keyHash,
requestId,
preSeed,
subId,
requestConfirmations,
callbackGasLimit,
numWords,
msg.sender
);
s_consumers[msg.sender][subId] = nonce;
return requestId;
}
function getCommitment(uint256 requestId) external view returns (bytes32) {
return s_requestCommitments[requestId];
}
function computeRequestId(
bytes32 keyHash,
address sender,
uint64 subId,
uint64 nonce
) private pure returns (uint256, uint256) {
uint256 preSeed = uint256(keccak256(abi.encode(keyHash, sender, subId, nonce)));
return (uint256(keccak256(abi.encode(keyHash, preSeed))), preSeed);
}
function callWithExactGas(uint256 gasAmount, address target, bytes memory data) private returns (bool success) {
assembly {
let g := gas()
if lt(g, GAS_FOR_CALL_EXACT_CHECK) {
revert(0, 0)
}
g := sub(g, GAS_FOR_CALL_EXACT_CHECK)
if iszero(gt(sub(g, div(g, 64)), gasAmount)) {
revert(0, 0)
}
if iszero(extcodesize(target)) {
revert(0, 0)
}
success := call(gasAmount, target, 0, add(data, 0x20), mload(data), 0, 0)
}
return success;
}
function getRandomnessFromProof(
Proof memory proof,
RequestCommitment memory rc
) private view returns (bytes32 keyHash, uint256 requestId, uint256 randomness) {
keyHash = hashOfKey(proof.pk);
address oracle = s_provingKeys[keyHash];
if (oracle == address(0)) {
revert NoSuchProvingKey(keyHash);
}
requestId = uint256(keccak256(abi.encode(keyHash, proof.seed)));
bytes32 commitment = s_requestCommitments[requestId];
if (commitment == 0) {
revert NoCorrespondingRequest();
}
if (
commitment != keccak256(abi.encode(requestId, rc.blockNum, rc.subId, rc.callbackGasLimit, rc.numWords, rc.sender))
) {
revert IncorrectCommitment();
}
bytes32 blockHash = ChainSpecificUtil.getBlockhash(rc.blockNum);
if (blockHash == bytes32(0)) {
blockHash = BLOCKHASH_STORE.getBlockhash(rc.blockNum);
if (blockHash == bytes32(0)) {
revert BlockhashNotInStore(rc.blockNum);
}
}
uint256 actualSeed = uint256(keccak256(abi.encodePacked(proof.seed, blockHash)));
randomness = VRF.randomValueFromVRFProof(proof, actualSeed);
}
function getFeeTier(uint64 reqCount) public view returns (uint32) {
FeeConfig memory fc = s_feeConfig;
if (0 <= reqCount && reqCount <= fc.reqsForTier2) {
return fc.fulfillmentFlatFeeLinkPPMTier1;
}
if (fc.reqsForTier2 < reqCount && reqCount <= fc.reqsForTier3) {
return fc.fulfillmentFlatFeeLinkPPMTier2;
}
if (fc.reqsForTier3 < reqCount && reqCount <= fc.reqsForTier4) {
return fc.fulfillmentFlatFeeLinkPPMTier3;
}
if (fc.reqsForTier4 < reqCount && reqCount <= fc.reqsForTier5) {
return fc.fulfillmentFlatFeeLinkPPMTier4;
}
return fc.fulfillmentFlatFeeLinkPPMTier5;
}
function fulfillRandomWords(Proof memory proof, RequestCommitment memory rc) external nonReentrant returns (uint96) {
uint256 startGas = gasleft();
(bytes32 keyHash, uint256 requestId, uint256 randomness) = getRandomnessFromProof(proof, rc);
uint256[] memory randomWords = new uint256[](rc.numWords);
for (uint256 i = 0; i < rc.numWords; i++) {
randomWords[i] = uint256(keccak256(abi.encode(randomness, i)));
}
delete s_requestCommitments[requestId];
VRFConsumerBaseV2 v;
bytes memory resp = abi.encodeWithSelector(v.rawFulfillRandomWords.selector, requestId, randomWords);
s_config.reentrancyLock = true;
bool success = callWithExactGas(rc.callbackGasLimit, rc.sender, resp);
s_config.reentrancyLock = false;
uint64 reqCount = s_subscriptions[rc.subId].reqCount;
s_subscriptions[rc.subId].reqCount += 1;
uint96 payment = calculatePaymentAmount(
startGas,
s_config.gasAfterPaymentCalculation,
getFeeTier(reqCount),
tx.gasprice
);
if (s_subscriptions[rc.subId].balance < payment) {
revert InsufficientBalance();
}
s_subscriptions[rc.subId].balance -= payment;
s_withdrawableTokens[s_provingKeys[keyHash]] += payment;
emit RandomWordsFulfilled(requestId, randomness, payment, success);
return payment;
}
function calculatePaymentAmount(
uint256 startGas,
uint256 gasAfterPaymentCalculation,
uint32 fulfillmentFlatFeeLinkPPM,
uint256 weiPerUnitGas
) internal view returns (uint96) {
int256 weiPerUnitLink;
weiPerUnitLink = getFeedData();
if (weiPerUnitLink <= 0) {
revert InvalidLinkWeiPrice(weiPerUnitLink);
}
uint256 l1CostWei = ChainSpecificUtil.getCurrentTxL1GasFees();
uint256 paymentNoFee = (1e18 * (weiPerUnitGas * (gasAfterPaymentCalculation + startGas - gasleft()) + l1CostWei)) /
uint256(weiPerUnitLink);
uint256 fee = 1e12 * uint256(fulfillmentFlatFeeLinkPPM);
if (paymentNoFee > (1e27 - fee)) {
revert PaymentTooLarge();
}
return uint96(paymentNoFee + fee);
}
function getFeedData() private view returns (int256) {
uint32 stalenessSeconds = s_config.stalenessSeconds;
bool staleFallback = stalenessSeconds > 0;
uint256 timestamp;
int256 weiPerUnitLink;
(, weiPerUnitLink, , timestamp, ) = LINK_ETH_FEED.latestRoundData();
if (staleFallback && stalenessSeconds < block.timestamp - timestamp) {
weiPerUnitLink = s_fallbackWeiPerUnitLink;
}
return weiPerUnitLink;
}
function oracleWithdraw(address recipient, uint96 amount) external nonReentrant {
if (s_withdrawableTokens[msg.sender] < amount) {
revert InsufficientBalance();
}
s_withdrawableTokens[msg.sender] -= amount;
s_totalBalance -= amount;
if (!LINK.transfer(recipient, amount)) {
revert InsufficientBalance();
}
}
function onTokenTransfer(address , uint256 amount, bytes calldata data) external override nonReentrant {
if (msg.sender != address(LINK)) {
revert OnlyCallableFromLink();
}
if (data.length != 32) {
revert InvalidCalldata();
}
uint64 subId = abi.decode(data, (uint64));
if (s_subscriptionConfigs[subId].owner == address(0)) {
revert InvalidSubscription();
}
uint256 oldBalance = s_subscriptions[subId].balance;
s_subscriptions[subId].balance += uint96(amount);
s_totalBalance += uint96(amount);
emit SubscriptionFunded(subId, oldBalance, oldBalance + amount);
}
function getCurrentSubId() external view returns (uint64) {
return s_currentSubId;
}
function getSubscription(
uint64 subId
) external view override returns (uint96 balance, uint64 reqCount, address owner, address[] memory consumers) {
if (s_subscriptionConfigs[subId].owner == address(0)) {
revert InvalidSubscription();
}
return (
s_subscriptions[subId].balance,
s_subscriptions[subId].reqCount,
s_subscriptionConfigs[subId].owner,
s_subscriptionConfigs[subId].consumers
);
}
function createSubscription() external override nonReentrant returns (uint64) {
s_currentSubId++;
uint64 currentSubId = s_currentSubId;
address[] memory consumers = new address[](0);
s_subscriptions[currentSubId] = Subscription({balance: 0, reqCount: 0});
s_subscriptionConfigs[currentSubId] = SubscriptionConfig({
owner: msg.sender,
requestedOwner: address(0),
consumers: consumers
});
emit SubscriptionCreated(currentSubId, msg.sender);
return currentSubId;
}
function requestSubscriptionOwnerTransfer(
uint64 subId,
address newOwner
) external override onlySubOwner(subId) nonReentrant {
if (s_subscriptionConfigs[subId].requestedOwner != newOwner) {
s_subscriptionConfigs[subId].requestedOwner = newOwner;
emit SubscriptionOwnerTransferRequested(subId, msg.sender, newOwner);
}
}
function acceptSubscriptionOwnerTransfer(uint64 subId) external override nonReentrant {
if (s_subscriptionConfigs[subId].owner == address(0)) {
revert InvalidSubscription();
}
if (s_subscriptionConfigs[subId].requestedOwner != msg.sender) {
revert MustBeRequestedOwner(s_subscriptionConfigs[subId].requestedOwner);
}
address oldOwner = s_subscriptionConfigs[subId].owner;
s_subscriptionConfigs[subId].owner = msg.sender;
s_subscriptionConfigs[subId].requestedOwner = address(0);
emit SubscriptionOwnerTransferred(subId, oldOwner, msg.sender);
}
function removeConsumer(uint64 subId, address consumer) external override onlySubOwner(subId) nonReentrant {
if (pendingRequestExists(subId)) {
revert PendingRequestExists();
}
if (s_consumers[consumer][subId] == 0) {
revert InvalidConsumer(subId, consumer);
}
address[] memory consumers = s_subscriptionConfigs[subId].consumers;
uint256 lastConsumerIndex = consumers.length - 1;
for (uint256 i = 0; i < consumers.length; i++) {
if (consumers[i] == consumer) {
address last = consumers[lastConsumerIndex];
s_subscriptionConfigs[subId].consumers[i] = last;
s_subscriptionConfigs[subId].consumers.pop();
break;
}
}
delete s_consumers[consumer][subId];
emit SubscriptionConsumerRemoved(subId, consumer);
}
function addConsumer(uint64 subId, address consumer) external override onlySubOwner(subId) nonReentrant {
if (s_subscriptionConfigs[subId].consumers.length == MAX_CONSUMERS) {
revert TooManyConsumers();
}
if (s_consumers[consumer][subId] != 0) {
return;
}
s_consumers[consumer][subId] = 1;
s_subscriptionConfigs[subId].consumers.push(consumer);
emit SubscriptionConsumerAdded(subId, consumer);
}
function cancelSubscription(uint64 subId, address to) external override onlySubOwner(subId) nonReentrant {
if (pendingRequestExists(subId)) {
revert PendingRequestExists();
}
cancelSubscriptionHelper(subId, to);
}
function cancelSubscriptionHelper(uint64 subId, address to) private nonReentrant {
SubscriptionConfig memory subConfig = s_subscriptionConfigs[subId];
Subscription memory sub = s_subscriptions[subId];
uint96 balance = sub.balance;
for (uint256 i = 0; i < subConfig.consumers.length; i++) {
delete s_consumers[subConfig.consumers[i]][subId];
}
delete s_subscriptionConfigs[subId];
delete s_subscriptions[subId];
s_totalBalance -= balance;
if (!LINK.transfer(to, uint256(balance))) {
revert InsufficientBalance();
}
emit SubscriptionCanceled(subId, to, balance);
}
function pendingRequestExists(uint64 subId) public view override returns (bool) {
SubscriptionConfig memory subConfig = s_subscriptionConfigs[subId];
for (uint256 i = 0; i < subConfig.consumers.length; i++) {
for (uint256 j = 0; j < s_provingKeyHashes.length; j++) {
(uint256 reqId, ) = computeRequestId(
s_provingKeyHashes[j],
subConfig.consumers[i],
subId,
s_consumers[subConfig.consumers[i]][subId]
);
if (s_requestCommitments[reqId] != 0) {
return true;
}
}
}
return false;
}
modifier onlySubOwner(uint64 subId) {
address owner = s_subscriptionConfigs[subId].owner;
if (owner == address(0)) {
revert InvalidSubscription();
}
if (msg.sender != owner) {
revert MustBeSubOwner(owner);
}
_;
}
modifier nonReentrant() {
if (s_config.reentrancyLock) {
revert Reentrant();
}
_;
}
function typeAndVersion() external pure virtual override returns (string memory) {
return "VRFCoordinatorV2 1.0.0";
}
}
文件 51 的 52:VRFCoordinatorV2Interface.sol
pragma solidity ^0.8.0;
interface VRFCoordinatorV2Interface {
function getRequestConfig() external view returns (uint16, uint32, bytes32[] memory);
function requestRandomWords(
bytes32 keyHash,
uint64 subId,
uint16 minimumRequestConfirmations,
uint32 callbackGasLimit,
uint32 numWords
) external returns (uint256 requestId);
function createSubscription() external returns (uint64 subId);
function getSubscription(
uint64 subId
) external view returns (uint96 balance, uint64 reqCount, address owner, address[] memory consumers);
function requestSubscriptionOwnerTransfer(uint64 subId, address newOwner) external;
function acceptSubscriptionOwnerTransfer(uint64 subId) external;
function addConsumer(uint64 subId, address consumer) external;
function removeConsumer(uint64 subId, address consumer) external;
function cancelSubscription(uint64 subId, address to) external;
function pendingRequestExists(uint64 subId) external view returns (bool);
}
文件 52 的 52:draft-IERC6093.sol
pragma solidity ^0.8.20;
interface IERC20Errors {
error ERC20InsufficientBalance(address sender, uint256 balance, uint256 needed);
error ERC20InvalidSender(address sender);
error ERC20InvalidReceiver(address receiver);
error ERC20InsufficientAllowance(address spender, uint256 allowance, uint256 needed);
error ERC20InvalidApprover(address approver);
error ERC20InvalidSpender(address spender);
}
interface IERC721Errors {
error ERC721InvalidOwner(address owner);
error ERC721NonexistentToken(uint256 tokenId);
error ERC721IncorrectOwner(address sender, uint256 tokenId, address owner);
error ERC721InvalidSender(address sender);
error ERC721InvalidReceiver(address receiver);
error ERC721InsufficientApproval(address operator, uint256 tokenId);
error ERC721InvalidApprover(address approver);
error ERC721InvalidOperator(address operator);
}
interface IERC1155Errors {
error ERC1155InsufficientBalance(address sender, uint256 balance, uint256 needed, uint256 tokenId);
error ERC1155InvalidSender(address sender);
error ERC1155InvalidReceiver(address receiver);
error ERC1155MissingApprovalForAll(address operator, address owner);
error ERC1155InvalidApprover(address approver);
error ERC1155InvalidOperator(address operator);
error ERC1155InvalidArrayLength(uint256 idsLength, uint256 valuesLength);
}
{
"compilationTarget": {
"contracts/TetraSpectra.sol": "TetraSpektra"
},
"evmVersion": "paris",
"libraries": {
"contracts/utils/RenderHelper.sol:RenderHelper": "0xccd1203f67954d8886c13da1ad1c21595ab6db31"
},
"metadata": {
"bytecodeHash": "ipfs"
},
"optimizer": {
"enabled": true,
"runs": 1000
},
"remappings": []
}
[{"inputs":[{"components":[{"internalType":"address","name":"randomizerAddress","type":"address"},{"internalType":"address","name":"claimsManagerAddress","type":"address"},{"internalType":"address","name":"priceOracleAddress","type":"address"},{"internalType":"address","name":"bountyVaultAddress","type":"address"},{"internalType":"address","name":"uniswapRouterAddress","type":"address"}],"internalType":"struct AddressConfig","name":"addressConfig","type":"tuple"},{"components":[{"internalType":"address","name":"wethTokenAddress","type":"address"},{"internalType":"address","name":"porkTokenAddress","type":"address"}],"internalType":"struct TokenConfig","name":"tokenConfig","type":"tuple"},{"components":[{"internalType":"address","name":"vrfCoordinatorAddress","type":"address"},{"internalType":"bytes32","name":"vrfKeyHash","type":"bytes32"},{"internalType":"address","name":"linkTokenAddress","type":"address"}],"internalType":"struct ExcavatorConfig","name":"excavatorConfig","type":"tuple"},{"components":[{"internalType":"uint256","name":"maxSupply","type":"uint256"},{"internalType":"uint256","name":"prospektPrice","type":"uint256"},{"internalType":"uint256","name":"refreshPrice","type":"uint256"},{"internalType":"uint256","name":"flashBountyValue","type":"uint256"},{"internalType":"uint8","name":"excavatorFundingPercentage","type":"uint8"},{"internalType":"uint16","name":"maxSilver2Swaps","type":"uint16"},{"internalType":"uint8","name":"silver2UnlockCadence","type":"uint8"},{"internalType":"uint8","name":"silver2UnlockAmount","type":"uint8"},{"internalType":"string[]","name":"colorHexCodes","type":"string[]"},{"internalType":"string[]","name":"colorNames","type":"string[]"}],"internalType":"struct TetraSpektraConfig","name":"tetraSpektraConfig","type":"tuple"},{"internalType":"uint256[3][3]","name":"bounties","type":"uint256[3][3]"}],"stateMutability":"payable","type":"constructor"},{"inputs":[],"name":"CannotRemoveSelf","type":"error"},{"inputs":[{"internalType":"address","name":"sender","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"address","name":"owner","type":"address"}],"name":"ERC721IncorrectOwner","type":"error"},{"inputs":[{"internalType":"address","name":"operator","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"ERC721InsufficientApproval","type":"error"},{"inputs":[{"internalType":"address","name":"approver","type":"address"}],"name":"ERC721InvalidApprover","type":"error"},{"inputs":[{"internalType":"address","name":"operator","type":"address"}],"name":"ERC721InvalidOperator","type":"error"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"ERC721InvalidOwner","type":"error"},{"inputs":[{"internalType":"address","name":"receiver","type":"address"}],"name":"ERC721InvalidReceiver","type":"error"},{"inputs":[{"internalType":"address","name":"sender","type":"address"}],"name":"ERC721InvalidSender","type":"error"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"ERC721NonexistentToken","type":"error"},{"inputs":[],"name":"EnforcedPause","type":"error"},{"inputs":[],"name":"ExcavationGroupNotReady","type":"error"},{"inputs":[],"name":"ExcavationInProgress","type":"error"},{"inputs":[],"name":"ExpectedPause","type":"error"},{"inputs":[],"name":"InsufficientPayment","type":"error"},{"inputs":[],"name":"InvalidToken","type":"error"},{"inputs":[],"name":"InvalidTokenCompare","type":"error"},{"inputs":[],"name":"InvalidTokenCount","type":"error"},{"inputs":[],"name":"InvalidTokenOwner","type":"error"},{"inputs":[{"internalType":"address","name":"attempted","type":"address"}],"name":"NotExecutor","type":"error"},{"inputs":[],"name":"NotMatched","type":"error"},{"inputs":[],"name":"NotSoldOut","type":"error"},{"inputs":[],"name":"ReentrancyGuardReentrantCall","type":"error"},{"inputs":[],"name":"TooManySilver2Swaps","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"prospektor","type":"address"},{"indexed":false,"internalType":"uint256","name":"interactionCount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"porkAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"wethAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"canAddLiquidityAfter","type":"uint256"}],"name":"AddedLiquidityForBounty","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"approved","type":"address"},{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"operator","type":"address"},{"indexed":false,"internalType":"bool","name":"approved","type":"bool"}],"name":"ApprovalForAll","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"prospektor","type":"address"},{"indexed":false,"internalType":"uint256","name":"claimsSessionId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"claimedTokenId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"Claimed","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"prospektor","type":"address"},{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"},{"indexed":false,"internalType":"bytes32","name":"tetraKey","type":"bytes32"},{"indexed":false,"internalType":"uint256","name":"bountyValue","type":"uint256"}],"name":"ClaimedFlashBounty","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"prospektor","type":"address"},{"indexed":false,"internalType":"uint256","name":"interactionCount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"porkAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"canCollectFeesAfter","type":"uint256"}],"name":"CollectedFeesForBounty","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"prospektor","type":"address"},{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"},{"indexed":false,"internalType":"enum DiscoveryType","name":"discoveryType","type":"uint8"}],"name":"Discovery","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"prospektor","type":"address"},{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"},{"indexed":false,"internalType":"bytes32","name":"silverTetraKey","type":"bytes32"},{"indexed":false,"internalType":"bytes32","name":"goldTetraKey","type":"bytes32"},{"indexed":false,"internalType":"bytes32","name":"platinumTetraKey","type":"bytes32"}],"name":"Excavated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bytes32","name":"silverTetraKey","type":"bytes32"},{"indexed":false,"internalType":"bytes32","name":"goldTetraKey","type":"bytes32"},{"indexed":false,"internalType":"bytes32","name":"platinumTetraKey","type":"bytes32"}],"name":"ExcavatedFlashBounty","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"excavationId","type":"uint256"},{"indexed":true,"internalType":"uint256","name":"value","type":"uint256"}],"name":"ExcavationComplete","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"excavationId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"tokenId_0","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"tokenId_1","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"tokenId_2","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"tokenId_3","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"tokenId_4","type":"uint256"}],"name":"ExcavationContracted","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"account","type":"address"}],"name":"Paused","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"prospektor","type":"address"},{"indexed":true,"internalType":"uint256","name":"tier","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"bountyValue","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"tokenId_0","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"tokenId_1","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"tokenId_2","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"tokenId_3","type":"uint256"}],"name":"Swapped","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"Transfer","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"account","type":"address"}],"name":"Unpaused","type":"event"},{"inputs":[],"name":"BASE_TETRA_KEY","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"DEFAULT_POOL_FEE","outputs":[{"internalType":"uint24","name":"","type":"uint24"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MAX_TO_PROSPEKT","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MIN_PORK_AMOUNT","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MIN_WETH_AMOUNT","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"PORK","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"PORK_WETH_POOL_FEE","outputs":[{"internalType":"uint24","name":"","type":"uint24"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"SWAP_PERCENTAGE_INCREMENT","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"WETH","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"_getTetra","outputs":[{"internalType":"uint8[7]","name":"","type":"uint8[7]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"value","type":"uint256"},{"internalType":"uint256","name":"seed","type":"uint256"}],"name":"_modToColorIndex","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_toAdd","type":"address"}],"name":"addExecutor","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"addLiquidityForBounty","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"approve","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"bountyVault","outputs":[{"internalType":"contract IBountyVault","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"canAddLiquidity","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"canAddLiquidityAfter","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"canCollectFees","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"canCollectFeesAfter","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"claimTokenId","type":"uint256"}],"name":"claim","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"claimFlashBounty","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"claimVaultBounty","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"claimsManager","outputs":[{"internalType":"contract IClaimsManager","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"collectFeesForBounty","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"colorConfig","outputs":[{"internalType":"uint8","name":"maxColors","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"enableFlashBounty","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"excavationId","type":"uint256"},{"internalType":"uint256","name":"value","type":"uint256"}],"name":"excavate","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"excavationGroup","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"excavationTracker","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"excavator","outputs":[{"internalType":"contract Excavator","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"executors","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"feeCollectorBounty","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"feeCollectorCadence","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"flashBountyEnabled","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"flashBountyValue","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"excavationId","type":"uint256"}],"name":"forceExcavate","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"fundingPercentage","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"getApproved","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getBounties","outputs":[{"internalType":"uint256[3][3]","name":"","type":"uint256[3][3]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"getTetraExcavationId","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"enum BountyTier","name":"tier","type":"uint8"}],"name":"getTetraKey","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"getTetraKeys","outputs":[{"internalType":"bytes32[3]","name":"","type":"bytes32[3]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getTetrasRemaining","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"prospektorFundManagerAddress","type":"address"}],"name":"initialize","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"interactionCount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"operator","type":"address"}],"name":"isApprovedForAll","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"isExcavated","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"isExcavating","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_executor","type":"address"}],"name":"isExecutor","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"isFlashBountyEnabled","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"isFlashBountyMatch","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"isInitialized","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"liquidityAdderBounty","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"liquidityAdderCadence","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"maxSilver2Swaps","outputs":[{"internalType":"uint16","name":"","type":"uint16"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"maxSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bytes","name":"","type":"bytes"}],"name":"onERC721Received","outputs":[{"internalType":"bytes4","name":"","type":"bytes4"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"ownerOf","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pause","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"paused","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"priceOracle","outputs":[{"internalType":"contract IPriceOracle","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"count","type":"uint256"}],"name":"prospekt","outputs":[{"internalType":"uint256[5]","name":"tokenIds","type":"uint256[5]"}],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"prospektPrice","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"prospektorFundManager","outputs":[{"internalType":"contract IProspektorFundManager","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"randomizer","outputs":[{"internalType":"contract IRandomizer","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"tokenIds","type":"uint256[]"}],"name":"refine","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"tokenIds","type":"uint256[]"}],"name":"reforge","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"refreshCount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"refreshPrice","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_toRemove","type":"address"}],"name":"removeExecutor","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"safeTransferFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"safeTransferFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"operator","type":"address"},{"internalType":"bool","name":"approved","type":"bool"}],"name":"setApprovalForAll","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"silver2Swaps","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"silver2UnlockAmount","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"silver2UnlockCadence","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes4","name":"interfaceId","type":"bytes4"}],"name":"supportsInterface","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256[4]","name":"tokenIds","type":"uint256[4]"},{"internalType":"enum CreatorBountyTier","name":"creatorBountyTier","type":"uint8"}],"name":"swapForBounty","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"swapPercentage","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"tetraExcavationIds","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"tokenURI","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"transferFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"uniswapPool","outputs":[{"internalType":"contract IUniswapV3Pool","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"uniswapRouter","outputs":[{"internalType":"contract ISwapRouter","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"unpause","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint8","name":"percentage","type":"uint8"}],"name":"updateExcavatorFundingPercentage","outputs":[],"stateMutability":"nonpayable","type":"function"}]