-
Notifications
You must be signed in to change notification settings - Fork 25
/
Copy pathRabbitHoleReceipt.sol
195 lines (169 loc) · 7.41 KB
/
RabbitHoleReceipt.sol
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.15;
import '@openzeppelin/contracts-upgradeable/token/ERC721/ERC721Upgradeable.sol';
import '@openzeppelin/contracts-upgradeable/token/ERC721/extensions/ERC721URIStorageUpgradeable.sol';
import '@openzeppelin/contracts-upgradeable/token/ERC721/extensions/ERC721EnumerableUpgradeable.sol';
import '@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol';
import '@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol';
import '@openzeppelin/contracts-upgradeable/interfaces/IERC2981Upgradeable.sol';
import '@openzeppelin/contracts-upgradeable/utils/CountersUpgradeable.sol';
import './ReceiptRenderer.sol';
import './interfaces/IQuestFactory.sol';
import './interfaces/IQuest.sol';
contract RabbitHoleReceipt is
Initializable,
ERC721Upgradeable,
ERC721EnumerableUpgradeable,
ERC721URIStorageUpgradeable,
OwnableUpgradeable,
IERC2981Upgradeable
{
event RoyaltyFeeSet(uint256 indexed royaltyFee);
event MinterAddressSet(address indexed minterAddress);
using CountersUpgradeable for CountersUpgradeable.Counter;
CountersUpgradeable.Counter private _tokenIds;
// storage
mapping(uint => string) public questIdForTokenId;
address public royaltyRecipient;
address public minterAddress;
uint public royaltyFee;
mapping(uint => uint) public timestampForTokenId;
ReceiptRenderer public ReceiptRendererContract;
IQuestFactory public QuestFactoryContract;
/// @custom:oz-upgrades-unsafe-allow constructor
constructor() {
_disableInitializers();
}
function initialize(
address receiptRenderer_,
address royaltyRecipient_,
address minterAddress_,
uint royaltyFee_
) public initializer {
__ERC721_init('RabbitHoleReceipt', 'RHR');
__ERC721URIStorage_init();
__Ownable_init();
royaltyRecipient = royaltyRecipient_;
minterAddress = minterAddress_;
royaltyFee = royaltyFee_;
ReceiptRendererContract = ReceiptRenderer(receiptRenderer_);
}
modifier onlyMinter() {
msg.sender == minterAddress;
_;
}
/// @dev set the receipt renderer contract
/// @param receiptRenderer_ the address of the receipt renderer contract
function setReceiptRenderer(address receiptRenderer_) public onlyOwner {
ReceiptRendererContract = ReceiptRenderer(receiptRenderer_);
}
/// @dev set the royalty recipient
/// @param royaltyRecipient_ the address of the royalty recipient
function setRoyaltyRecipient(address royaltyRecipient_) public onlyOwner {
royaltyRecipient = royaltyRecipient_;
}
/// @dev set the quest factory contract
/// @param questFactory_ the address of the quest factory contract
function setQuestFactory(address questFactory_) public onlyOwner {
QuestFactoryContract = IQuestFactory(questFactory_);
}
/// @dev set the minter address
/// @param minterAddress_ the address of the minter
function setMinterAddress(address minterAddress_) public onlyOwner {
minterAddress = minterAddress_;
emit MinterAddressSet(minterAddress_);
}
/// @dev set the royalty fee
/// @param royaltyFee_ the royalty fee
function setRoyaltyFee(uint256 royaltyFee_) public onlyOwner {
royaltyFee = royaltyFee_;
emit RoyaltyFeeSet(royaltyFee_);
}
/// @dev mint a receipt
/// @param to_ the address to mint to
/// @param questId_ the quest id
function mint(address to_, string memory questId_) public onlyMinter {
_tokenIds.increment();
uint newTokenID = _tokenIds.current();
questIdForTokenId[newTokenID] = questId_;
timestampForTokenId[newTokenID] = block.timestamp;
_safeMint(to_, newTokenID);
}
/// @dev get the token ids for a quest owned by an address
/// @param questId_ the quest id
/// @param claimingAddress_ the address claiming to own the tokens
function getOwnedTokenIdsOfQuest(
string memory questId_,
address claimingAddress_
) public view returns (uint[] memory) {
uint msgSenderBalance = balanceOf(claimingAddress_);
uint[] memory tokenIdsForQuest = new uint[](msgSenderBalance);
uint foundTokens = 0;
for (uint i = 0; i < msgSenderBalance; i++) {
uint tokenId = tokenOfOwnerByIndex(claimingAddress_, i);
if (keccak256(bytes(questIdForTokenId[tokenId])) == keccak256(bytes(questId_))) {
tokenIdsForQuest[i] = tokenId;
foundTokens++;
}
}
uint[] memory filteredTokens = new uint[](foundTokens);
uint filterTokensIndexTracker = 0;
for (uint i = 0; i < msgSenderBalance; i++) {
if (tokenIdsForQuest[i] > 0) {
filteredTokens[filterTokensIndexTracker] = tokenIdsForQuest[i];
filterTokensIndexTracker++;
}
}
return filteredTokens;
}
/// @dev burn a receipt
/// @param tokenId_ the token id
function _burn(uint256 tokenId_) internal override(ERC721Upgradeable, ERC721URIStorageUpgradeable) {
super._burn(tokenId_);
}
/// @dev before token transfer hook, called before any token transfer
/// @param from_ the address from
/// @param to_ the address to
/// @param tokenId_ the token id
/// @param batchSize_ the batch size
function _beforeTokenTransfer(
address from_,
address to_,
uint256 tokenId_,
uint256 batchSize_
) internal override(ERC721Upgradeable, ERC721EnumerableUpgradeable) {
super._beforeTokenTransfer(from_, to_, tokenId_, batchSize_);
}
/// @dev return the token uri, this delegates to the receipt renderer contract
function tokenURI(
uint tokenId_
) public view virtual override(ERC721Upgradeable, ERC721URIStorageUpgradeable) returns (string memory) {
require(_exists(tokenId_), 'ERC721URIStorage: URI query for nonexistent token');
require(QuestFactoryContract != IQuestFactory(address(0)), 'QuestFactory not set');
string memory questId = questIdForTokenId[tokenId_];
(address questAddress, uint totalParticipants, ) = QuestFactoryContract.questInfo(questId);
IQuest questContract = IQuest(questAddress);
bool claimed = questContract.isClaimed(tokenId_);
uint rewardAmount = questContract.getRewardAmount();
address rewardAddress = questContract.getRewardToken();
return ReceiptRendererContract.generateTokenURI(tokenId_, questId, totalParticipants, claimed, rewardAmount, rewardAddress);
}
/// @dev See {IERC165-royaltyInfo}
/// @param tokenId_ the token id
/// @param salePrice_ the sale price
function royaltyInfo(
uint256 tokenId_,
uint256 salePrice_
) external view override returns (address receiver, uint256 royaltyAmount) {
require(_exists(tokenId_), 'Nonexistent token');
uint256 royaltyPayment = (salePrice_ * royaltyFee) / 10_000;
return (royaltyRecipient, royaltyPayment);
}
/// @dev returns true if the supplied interface id is supported
/// @param interfaceId_ the interface id
function supportsInterface(
bytes4 interfaceId_
) public view virtual override(ERC721Upgradeable, ERC721EnumerableUpgradeable, IERC165Upgradeable) returns (bool) {
return interfaceId_ == type(IERC2981Upgradeable).interfaceId || super.supportsInterface(interfaceId_);
}
}