文件 1 的 7:ChimeraPillarsMinter.sol
pragma solidity ^0.8.0;
import "./Blimpie/Delegated.sol";
import "./Blimpie/Verify.sol";
interface IChimeraPillars {
function mintTo (uint16[] calldata quantity, address[] calldata recipient) external payable;
}
interface IPartner {
function balanceOf( address account ) external returns( uint );
}
struct Partner {
string name;
address contractAddress;
uint256 price;
string slug;
uint16 minBalance;
bool isActive;
bool isMatic;
bool isOS;
}
contract ChimeraPillarsMinter is Delegated, Verify {
bool public isActive = false;
Partner[] public partners;
IChimeraPillars public ChimeraPillars = IChimeraPillars(0x6f3B255eFA6b2d4133c4F208E98E330e8CaF86f3);
constructor (address _signer) {
setSigner(_signer);
}
function mint (uint16 _quantity, uint256 _partnerId, bytes memory _signature) external payable {
require(isActive, "Mint is not active.");
require(partners[_partnerId].contractAddress != address(0), "Invalid partner.");
Partner storage partner = partners[_partnerId];
require(partner.isActive, "Partner mint is not active.");
require(msg.value >= partner.price * _quantity, "Invalid ETH sent.");
if (partner.isOS || partner.isMatic) {
require(verify(_quantity, _partnerId, _signature), "Invalid signature.");
} else {
IPartner partnerContract = IPartner(partner.contractAddress);
uint256 balance = partnerContract.balanceOf(msg.sender);
require(balance >= partner.minBalance, "Not holding enough partner tokens for discount.");
}
uint16[] memory quantity = new uint16[](1);
quantity[0] = _quantity;
address[] memory recipient = new address[](1);
recipient[0] = msg.sender;
ChimeraPillars.mintTo{ value: msg.value }(quantity, recipient);
}
function setIsActive (bool _isActive) external onlyDelegates {
isActive = _isActive;
}
function setChimeraPillars (IChimeraPillars principal) external onlyDelegates {
ChimeraPillars = principal;
}
function addPartner (
string calldata _name,
address _contractAddress,
uint256 _price,
string calldata _slug,
uint16 _minBalance,
bool _isActive,
bool _isMatic,
bool _isOS
) external onlyDelegates {
Partner memory partner = Partner({
name: _name,
contractAddress: _contractAddress,
price: _price,
slug: _slug,
minBalance: _minBalance,
isActive: _isActive,
isMatic: _isMatic,
isOS: _isOS
});
partners.push(partner);
}
function editPartner (
uint256 _id,
string calldata _name,
address _contractAddress,
uint256 _price,
string calldata _slug,
uint16 _minBalance,
bool _isActive,
bool _isMatic,
bool _isOS
) external onlyDelegates {
require(partners[_id].contractAddress != address(0), "Invalid partner." );
Partner storage partner = partners[_id];
partner.name = _name;
partner.contractAddress = _contractAddress;
partner.price = _price;
partner.slug = _slug;
partner.minBalance = _minBalance;
partner.isActive = _isActive;
partner.isMatic = _isMatic;
partner.isOS = _isOS;
}
}
文件 2 的 7: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 的 7:Delegated.sol
pragma solidity ^0.8.0;
import "@openzeppelin/contracts/access/Ownable.sol";
contract Delegated is Ownable{
mapping(address => bool) internal _delegates;
constructor(){
_delegates[owner()] = true;
}
modifier onlyDelegates {
require(_delegates[msg.sender], "Invalid delegate" );
_;
}
function isDelegate( address addr ) external view onlyOwner returns ( bool ){
return _delegates[addr];
}
function setDelegate( address addr, bool isDelegate_ ) external onlyOwner{
_delegates[addr] = isDelegate_;
}
function transferOwnership(address newOwner) public virtual override onlyOwner {
_delegates[newOwner] = true;
super.transferOwnership( newOwner );
}
}
文件 4 的 7:ECDSA.sol
pragma solidity ^0.8.0;
import "../Strings.sol";
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 {
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 = vs & bytes32(0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff);
uint8 v = uint8((uint256(vs) >> 255) + 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 toEthSignedMessageHash(bytes memory s) internal pure returns (bytes32) {
return keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n", Strings.toString(s.length), s));
}
function toTypedDataHash(bytes32 domainSeparator, bytes32 structHash) internal pure returns (bytes32) {
return keccak256(abi.encodePacked("\x19\x01", domainSeparator, structHash));
}
}
文件 5 的 7: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() {
_transferOwnership(_msgSender());
}
modifier onlyOwner() {
_checkOwner();
_;
}
function owner() public view virtual returns (address) {
return _owner;
}
function _checkOwner() internal view virtual {
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);
}
}
文件 6 的 7:Strings.sol
pragma solidity ^0.8.0;
library Strings {
bytes16 private constant _HEX_SYMBOLS = "0123456789abcdef";
uint8 private constant _ADDRESS_LENGTH = 20;
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);
}
function toHexString(address addr) internal pure returns (string memory) {
return toHexString(uint256(uint160(addr)), _ADDRESS_LENGTH);
}
}
文件 7 的 7:Verify.sol
pragma solidity ^0.8.4;
import "./Delegated.sol";
import "@openzeppelin/contracts/utils/Strings.sol";
import "@openzeppelin/contracts/utils/cryptography/ECDSA.sol";
contract Verify is Delegated {
using Strings for uint;
using ECDSA for bytes32;
address private signer;
function verify( uint _quantity, uint _partnerId, bytes memory _signature ) internal view returns ( bool ) {
address signerCheck = getAddressSigner( _quantity.toString(), _partnerId.toString(), _signature );
return signerCheck == signer;
}
function getAddressSigner( string memory _quantity, string memory _partnerId, bytes memory _signature ) private view returns ( address ) {
bytes32 hash = createHash( _quantity, _partnerId );
return hash.toEthSignedMessageHash().recover( _signature );
}
function createHash( string memory _quantity, string memory _partnerId ) private view returns ( bytes32 ) {
return keccak256( abi.encodePacked( address(this), msg.sender, _quantity, _partnerId ) );
}
function setSigner( address _signer ) public onlyOwner{
signer = _signer;
}
}
{
"compilationTarget": {
"contracts/minter/ChimeraPillarsMinter.sol": "ChimeraPillarsMinter"
},
"evmVersion": "london",
"libraries": {},
"metadata": {
"bytecodeHash": "ipfs"
},
"optimizer": {
"enabled": true,
"runs": 200
},
"remappings": []
}
[{"inputs":[{"internalType":"address","name":"_signer","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"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":"ChimeraPillars","outputs":[{"internalType":"contract IChimeraPillars","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"_name","type":"string"},{"internalType":"address","name":"_contractAddress","type":"address"},{"internalType":"uint256","name":"_price","type":"uint256"},{"internalType":"string","name":"_slug","type":"string"},{"internalType":"uint16","name":"_minBalance","type":"uint16"},{"internalType":"bool","name":"_isActive","type":"bool"},{"internalType":"bool","name":"_isMatic","type":"bool"},{"internalType":"bool","name":"_isOS","type":"bool"}],"name":"addPartner","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_id","type":"uint256"},{"internalType":"string","name":"_name","type":"string"},{"internalType":"address","name":"_contractAddress","type":"address"},{"internalType":"uint256","name":"_price","type":"uint256"},{"internalType":"string","name":"_slug","type":"string"},{"internalType":"uint16","name":"_minBalance","type":"uint16"},{"internalType":"bool","name":"_isActive","type":"bool"},{"internalType":"bool","name":"_isMatic","type":"bool"},{"internalType":"bool","name":"_isOS","type":"bool"}],"name":"editPartner","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"isActive","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"addr","type":"address"}],"name":"isDelegate","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint16","name":"_quantity","type":"uint16"},{"internalType":"uint256","name":"_partnerId","type":"uint256"},{"internalType":"bytes","name":"_signature","type":"bytes"}],"name":"mint","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"partners","outputs":[{"internalType":"string","name":"name","type":"string"},{"internalType":"address","name":"contractAddress","type":"address"},{"internalType":"uint256","name":"price","type":"uint256"},{"internalType":"string","name":"slug","type":"string"},{"internalType":"uint16","name":"minBalance","type":"uint16"},{"internalType":"bool","name":"isActive","type":"bool"},{"internalType":"bool","name":"isMatic","type":"bool"},{"internalType":"bool","name":"isOS","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract IChimeraPillars","name":"principal","type":"address"}],"name":"setChimeraPillars","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"addr","type":"address"},{"internalType":"bool","name":"isDelegate_","type":"bool"}],"name":"setDelegate","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bool","name":"_isActive","type":"bool"}],"name":"setIsActive","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_signer","type":"address"}],"name":"setSigner","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"}]