Skip to content

Instantly share code, notes, and snippets.

@Sambillingham
Created May 20, 2023 18:35
Show Gist options
  • Save Sambillingham/b35b1204e7d11f3557e68cf5b92a2e7c to your computer and use it in GitHub Desktop.
Save Sambillingham/b35b1204e7d11f3557e68cf5b92a2e7c to your computer and use it in GitHub Desktop.
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.12;
import "solmate/src/auth/Owned.sol";
import "solmate/src/tokens/ERC1155.sol";
import "solmate/src/utils/LibString.sol";
import "@openzeppelin/contracts/utils/cryptography/ECDSA.sol";
interface IERC721 {
function burn(uint256 tokenId) external;
}
contract HexapodComponents is ERC1155, Owned {
using LibString for uint256;
string public name = 'HOLO COMPONENTS';
string public symbol = 'HHC';
string baseURI = 'https://hexapod.industries/api/components/';
address hexapod;
bool OPENEDITION_LIVE = false;
bool REROLL_LIVE = false;
uint16 MIN_ID = 1;
uint16 MAX_ID = 100;
uint256 PRICE = 0.0025 ether;
uint256 RECYCLE_PRICE = 0.00125 ether;
mapping(address => bool) public allowedAddresses;
mapping(uint256 => mapping(uint256 => bool)) public MintPerHoloId;
mapping(uint256 => bytes) public DeconstructValues;
error Unauthorized();
error NotOpen();
error InsufficientValue();
error InvalidSignature();
error OneTimeOnly();
error LengthMismatch();
constructor(address _hexapod) Owned(msg.sender) {
hexapod = _hexapod;
}
function flipAllowedAddress(address _address) external onlyOwner() {
allowedAddresses[_address] = !allowedAddresses[_address];
}
function mintBatchExternal(address to, uint256[] memory ids, uint256[] memory amounts) external {
if (!allowedAddresses[msg.sender]) revert Unauthorized();
_batchMint(to, ids, amounts, '');
}
function burnBatchExternal(address from, uint256[] memory ids, uint256[] memory amounts) external {
if (!allowedAddresses[msg.sender]) revert Unauthorized();
_batchBurn(from, ids, amounts);
}
function setURI(string calldata newBaseUri) external onlyOwner() {
baseURI = newBaseUri;
}
function uri(uint256 id) public view override returns (string memory) {
return string.concat(baseURI, id.toString());
}
function setValues(bool _status, bool _reroll, uint16 _min, uint16 _max, uint256 _price, uint256 _rprice) external onlyOwner() {
OPENEDITION_LIVE = _status;
REROLL_LIVE = _reroll;
MIN_ID = _min;
MAX_ID = _max;
PRICE = _price;
RECYCLE_PRICE = _rprice;
}
function setDeconstructValues(uint256 _id, bytes calldata _values) external onlyOwner() {
DeconstructValues[_id] = _values;
}
function mintBatch(uint256 amount) payable external {
if (!OPENEDITION_LIVE) revert NotOpen();
if (msg.value < PRICE * amount) revert InsufficientValue();
internalmint(amount);
}
function mintWithSignature(
bytes calldata signature,
uint256 holoId,
uint256 compId
) external payable {
if (recoverSigner(msg.sender, signature, holoId, compId) != owner) revert InvalidSignature();
if (MintPerHoloId[holoId][compId] == true) revert OneTimeOnly();
MintPerHoloId[holoId][compId] = true;
_mint(msg.sender, compId, 1, '');
}
function deconstructHolo(bytes calldata signature, uint256 holoId, uint256 reactLevel, uint256 groupId) external {
if (recoverSigner(msg.sender, signature, holoId, reactLevel, groupId) != owner) revert InvalidSignature();
uint256[] memory amounts = new uint256[](reactLevel);
uint256[] memory ids = new uint256[](reactLevel);
for (uint256 i = 0; i < reactLevel;) {
ids[i] = uint8(DeconstructValues[groupId][i]);
amounts[i] = 1;
unchecked {
++i;
}
}
IERC721(hexapod).burn(holoId);
_batchMint(msg.sender, ids, amounts, '');
}
function recompBatch(uint256[] memory ids, uint256[] memory amounts) external {
if (!REROLL_LIVE) revert NotOpen();
if (ids.length != amounts.length) revert LengthMismatch();
uint256 halfTotal = calcTotal(amounts) / 2;
_batchBurn(msg.sender, ids, amounts );
internalmint(halfTotal);
}
function recycleBatch(uint256[] memory ids, uint256[] memory amounts) payable external {
if (!REROLL_LIVE) revert NotOpen();
uint256 total = calcTotal(amounts);
if (msg.value < RECYCLE_PRICE * total) revert InsufficientValue();
_batchBurn(msg.sender, ids, amounts );
internalmint(total);
}
function internalmint(uint256 amount) internal {
uint256[] memory amounts = new uint256[](amount);
uint256[] memory ids = new uint256[](amount);
for (uint256 i = 0; i < amount;) {
ids[i] = (random(i) % (MAX_ID-MIN_ID) ) + MIN_ID;
amounts[i] = 1;
unchecked {
++i;
}
}
_batchMint(msg.sender, ids, amounts, '');
}
function random(uint256 i) internal view returns (uint256) {
return uint256(keccak256(abi.encodePacked(
tx.origin,
blockhash(block.number - i),
block.timestamp * i
)));
}
function recoverSigner(
address _address,
bytes memory _signature,
uint256 holoId,
uint256 compId
) public pure returns (address) {
bytes32 messageDigest = keccak256(
abi.encodePacked(
"\x19Ethereum Signed Message:\n32",
keccak256(abi.encodePacked(_address, holoId, compId))
)
);
return ECDSA.recover(messageDigest, _signature);
}
function recoverSigner(
address _address,
bytes memory _signature,
uint256 holoId,
uint256 reactLevel,
uint256 groupId
) public pure returns (address) {
bytes32 messageDigest = keccak256(
abi.encodePacked(
"\x19Ethereum Signed Message:\n32",
keccak256(abi.encodePacked(_address, holoId, reactLevel, groupId))
)
);
return ECDSA.recover(messageDigest, _signature);
}
function calcTotal(uint256[] memory amounts) internal pure returns (uint256) {
uint256 total;
for (uint256 i = 0; i < amounts.length;) {
total += amounts[i];
unchecked {
++i;
}
}
return total;
}
function withdraw() external onlyOwner() {
(bool success, ) = (msg.sender).call{ value: address(this).balance }('');
require(success, "FAILED");
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment