文件 1 的 10:Context.sol
pragma solidity ^0.8.0;
abstract contract Context {
function _msgSender() internal view virtual returns (address) {
return msg.sender;
}
function _msgData() internal view virtual returns (bytes calldata) {
return msg.data;
}
}
文件 2 的 10:IERC1155.sol
pragma solidity ^0.8.0;
import "IERC165.sol";
interface IERC1155 is IERC165 {
event TransferSingle(address indexed operator, address indexed from, address indexed to, uint256 id, uint256 value);
event TransferBatch(
address indexed operator,
address indexed from,
address indexed to,
uint256[] ids,
uint256[] values
);
event ApprovalForAll(address indexed account, address indexed operator, bool approved);
event URI(string value, uint256 indexed id);
function balanceOf(address account, uint256 id) external view returns (uint256);
function balanceOfBatch(address[] calldata accounts, uint256[] calldata ids)
external
view
returns (uint256[] memory);
function setApprovalForAll(address operator, bool approved) external;
function isApprovedForAll(address account, address operator) external view returns (bool);
function safeTransferFrom(
address from,
address to,
uint256 id,
uint256 amount,
bytes calldata data
) external;
function safeBatchTransferFrom(
address from,
address to,
uint256[] calldata ids,
uint256[] calldata amounts,
bytes calldata data
) external;
}
文件 3 的 10:IERC1155Burnable.sol
pragma solidity ^0.8.2;
interface IERC1155Burnable {
function burnFor(address _user, uint256 _tokenId, uint256 _amount) external;
}
文件 4 的 10:IERC165.sol
pragma solidity ^0.8.0;
interface IERC165 {
function supportsInterface(bytes4 interfaceId) external view returns (bool);
}
文件 5 的 10:IERC721.sol
pragma solidity ^0.8.0;
import "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);
}
文件 6 的 10:ILink.sol
pragma solidity ^0.8.2;
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);
}
文件 7 的 10:IVRFCoordinatorV2Interface.sol
pragma solidity ^0.8.2;
interface IVRFCoordinatorV2Interface {
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;
}
文件 8 的 10:MainnetRedeemer.sol
pragma solidity ^0.8.2;
import "IERC721.sol";
import "IERC1155.sol";
import "Ownable.sol";
import "VRFConsumberBase.sol";
import "IERC1155Burnable.sol";
contract TheKlaw is VRFConsumerBaseV2 {
struct NFT {
address contractAddress;
uint256 tokenId;
}
address public immutable GOLDEN_TICKET;
uint256 immutable TICKED_ID;
address constant BURN_ADDRESS = 0x000000000000000000000000000000000000dEaD;
uint256 totalCount;
mapping(uint256 => NFT) public nfts;
mapping(uint256 => address) public requestIds;
mapping(address => bool) authorised;
event NFTClaimed(address indexed receiver, address indexed tokenAddress, uint256 tokenId);
constructor(address _gold, uint256 _ticketId, address _vrfCoordinator, address _link) VRFConsumerBaseV2(_vrfCoordinator, _link) {
GOLDEN_TICKET = _gold;
TICKED_ID = _ticketId;
}
modifier isAuthorised() {
require(authorised[msg.sender] || msg.sender == owner());
_;
}
function setAuthorised(address _user, bool _val) external onlyOwner {
authorised[_user] = _val;
}
function viewNFTs(uint256 _start, uint256 _maxLen) external view returns(NFT[] memory) {
if (_start >= totalCount)
return new NFT[](0);
if (_start + _maxLen > totalCount)
_maxLen = totalCount - _start;
NFT[] memory _nfts = new NFT[](_maxLen);
for (uint256 i = 0; i < _maxLen; i++) {
_nfts[i] = nfts[i + _start];
}
return _nfts;
}
function depositNFTs(address[] calldata _contracts, uint256[] calldata _tokenIds) external isAuthorised {
require(_contracts.length == _tokenIds.length);
uint256 len = _contracts.length;
uint256 currentCount = totalCount;
for (uint256 i = 0; i < len; i++) {
nfts[currentCount++] = NFT(_contracts[i], _tokenIds[i]);
}
totalCount = currentCount;
}
function removeNFTs(uint256[] calldata _nfts) external isAuthorised {
uint256 total = totalCount;
for (uint256 i = 0 ; i < _nfts.length; i++) {
address nftContract = nfts[_nfts[i]].contractAddress;
uint256 tokenId = nfts[_nfts[i]].tokenId;
nfts[_nfts[i]] = nfts[total - i - 1];
delete nfts[total - i - 1];
IERC721(nftContract).transferFrom(address(this), owner(), tokenId);
}
totalCount -= _nfts.length;
}
function claimNft() external {
uint256 requestId = requestRandomWords();
requestIds[requestId] = msg.sender;
IERC1155Burnable(GOLDEN_TICKET).burnFor(msg.sender, TICKED_ID, 1);
}
function fulfillRandomWords(uint256 requestId, uint256[] memory randomWords) internal override {
address receiver = requestIds[requestId];
uint256 total = totalCount--;
uint256 index = randomWords[0] % total;
address nftContract = nfts[index].contractAddress;
uint256 tokenId = nfts[index].tokenId;
nfts[index] = nfts[total - 1];
delete nfts[total - 1];
IERC721(nftContract).transferFrom(address(this), receiver, tokenId);
emit NFTClaimed(receiver, nftContract, tokenId);
}
function onERC1155Received(address _operator, address _from, uint256 _id, uint256 _value, bytes calldata _data) external returns(bytes4) {
return TheKlaw.onERC1155Received.selector;
}
}
文件 9 的 10:Ownable.sol
pragma solidity ^0.8.0;
import "Context.sol";
abstract contract Ownable is Context {
address internal _owner;
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
constructor() {
_transferOwnership(_msgSender());
}
function owner() public view virtual returns (address) {
return _owner;
}
modifier onlyOwner() {
require(owner() == _msgSender(), "Ownable: caller is not the owner");
_;
}
function renounceOwnership() public virtual onlyOwner {
_transferOwnership(address(0));
}
function transferOwnership(address newOwner) public virtual onlyOwner {
require(newOwner != address(0), "Ownable: new owner is the zero address");
_transferOwnership(newOwner);
}
function _transferOwnership(address newOwner) internal virtual {
address oldOwner = _owner;
_owner = newOwner;
emit OwnershipTransferred(oldOwner, newOwner);
}
}
文件 10 的 10:VRFConsumberBase.sol
pragma solidity ^0.8.2;
import "IVRFCoordinatorV2Interface.sol";
import "ILink.sol";
import "Ownable.sol";
abstract contract VRFConsumerBaseV2 is Ownable {
struct RequestConfig {
uint64 subId;
uint32 callbackGasLimit;
uint16 requestConfirmations;
uint32 numWords;
bytes32 keyHash;
}
RequestConfig public config;
IVRFCoordinatorV2Interface private COORDINATOR;
LinkTokenInterface private LINK;
constructor(address _vrfCoordinator, address _link) {
COORDINATOR = IVRFCoordinatorV2Interface(_vrfCoordinator);
LINK = LinkTokenInterface(_link);
config = RequestConfig({
subId: 0,
callbackGasLimit: 1000000,
requestConfirmations: 3,
numWords: 1,
keyHash: 0x8af398995b04c28e9951adb9721ef74c74f93e6a478f39e7e0777be13527e7ef
});
}
function setupConfig(uint32 _gasLimit, uint16 _requestConfirmations, uint32 _numWords, bytes32 _keyHash) external onlyOwner {
uint64 _subId = config.subId;
config = RequestConfig({
subId: _subId,
callbackGasLimit: _gasLimit,
requestConfirmations: _requestConfirmations,
numWords: _numWords,
keyHash: _keyHash
});
}
function fulfillRandomWords(uint256 requestId, uint256[] memory randomWords) internal virtual;
function rawFulfillRandomWords(uint256 requestId, uint256[] memory randomWords) external {
require (msg.sender == address(COORDINATOR), "!coordinator");
fulfillRandomWords(requestId, randomWords);
}
function requestRandomWords() internal returns(uint256 requestId) {
RequestConfig memory rc = config;
requestId = COORDINATOR.requestRandomWords(
rc.keyHash,
rc.subId,
rc.requestConfirmations,
rc.callbackGasLimit,
rc.numWords
);
}
function topUpSubscription(uint256 amount) external onlyOwner {
LINK.transferAndCall(address(COORDINATOR), amount, abi.encode(config.subId));
}
function withdraw(uint256 amount, address to) external onlyOwner {
LINK.transfer(to, amount);
}
function unsubscribe(address to) external onlyOwner {
COORDINATOR.cancelSubscription(config.subId, to);
config.subId = 0;
}
function subscribe() public onlyOwner {
address[] memory consumers = new address[](1);
consumers[0] = address(this);
config.subId = COORDINATOR.createSubscription();
COORDINATOR.addConsumer(config.subId, consumers[0]);
}
}
{
"compilationTarget": {
"MainnetRedeemer.sol": "TheKlaw"
},
"evmVersion": "istanbul",
"libraries": {},
"metadata": {
"bytecodeHash": "ipfs"
},
"optimizer": {
"enabled": true,
"runs": 200
},
"remappings": []
}
[{"inputs":[{"internalType":"address","name":"_gold","type":"address"},{"internalType":"uint256","name":"_ticketId","type":"uint256"},{"internalType":"address","name":"_vrfCoordinator","type":"address"},{"internalType":"address","name":"_link","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"receiver","type":"address"},{"indexed":true,"internalType":"address","name":"tokenAddress","type":"address"},{"indexed":false,"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"NFTClaimed","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"inputs":[],"name":"GOLDEN_TICKET","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"claimNft","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"config","outputs":[{"internalType":"uint64","name":"subId","type":"uint64"},{"internalType":"uint32","name":"callbackGasLimit","type":"uint32"},{"internalType":"uint16","name":"requestConfirmations","type":"uint16"},{"internalType":"uint32","name":"numWords","type":"uint32"},{"internalType":"bytes32","name":"keyHash","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address[]","name":"_contracts","type":"address[]"},{"internalType":"uint256[]","name":"_tokenIds","type":"uint256[]"}],"name":"depositNFTs","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"nfts","outputs":[{"internalType":"address","name":"contractAddress","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_operator","type":"address"},{"internalType":"address","name":"_from","type":"address"},{"internalType":"uint256","name":"_id","type":"uint256"},{"internalType":"uint256","name":"_value","type":"uint256"},{"internalType":"bytes","name":"_data","type":"bytes"}],"name":"onERC1155Received","outputs":[{"internalType":"bytes4","name":"","type":"bytes4"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"requestId","type":"uint256"},{"internalType":"uint256[]","name":"randomWords","type":"uint256[]"}],"name":"rawFulfillRandomWords","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"_nfts","type":"uint256[]"}],"name":"removeNFTs","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"requestIds","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_user","type":"address"},{"internalType":"bool","name":"_val","type":"bool"}],"name":"setAuthorised","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint32","name":"_gasLimit","type":"uint32"},{"internalType":"uint16","name":"_requestConfirmations","type":"uint16"},{"internalType":"uint32","name":"_numWords","type":"uint32"},{"internalType":"bytes32","name":"_keyHash","type":"bytes32"}],"name":"setupConfig","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"subscribe","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"topUpSubscription","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"}],"name":"unsubscribe","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_start","type":"uint256"},{"internalType":"uint256","name":"_maxLen","type":"uint256"}],"name":"viewNFTs","outputs":[{"components":[{"internalType":"address","name":"contractAddress","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"internalType":"struct TheKlaw.NFT[]","name":"","type":"tuple[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"address","name":"to","type":"address"}],"name":"withdraw","outputs":[],"stateMutability":"nonpayable","type":"function"}]