文件 1 的 14:Address.sol
pragma solidity ^0.8.0;
library Address {
function isContract(address account) internal view returns (bool) {
uint256 size;
assembly {
size := extcodesize(account)
}
return size > 0;
}
function sendValue(address payable recipient, uint256 amount) internal {
require(address(this).balance >= amount, "Address: insufficient balance");
(bool success, ) = recipient.call{value: amount}("");
require(success, "Address: unable to send value, recipient may have reverted");
}
function functionCall(address target, bytes memory data) internal returns (bytes memory) {
return functionCall(target, data, "Address: low-level call failed");
}
function functionCall(
address target,
bytes memory data,
string memory errorMessage
) internal returns (bytes memory) {
return functionCallWithValue(target, data, 0, errorMessage);
}
function functionCallWithValue(
address target,
bytes memory data,
uint256 value
) internal returns (bytes memory) {
return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
}
function functionCallWithValue(
address target,
bytes memory data,
uint256 value,
string memory errorMessage
) internal returns (bytes memory) {
require(address(this).balance >= value, "Address: insufficient balance for call");
require(isContract(target), "Address: call to non-contract");
(bool success, bytes memory returndata) = target.call{value: value}(data);
return verifyCallResult(success, returndata, errorMessage);
}
function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
return functionStaticCall(target, data, "Address: low-level static call failed");
}
function functionStaticCall(
address target,
bytes memory data,
string memory errorMessage
) internal view returns (bytes memory) {
require(isContract(target), "Address: static call to non-contract");
(bool success, bytes memory returndata) = target.staticcall(data);
return verifyCallResult(success, returndata, errorMessage);
}
function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
return functionDelegateCall(target, data, "Address: low-level delegate call failed");
}
function functionDelegateCall(
address target,
bytes memory data,
string memory errorMessage
) internal returns (bytes memory) {
require(isContract(target), "Address: delegate call to non-contract");
(bool success, bytes memory returndata) = target.delegatecall(data);
return verifyCallResult(success, returndata, errorMessage);
}
function verifyCallResult(
bool success,
bytes memory returndata,
string memory errorMessage
) internal pure returns (bytes memory) {
if (success) {
return returndata;
} else {
if (returndata.length > 0) {
assembly {
let returndata_size := mload(returndata)
revert(add(32, returndata), returndata_size)
}
} else {
revert(errorMessage);
}
}
}
}
文件 2 的 14: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;
}
}
文件 3 的 14:ECDSA.sol
pragma solidity ^0.8.0;
library ECDSA {
enum RecoverError {
NoError,
InvalidSignature,
InvalidSignatureLength,
InvalidSignatureS,
InvalidSignatureV
}
function _throwError(RecoverError error) private pure {
if (error == RecoverError.NoError) {
return;
} else if (error == RecoverError.InvalidSignature) {
revert("ECDSA: invalid signature");
} else if (error == RecoverError.InvalidSignatureLength) {
revert("ECDSA: invalid signature length");
} else if (error == RecoverError.InvalidSignatureS) {
revert("ECDSA: invalid signature 's' value");
} else if (error == RecoverError.InvalidSignatureV) {
revert("ECDSA: invalid signature 'v' value");
}
}
function tryRecover(bytes32 hash, bytes memory signature) internal pure returns (address, RecoverError) {
if (signature.length == 65) {
bytes32 r;
bytes32 s;
uint8 v;
assembly {
r := mload(add(signature, 0x20))
s := mload(add(signature, 0x40))
v := byte(0, mload(add(signature, 0x60)))
}
return tryRecover(hash, v, r, s);
} else if (signature.length == 64) {
bytes32 r;
bytes32 vs;
assembly {
r := mload(add(signature, 0x20))
vs := mload(add(signature, 0x40))
}
return tryRecover(hash, r, vs);
} else {
return (address(0), RecoverError.InvalidSignatureLength);
}
}
function recover(bytes32 hash, bytes memory signature) internal pure returns (address) {
(address recovered, RecoverError error) = tryRecover(hash, signature);
_throwError(error);
return recovered;
}
function tryRecover(
bytes32 hash,
bytes32 r,
bytes32 vs
) internal pure returns (address, RecoverError) {
bytes32 s;
uint8 v;
assembly {
s := and(vs, 0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff)
v := add(shr(255, vs), 27)
}
return tryRecover(hash, v, r, s);
}
function recover(
bytes32 hash,
bytes32 r,
bytes32 vs
) internal pure returns (address) {
(address recovered, RecoverError error) = tryRecover(hash, r, vs);
_throwError(error);
return recovered;
}
function tryRecover(
bytes32 hash,
uint8 v,
bytes32 r,
bytes32 s
) internal pure returns (address, RecoverError) {
if (uint256(s) > 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0) {
return (address(0), RecoverError.InvalidSignatureS);
}
if (v != 27 && v != 28) {
return (address(0), RecoverError.InvalidSignatureV);
}
address signer = ecrecover(hash, v, r, s);
if (signer == address(0)) {
return (address(0), RecoverError.InvalidSignature);
}
return (signer, RecoverError.NoError);
}
function recover(
bytes32 hash,
uint8 v,
bytes32 r,
bytes32 s
) internal pure returns (address) {
(address recovered, RecoverError error) = tryRecover(hash, v, r, s);
_throwError(error);
return recovered;
}
function toEthSignedMessageHash(bytes32 hash) internal pure returns (bytes32) {
return keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n32", hash));
}
function toTypedDataHash(bytes32 domainSeparator, bytes32 structHash) internal pure returns (bytes32) {
return keccak256(abi.encodePacked("\x19\x01", domainSeparator, structHash));
}
}
文件 4 的 14:EIP712Whitelisting.sol
pragma solidity ^0.8.0;
import '@openzeppelin/contracts/utils/cryptography/ECDSA.sol';
import '@openzeppelin/contracts/access/Ownable.sol';
contract EIP712Whitelisting is Ownable {
using ECDSA for bytes32;
address whitelistSigningKey = address(0);
bytes32 internal DOMAIN_SEPARATOR;
bytes32 internal constant MINTER_TYPEHASH = keccak256('Minter(address wallet)');
bytes32 internal constant DOMAIN_TYPEHASH =
keccak256('EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)');
constructor() {
DOMAIN_SEPARATOR = keccak256(
abi.encode(
DOMAIN_TYPEHASH,
keccak256(bytes('RingsForLootWhitelistToken')),
keccak256(bytes('1')),
block.chainid,
address(this)
)
);
}
function setWhitelistSigningAddress(address newSigningKey) public onlyOwner {
whitelistSigningKey = newSigningKey;
}
function requiresWhitelist(bytes calldata signature) internal view {
require(whitelistSigningKey != address(0), 'Whitelist not enabled');
bytes32 digest = keccak256(
abi.encodePacked('\x19\x01', DOMAIN_SEPARATOR, keccak256(abi.encode(MINTER_TYPEHASH, msg.sender)))
);
address recoveredAddress = digest.recover(signature);
require(recoveredAddress == whitelistSigningKey, 'Invalid Signature');
}
}
文件 5 的 14:ERC1155.sol
pragma solidity ^0.8.0;
import "./IERC1155.sol";
import "./IERC1155Receiver.sol";
import "./extensions/IERC1155MetadataURI.sol";
import "../../utils/Address.sol";
import "../../utils/Context.sol";
import "../../utils/introspection/ERC165.sol";
contract ERC1155 is Context, ERC165, IERC1155, IERC1155MetadataURI {
using Address for address;
mapping(uint256 => mapping(address => uint256)) private _balances;
mapping(address => mapping(address => bool)) private _operatorApprovals;
string private _uri;
constructor(string memory uri_) {
_setURI(uri_);
}
function supportsInterface(bytes4 interfaceId) public view virtual override(ERC165, IERC165) returns (bool) {
return
interfaceId == type(IERC1155).interfaceId ||
interfaceId == type(IERC1155MetadataURI).interfaceId ||
super.supportsInterface(interfaceId);
}
function uri(uint256) public view virtual override returns (string memory) {
return _uri;
}
function balanceOf(address account, uint256 id) public view virtual override returns (uint256) {
require(account != address(0), "ERC1155: balance query for the zero address");
return _balances[id][account];
}
function balanceOfBatch(address[] memory accounts, uint256[] memory ids)
public
view
virtual
override
returns (uint256[] memory)
{
require(accounts.length == ids.length, "ERC1155: accounts and ids length mismatch");
uint256[] memory batchBalances = new uint256[](accounts.length);
for (uint256 i = 0; i < accounts.length; ++i) {
batchBalances[i] = balanceOf(accounts[i], ids[i]);
}
return batchBalances;
}
function setApprovalForAll(address operator, bool approved) public virtual override {
require(_msgSender() != operator, "ERC1155: setting approval status for self");
_operatorApprovals[_msgSender()][operator] = approved;
emit ApprovalForAll(_msgSender(), operator, approved);
}
function isApprovedForAll(address account, address operator) public view virtual override returns (bool) {
return _operatorApprovals[account][operator];
}
function safeTransferFrom(
address from,
address to,
uint256 id,
uint256 amount,
bytes memory data
) public virtual override {
require(
from == _msgSender() || isApprovedForAll(from, _msgSender()),
"ERC1155: caller is not owner nor approved"
);
_safeTransferFrom(from, to, id, amount, data);
}
function safeBatchTransferFrom(
address from,
address to,
uint256[] memory ids,
uint256[] memory amounts,
bytes memory data
) public virtual override {
require(
from == _msgSender() || isApprovedForAll(from, _msgSender()),
"ERC1155: transfer caller is not owner nor approved"
);
_safeBatchTransferFrom(from, to, ids, amounts, data);
}
function _safeTransferFrom(
address from,
address to,
uint256 id,
uint256 amount,
bytes memory data
) internal virtual {
require(to != address(0), "ERC1155: transfer to the zero address");
address operator = _msgSender();
_beforeTokenTransfer(operator, from, to, _asSingletonArray(id), _asSingletonArray(amount), data);
uint256 fromBalance = _balances[id][from];
require(fromBalance >= amount, "ERC1155: insufficient balance for transfer");
unchecked {
_balances[id][from] = fromBalance - amount;
}
_balances[id][to] += amount;
emit TransferSingle(operator, from, to, id, amount);
_doSafeTransferAcceptanceCheck(operator, from, to, id, amount, data);
}
function _safeBatchTransferFrom(
address from,
address to,
uint256[] memory ids,
uint256[] memory amounts,
bytes memory data
) internal virtual {
require(ids.length == amounts.length, "ERC1155: ids and amounts length mismatch");
require(to != address(0), "ERC1155: transfer to the zero address");
address operator = _msgSender();
_beforeTokenTransfer(operator, from, to, ids, amounts, data);
for (uint256 i = 0; i < ids.length; ++i) {
uint256 id = ids[i];
uint256 amount = amounts[i];
uint256 fromBalance = _balances[id][from];
require(fromBalance >= amount, "ERC1155: insufficient balance for transfer");
unchecked {
_balances[id][from] = fromBalance - amount;
}
_balances[id][to] += amount;
}
emit TransferBatch(operator, from, to, ids, amounts);
_doSafeBatchTransferAcceptanceCheck(operator, from, to, ids, amounts, data);
}
function _setURI(string memory newuri) internal virtual {
_uri = newuri;
}
function _mint(
address account,
uint256 id,
uint256 amount,
bytes memory data
) internal virtual {
require(account != address(0), "ERC1155: mint to the zero address");
address operator = _msgSender();
_beforeTokenTransfer(operator, address(0), account, _asSingletonArray(id), _asSingletonArray(amount), data);
_balances[id][account] += amount;
emit TransferSingle(operator, address(0), account, id, amount);
_doSafeTransferAcceptanceCheck(operator, address(0), account, id, amount, data);
}
function _mintBatch(
address to,
uint256[] memory ids,
uint256[] memory amounts,
bytes memory data
) internal virtual {
require(to != address(0), "ERC1155: mint to the zero address");
require(ids.length == amounts.length, "ERC1155: ids and amounts length mismatch");
address operator = _msgSender();
_beforeTokenTransfer(operator, address(0), to, ids, amounts, data);
for (uint256 i = 0; i < ids.length; i++) {
_balances[ids[i]][to] += amounts[i];
}
emit TransferBatch(operator, address(0), to, ids, amounts);
_doSafeBatchTransferAcceptanceCheck(operator, address(0), to, ids, amounts, data);
}
function _burn(
address account,
uint256 id,
uint256 amount
) internal virtual {
require(account != address(0), "ERC1155: burn from the zero address");
address operator = _msgSender();
_beforeTokenTransfer(operator, account, address(0), _asSingletonArray(id), _asSingletonArray(amount), "");
uint256 accountBalance = _balances[id][account];
require(accountBalance >= amount, "ERC1155: burn amount exceeds balance");
unchecked {
_balances[id][account] = accountBalance - amount;
}
emit TransferSingle(operator, account, address(0), id, amount);
}
function _burnBatch(
address account,
uint256[] memory ids,
uint256[] memory amounts
) internal virtual {
require(account != address(0), "ERC1155: burn from the zero address");
require(ids.length == amounts.length, "ERC1155: ids and amounts length mismatch");
address operator = _msgSender();
_beforeTokenTransfer(operator, account, address(0), ids, amounts, "");
for (uint256 i = 0; i < ids.length; i++) {
uint256 id = ids[i];
uint256 amount = amounts[i];
uint256 accountBalance = _balances[id][account];
require(accountBalance >= amount, "ERC1155: burn amount exceeds balance");
unchecked {
_balances[id][account] = accountBalance - amount;
}
}
emit TransferBatch(operator, account, address(0), ids, amounts);
}
function _beforeTokenTransfer(
address operator,
address from,
address to,
uint256[] memory ids,
uint256[] memory amounts,
bytes memory data
) internal virtual {}
function _doSafeTransferAcceptanceCheck(
address operator,
address from,
address to,
uint256 id,
uint256 amount,
bytes memory data
) private {
if (to.isContract()) {
try IERC1155Receiver(to).onERC1155Received(operator, from, id, amount, data) returns (bytes4 response) {
if (response != IERC1155Receiver.onERC1155Received.selector) {
revert("ERC1155: ERC1155Receiver rejected tokens");
}
} catch Error(string memory reason) {
revert(reason);
} catch {
revert("ERC1155: transfer to non ERC1155Receiver implementer");
}
}
}
function _doSafeBatchTransferAcceptanceCheck(
address operator,
address from,
address to,
uint256[] memory ids,
uint256[] memory amounts,
bytes memory data
) private {
if (to.isContract()) {
try IERC1155Receiver(to).onERC1155BatchReceived(operator, from, ids, amounts, data) returns (
bytes4 response
) {
if (response != IERC1155Receiver.onERC1155BatchReceived.selector) {
revert("ERC1155: ERC1155Receiver rejected tokens");
}
} catch Error(string memory reason) {
revert(reason);
} catch {
revert("ERC1155: transfer to non ERC1155Receiver implementer");
}
}
}
function _asSingletonArray(uint256 element) private pure returns (uint256[] memory) {
uint256[] memory array = new uint256[](1);
array[0] = element;
return array;
}
}
文件 6 的 14:ERC165.sol
pragma solidity ^0.8.0;
import "./IERC165.sol";
abstract contract ERC165 is IERC165 {
function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
return interfaceId == type(IERC165).interfaceId;
}
}
文件 7 的 14:IERC1155.sol
pragma solidity ^0.8.0;
import "../../utils/introspection/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;
}
文件 8 的 14:IERC1155MetadataURI.sol
pragma solidity ^0.8.0;
import "../IERC1155.sol";
interface IERC1155MetadataURI is IERC1155 {
function uri(uint256 id) external view returns (string memory);
}
文件 9 的 14:IERC1155Receiver.sol
pragma solidity ^0.8.0;
import "../../utils/introspection/IERC165.sol";
interface IERC1155Receiver is IERC165 {
function onERC1155Received(
address operator,
address from,
uint256 id,
uint256 value,
bytes calldata data
) external returns (bytes4);
function onERC1155BatchReceived(
address operator,
address from,
uint256[] calldata ids,
uint256[] calldata values,
bytes calldata data
) external returns (bytes4);
}
文件 10 的 14:IERC165.sol
pragma solidity ^0.8.0;
interface IERC165 {
function supportsInterface(bytes4 interfaceId) external view returns (bool);
}
文件 11 的 14:Ownable.sol
pragma solidity ^0.8.0;
import "../utils/Context.sol";
abstract contract Ownable is Context {
address private _owner;
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
constructor() {
_setOwner(_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 {
_setOwner(address(0));
}
function transferOwnership(address newOwner) public virtual onlyOwner {
require(newOwner != address(0), "Ownable: new owner is the zero address");
_setOwner(newOwner);
}
function _setOwner(address newOwner) private {
address oldOwner = _owner;
_owner = newOwner;
emit OwnershipTransferred(oldOwner, newOwner);
}
}
文件 12 的 14:RingsForLoot.sol
import '@openzeppelin/contracts/access/Ownable.sol';
import '@openzeppelin/contracts/token/ERC1155/ERC1155.sol';
import '@openzeppelin/contracts/utils/cryptography/ECDSA.sol';
import '@openzeppelin/contracts/utils/Strings.sol';
import 'base64-sol/base64.sol';
import './EIP712Whitelisting.sol';
pragma solidity ^0.8.0;
interface ILoot {
function ownerOf(uint256 tokenId) external view returns (address owner);
function balanceOf(address owner) external view returns (uint256 balance);
function tokenOfOwnerByIndex(address owner, uint256 index) external view returns (uint256 tokenId);
function tokenByIndex(uint256 index) external view returns (uint256);
function getRing(uint256 tokenId) external view returns (string memory);
}
interface IERC20 {
function balanceOf(address account) external view returns (uint256);
function transfer(address recipient, uint256 amount) external returns (bool);
}
interface IERC2981 {
function royaltyInfo(uint256 _tokenId, uint256 _salePrice)
external
view
returns (address receiver, uint256 royaltyAmount);
}
interface ProxyRegistry {
function proxies(address) external view returns (address);
}
contract RingsForLoot is ERC1155, IERC2981, Ownable, EIP712Whitelisting {
enum SaleState {
Paused,
OnlyCommon,
Active
}
SaleState public state = SaleState.Paused;
ILoot private ogLootContract;
mapping(ILoot => bool) private lootContracts;
mapping(ILoot => mapping(uint256 => bool)) public bagClaimed;
mapping(uint256 => uint256) private _minted;
string public name = 'Rings for Loot';
string public symbol = 'R4L';
string public ipfs;
uint256[5] private commonIds = [1, 6, 11, 7, 2];
uint256[5] private commonMax = [1093, 1178, 1166, 1163, 1112];
bytes[5] private epicIds;
bytes[5] private epicMax;
bytes[5] private legendaryIds;
bytes[5] private mythicIds;
uint256 private constant PRICE_RING_COMMON = 0.02 ether;
uint256 private constant PRICE_RING_EPIC = 0.06 ether;
uint256 private constant PRICE_RING_LEGENDARY = 0.1 ether;
uint256 private constant PRICE_RING_MYTHIC = 0.14 ether;
uint256 private constant PRICE_FORGE_EPIC = 0.02 ether;
uint256 private constant PRICE_FORGE_LEGENDARY = 0.04 ether;
uint256 private constant PRICE_FORGE_MYTHIC = 0.06 ether;
mapping(address => bool) public whitelistUsed;
constructor(ILoot[] memory lootsList) ERC1155('') {
for (uint256 i = 0; i < lootsList.length; i++) {
if (i == 0) {
ogLootContract = lootsList[i];
}
lootContracts[lootsList[i]] = true;
}
epicIds[0] = hex'00030009000d0016002300330078007d00b1011001ad022e02da038d03f604e0';
epicIds[1] = hex'002100a600d50156015c0174018301ba024402b702d002d40305032c03ab0429';
epicIds[2] = hex'002b0059006500730086009a00b600f50150016e01d502050209026302f8032f';
epicIds[3] = hex'00270031003400490064008a008b00b9012b0133016b017a026f0276028b0615';
epicIds[4] = hex'0024002e003c004300450047005c009c009e00b300d4010b018a01f202f40350';
epicMax[0] = hex'111412150f151113111a0f0d120f160b';
epicMax[1] = hex'16131c0f15120f1a0d180d0911111417';
epicMax[2] = hex'1712150f1412140d1014111011101118';
epicMax[3] = hex'1513120f1b1814130f1515161b13110e';
epicMax[4] = hex'19152019131410181a13110a0f121410';
legendaryIds[0] = hex'0151015101b401b4039203920558055807500750';
legendaryIds[1] = hex'00a400a400e900e905e105e10b140b140b5f0b5f132e132e';
legendaryIds[2] = hex'0125012504cd04cd0894089414f614f6';
legendaryIds[3] = hex'01b201b205180518056205620bac0bac16171617';
legendaryIds[4] = hex'02dc02dc06c506c50daa0daa105010501c7b1c7b';
mythicIds[0] = hex'00220123013b014c01e002d20325039603a003f903fe041504310448046c053b0561059305c505ee0638063a064c065a069b06a506c306f907050831083b08a70916095e09a00a120a5a0aa00aac0ab50aed0b1f0b280b340b5d0b890bbb0bc40c710c7a0c820cca0cd30cea0d2e0d340d550ef20f120f200f4e0f540f7d0fa410011014105e1070109f10b310d710e81101110d114b1176118111be11c6124f125812a412fe130d1378139413af13b213ec142d147814a214c914de14df14fd1543157f158115901598159a15a916df16f316ff170b171a175a179717e5187418d818ef18fc190a193c194519461a001a911a961abd1af31b1b1bcd1bd61c441c471c911cbd1cda1d031d651d9c1dfa1e391e6e1e8f1ef51f1c';
mythicIds[1] = hex'0025007e008200c500df016c01ee01fa02010211024202d802f3033c038203b003b303f70416041f042a0434053e056f05a505b605d505db06240653067d06e407380773079c07d2085e086e087c08b508bd08d70914097209d10a500a5b0a9c0ab10abd0b070b180b1d0b6f0b940c2f0c600c750c8d0cfa0d1f0d260d320d910de80e910e9b0eb00ed80edb0ef80f24104a106f107f109510c5110211261134118c11a011a111cc11d71274128a129212af12d712d9133b1343138413881412142c143a149a14c714ed15511580167016c716fb171017421763177817a117f8182e187619181924192b1967198019a719fa1a3f1a4a1a811ada1b111b2a1b4e1b5c1b621ba41bf11c2a1c701d001d161d361d611d731d781dc51dcb1dcf1e141ea31ea71eee1efd1f29';
mythicIds[2] = hex'001d0061007700dc01c4021b0221023c023d024e025e026b02bf02e9030903120358035a036603880424043c04d104dd05c205cc05d3061b06410680069c0766078f07d307d907e907f0082d0844089f08b208d4095709660983098609ac09d209fb0a1b0a4b0b020b660b930bd90c620d1b0d440d8c0db30dc30ddc0ed90f3e0f530f5b0fed0ffc103210bb11c81215132313471350137e13bc13c4140e14331493151b159415de15ec15f015f415fe161b167316a716b117701796179d17cb17e0181818311849189f18a918c918d718ff1956197d19a219d91acd1adc1ae91aea1b361b9c1bec1c871cc11ccd1cd61ce11cf51d141d1b1d311d5a1d621d681d6f1d741d901d9a1dba';
mythicIds[3] = hex'004200bf00d30107010e0116013001da01e102030208021402710273029b02b40359037403a903df041104370460048b04aa04e905510554055e0566057205c105ca05f00635066706b5071007200732076a07b60806086508c508e608fd095b099f0a030a220b7e0b810be20c330c350c380c9d0ca80cd40cd60d010d090d280dbb0df00dfc0e060ede0ee80f410fa810ac114e1150115e11c111ed11f711f912041216121f124112681309130f133f137313be14211427142f1459146e155415a715a815b11606164c168f1706182c189518b918c118e518ed194b198919f51a1f1a671a901ac11ad01b401b681b6f1b721b8a1bda1c6f1c8c1c9c1d3c1d551d661d801d9d1dea1def1e0b1e111e181e341e941ea51eb71ebd1ed11f0c1f16';
mythicIds[4] = hex'00380044006b00810104011101680170018501bd01c202190288028d02f002ff032b03720390042c045c04b204e60603061d063c064506b806c606db06e5071f07480765078207de082f088008f1090b09250943095f098b09ae09bc09cd09ea0a6d0a920a9f0aa30b210b7f0b960ba60bc90bcd0bcf0be40bf70bfa0c3a0c450c560d5d0de20e400e820e8b0e9d0eae0ec90ef70f520f7e0f910fb20fc40fd5103710561061110c115311641183118711a211fe1208124b131813441349136e13a613aa13cb13df147a14c214d91503150d15341629163f170c17b817f21816184c1856188618a3190e191a191b19341957197a1a171a321a371a511a601a691aa41ad31b2f1b3b1b791ba21beb1c341c7d1ce01d081d121d1f1d201d451d471d8e1e151e471ec91ed51eda1f041f131f1b';
}
function purchaseCommon(uint256 amount, bytes calldata signature) public payable {
require(state == SaleState.OnlyCommon || state == SaleState.Active, 'Sale not active');
if (signature.length > 0) {
validateGiveawaySignature(signature);
require(amount == 1, 'Can only get one');
require(msg.value == 0, 'Wrong price');
} else {
require(amount > 0, 'Buy at least one');
require(amount <= 26, 'Too many at once');
require(msg.value == amount * PRICE_RING_COMMON, 'Wrong price');
}
require(commonMax[0] + commonMax[1] + commonMax[2] + commonMax[3] + commonMax[4] >= amount, 'Not enough left');
uint256[5] memory amounts;
uint256 rand = uint256(keccak256(abi.encodePacked(block.timestamp, msg.sender)));
for (uint256 i = 0; i < amount; i++) {
while (true) {
require(rand > 0, 'ran out of randomness');
uint256 color = rand % 5;
rand += 1;
if (commonMax[color] > 0) {
amounts[color] += 1;
commonMax[color] -= 1;
break;
}
}
rand /= 5;
}
for (uint256 i = 0; i < 5; i++) {
if (amounts[i] > 0) {
_minted[commonIds[i]] += amounts[i];
_mint(msg.sender, commonIds[i], amounts[i], '');
}
}
}
function purchaseMatching(
ILoot loot,
uint256 bagId,
uint256 ringId,
bytes calldata signature
) public payable {
require(lootContracts[loot], 'Not compatible');
require(loot.ownerOf(bagId) == msg.sender, 'Not owner');
require(
keccak256(abi.encodePacked(loot.getRing(bagId))) ==
keccak256(abi.encodePacked(ogLootContract.getRing(ringId))),
'Wrong ring'
);
require(!bagClaimed[loot][bagId], 'Already claimed');
bagClaimed[loot][bagId] = true;
uint256 price;
uint256 rand = uint256(keccak256(abi.encodePacked('RING', Strings.toString(ringId))));
uint256 greatness = rand % 21;
uint256 color = rand % 5;
require(state == SaleState.Active || (state == SaleState.OnlyCommon && greatness <= 14), 'Sale not active');
if (greatness <= 14) {
require(commonMax[color] > 0, 'Not in stock');
price = PRICE_RING_COMMON;
commonMax[color] -= 1;
} else if (greatness < 19) {
price = PRICE_RING_EPIC;
(bool found, uint256 index) = findRingIndex(epicIds[color], ringId);
require(found, 'Not in stock');
uint8 max = uint8(epicMax[color][index]);
max -= 1;
if (max > 0) {
epicMax[color][index] = bytes1(max);
} else {
removeUint16At(epicIds[color], index * 2);
epicMax[color][index] = epicMax[color][epicMax[color].length - 1];
epicMax[color].pop();
}
} else {
(bool found, uint256 index) = findRingIndex(legendaryIds[color], ringId);
if (found) {
price = PRICE_RING_LEGENDARY;
removeUint16At(legendaryIds[color], index * 2);
} else {
price = PRICE_RING_MYTHIC;
(found, index) = findRingIndex(mythicIds[color], ringId);
require(found, 'Not in stock');
removeUint16At(mythicIds[color], index * 2);
}
}
if (signature.length > 0) {
validateGiveawaySignature(signature);
price = 0;
}
require(msg.value == price, 'Wrong price');
_minted[ringId] += 1;
_mint(msg.sender, ringId, 1, '');
}
function forge(uint256 color, uint256 amount) public payable {
require(state == SaleState.Active, 'Sale not active');
require(color < 5, 'Not a common ring');
uint256 ringIdToBurn = commonIds[color];
bytes storage data;
if (amount == 2) {
require(msg.value == PRICE_FORGE_EPIC, 'Wrong price');
data = epicIds[color];
} else if (amount == 3) {
require(msg.value == PRICE_FORGE_LEGENDARY, 'Wrong price');
data = legendaryIds[color];
} else if (amount == 4) {
require(msg.value == PRICE_FORGE_MYTHIC, 'Wrong price');
data = mythicIds[color];
} else {
revert('Wrong amount of rings to burn');
}
(uint256 ringIdToMint, uint256 index) = pickRandomRing(data);
uint256 ringsLeft;
if (amount == 2) {
ringsLeft = uint8(epicMax[color][index / 2]) - 1;
epicMax[color][index / 2] = bytes1(uint8(ringsLeft));
}
if (ringsLeft == 0) {
removeUint16At(data, index);
if (amount == 2) {
epicMax[color][index / 2] = epicMax[color][epicMax[color].length - 1];
epicMax[color].pop();
}
}
_minted[ringIdToMint] += 1;
_burn(msg.sender, ringIdToBurn, amount);
_mint(msg.sender, ringIdToMint, 1, '');
}
function mintedBatched(uint256[] calldata ids) public view returns (uint256[] memory counts) {
counts = new uint256[](ids.length);
for (uint256 i = 0; i < ids.length; i++) {
counts[i] = _minted[ids[i]];
}
}
function uri(uint256 tokenId) public view override returns (string memory) {
require(_minted[tokenId] > 0, 'Ring does not exist');
bytes memory ringName = bytes(ogLootContract.getRing(tokenId));
if (uint8(ringName[0]) == 34) {
bytes memory escRingName = new bytes(ringName.length + 2);
uint256 ei = 0;
for (uint256 i = 0; i < ringName.length; i++) {
if (uint8(ringName[i]) == 34) {
escRingName[ei++] = bytes1(uint8(92));
}
escRingName[ei++] = ringName[i];
}
ringName = escRingName;
}
string memory json = Base64.encode(
bytes(
string(
abi.encodePacked(
'{"name": "',
ringName,
'", "description": "Rings (for Loot) is the first and largest 3D interpretation of an entire category in Loot. Adventurers, builders, and artists are encouraged to reference Rings (for Loot) to further expand on the imagination of Loot.", "image": "ipfs://',
ipfs,
'/',
Strings.toString(tokenId),
'.jpg"}'
)
)
)
);
return string(abi.encodePacked('data:application/json;base64,', json));
}
function pickRandomRing(bytes storage data) internal view returns (uint256 result, uint256 index) {
require(data.length > 0, 'data is empty');
uint256 rand = uint256(keccak256(abi.encodePacked(block.timestamp)));
index = rand % data.length;
index -= (index % 2);
result = readUint16At(data, index);
}
function findRingIndex(bytes storage data, uint256 ringId) internal view returns (bool found, uint256 index) {
for (uint256 i = 0; i < data.length / 2; i++) {
if (uint8(data[i * 2]) == ((ringId >> 8) & 0xFF) && uint8(data[i * 2 + 1]) == (ringId & 0xFF)) {
return (true, i);
}
}
return (false, 0);
}
function readUint16At(bytes storage data, uint256 index) internal view returns (uint16 result) {
result = (uint16(uint8(data[index])) << 8) + uint8(data[index + 1]);
}
function writeUint16At(
bytes storage data,
uint256 index,
uint16 value
) internal {
data[index] = bytes1(uint8(value >> 8));
data[index + 1] = bytes1(uint8(value & 0xFF));
}
function removeUint16At(bytes storage data, uint256 index) internal {
require(data.length > 0, 'data is empty');
data[index] = data[data.length - 2];
data[index + 1] = data[data.length - 1];
data.pop();
data.pop();
}
function validateGiveawaySignature(bytes calldata signature) internal returns (bool) {
requiresWhitelist(signature);
require(!whitelistUsed[msg.sender], 'Already used');
whitelistUsed[msg.sender] = true;
return true;
}
function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
return interfaceId == type(IERC2981).interfaceId || super.supportsInterface(interfaceId);
}
function royaltyInfo(uint256, uint256 salePrice) external view returns (address receiver, uint256 royaltyAmount) {
receiver = owner();
royaltyAmount = (salePrice * 5) / 100;
}
function isApprovedForAll(address owner, address operator) public view override returns (bool) {
if (block.chainid == 4) {
if (ProxyRegistry(0xF57B2c51dED3A29e6891aba85459d600256Cf317).proxies(owner) == operator) {
return true;
}
} else if (block.chainid == 1) {
if (ProxyRegistry(0xa5409ec958C83C3f309868babACA7c86DCB077c1).proxies(owner) == operator) {
return true;
}
}
return ERC1155.isApprovedForAll(owner, operator);
}
function setState(SaleState newState) public onlyOwner {
state = newState;
}
function setIpfs(string calldata newIpfs) public onlyOwner {
ipfs = newIpfs;
}
function withdrawAll() public payable onlyOwner {
require(payable(msg.sender).send(address(this).balance));
}
function withdrawAllERC20(IERC20 erc20Token) public onlyOwner {
require(erc20Token.transfer(msg.sender, erc20Token.balanceOf(address(this))));
}
}
文件 13 的 14:Strings.sol
pragma solidity ^0.8.0;
library Strings {
bytes16 private constant _HEX_SYMBOLS = "0123456789abcdef";
function toString(uint256 value) internal pure returns (string memory) {
if (value == 0) {
return "0";
}
uint256 temp = value;
uint256 digits;
while (temp != 0) {
digits++;
temp /= 10;
}
bytes memory buffer = new bytes(digits);
while (value != 0) {
digits -= 1;
buffer[digits] = bytes1(uint8(48 + uint256(value % 10)));
value /= 10;
}
return string(buffer);
}
function toHexString(uint256 value) internal pure returns (string memory) {
if (value == 0) {
return "0x00";
}
uint256 temp = value;
uint256 length = 0;
while (temp != 0) {
length++;
temp >>= 8;
}
return toHexString(value, length);
}
function toHexString(uint256 value, uint256 length) internal pure returns (string memory) {
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_SYMBOLS[value & 0xf];
value >>= 4;
}
require(value == 0, "Strings: hex length insufficient");
return string(buffer);
}
}
文件 14 的 14:base64.sol
pragma solidity >=0.6.0;
library Base64 {
string internal constant TABLE_ENCODE = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
bytes internal constant TABLE_DECODE = hex"0000000000000000000000000000000000000000000000000000000000000000"
hex"00000000000000000000003e0000003f3435363738393a3b3c3d000000000000"
hex"00000102030405060708090a0b0c0d0e0f101112131415161718190000000000"
hex"001a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132330000000000";
function encode(bytes memory data) internal pure returns (string memory) {
if (data.length == 0) return '';
string memory table = TABLE_ENCODE;
uint256 encodedLen = 4 * ((data.length + 2) / 3);
string memory result = new string(encodedLen + 32);
assembly {
mstore(result, encodedLen)
let tablePtr := add(table, 1)
let dataPtr := data
let endPtr := add(dataPtr, mload(data))
let resultPtr := add(result, 32)
for {} 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 { mstore(sub(resultPtr, 2), shl(240, 0x3d3d)) }
case 2 { mstore(sub(resultPtr, 1), shl(248, 0x3d)) }
}
return result;
}
function decode(string memory _data) internal pure returns (bytes memory) {
bytes memory data = bytes(_data);
if (data.length == 0) return new bytes(0);
require(data.length % 4 == 0, "invalid base64 decoder input");
bytes memory table = TABLE_DECODE;
uint256 decodedLen = (data.length / 4) * 3;
bytes memory result = new bytes(decodedLen + 32);
assembly {
let lastBytes := mload(add(data, mload(data)))
if eq(and(lastBytes, 0xFF), 0x3d) {
decodedLen := sub(decodedLen, 1)
if eq(and(lastBytes, 0xFFFF), 0x3d3d) {
decodedLen := sub(decodedLen, 1)
}
}
mstore(result, decodedLen)
let tablePtr := add(table, 1)
let dataPtr := data
let endPtr := add(dataPtr, mload(data))
let resultPtr := add(result, 32)
for {} lt(dataPtr, endPtr) {}
{
dataPtr := add(dataPtr, 4)
let input := mload(dataPtr)
let output := add(
add(
shl(18, and(mload(add(tablePtr, and(shr(24, input), 0xFF))), 0xFF)),
shl(12, and(mload(add(tablePtr, and(shr(16, input), 0xFF))), 0xFF))),
add(
shl( 6, and(mload(add(tablePtr, and(shr( 8, input), 0xFF))), 0xFF)),
and(mload(add(tablePtr, and( input , 0xFF))), 0xFF)
)
)
mstore(resultPtr, shl(232, output))
resultPtr := add(resultPtr, 3)
}
}
return result;
}
}
{
"compilationTarget": {
"contracts/RingsForLoot.sol": "RingsForLoot"
},
"evmVersion": "london",
"libraries": {},
"metadata": {
"bytecodeHash": "ipfs"
},
"optimizer": {
"enabled": true,
"runs": 1000
},
"remappings": []
}
[{"inputs":[{"internalType":"contract ILoot[]","name":"lootsList","type":"address[]"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"account","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":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"operator","type":"address"},{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256[]","name":"ids","type":"uint256[]"},{"indexed":false,"internalType":"uint256[]","name":"values","type":"uint256[]"}],"name":"TransferBatch","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"operator","type":"address"},{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"id","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"TransferSingle","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"string","name":"value","type":"string"},{"indexed":true,"internalType":"uint256","name":"id","type":"uint256"}],"name":"URI","type":"event"},{"inputs":[{"internalType":"contract ILoot","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"bagClaimed","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"},{"internalType":"uint256","name":"id","type":"uint256"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address[]","name":"accounts","type":"address[]"},{"internalType":"uint256[]","name":"ids","type":"uint256[]"}],"name":"balanceOfBatch","outputs":[{"internalType":"uint256[]","name":"","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"color","type":"uint256"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"forge","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"ipfs","outputs":[{"internalType":"string","name":"","type":"string"}],"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":"ids","type":"uint256[]"}],"name":"mintedBatched","outputs":[{"internalType":"uint256[]","name":"counts","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"bytes","name":"signature","type":"bytes"}],"name":"purchaseCommon","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"contract ILoot","name":"loot","type":"address"},{"internalType":"uint256","name":"bagId","type":"uint256"},{"internalType":"uint256","name":"ringId","type":"uint256"},{"internalType":"bytes","name":"signature","type":"bytes"}],"name":"purchaseMatching","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"salePrice","type":"uint256"}],"name":"royaltyInfo","outputs":[{"internalType":"address","name":"receiver","type":"address"},{"internalType":"uint256","name":"royaltyAmount","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256[]","name":"ids","type":"uint256[]"},{"internalType":"uint256[]","name":"amounts","type":"uint256[]"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"safeBatchTransferFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"id","type":"uint256"},{"internalType":"uint256","name":"amount","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":[{"internalType":"string","name":"newIpfs","type":"string"}],"name":"setIpfs","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"enum RingsForLoot.SaleState","name":"newState","type":"uint8"}],"name":"setState","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newSigningKey","type":"address"}],"name":"setWhitelistSigningAddress","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"state","outputs":[{"internalType":"enum RingsForLoot.SaleState","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":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"uri","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"whitelistUsed","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"withdrawAll","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"contract IERC20","name":"erc20Token","type":"address"}],"name":"withdrawAllERC20","outputs":[],"stateMutability":"nonpayable","type":"function"}]