Created
April 22, 2022 16:46
-
-
Save z0r0z/cdbcbddc55250b35ded12b2237638e01 to your computer and use it in GitHub Desktop.
curia panel reputation / membership protocol
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// SPDX-License-Identifier: AGPL-3.0-only | |
pragma solidity >=0.8.0; | |
/// @notice Minimalist and gas efficient standard ERC1155 implementation. | |
/// @author Solmate (https://github.com/Rari-Capital/solmate/blob/main/src/tokens/ERC1155.sol) | |
abstract contract ERC1155 { | |
/*////////////////////////////////////////////////////////////// | |
EVENTS | |
//////////////////////////////////////////////////////////////*/ | |
event TransferSingle( | |
address indexed operator, | |
address indexed from, | |
address indexed to, | |
uint256 id, | |
uint256 amount | |
); | |
event TransferBatch( | |
address indexed operator, | |
address indexed from, | |
address indexed to, | |
uint256[] ids, | |
uint256[] amounts | |
); | |
event ApprovalForAll(address indexed owner, address indexed operator, bool approved); | |
event URI(string value, uint256 indexed id); | |
/*////////////////////////////////////////////////////////////// | |
ERC1155 STORAGE | |
//////////////////////////////////////////////////////////////*/ | |
mapping(address => mapping(uint256 => uint256)) public balanceOf; | |
mapping(address => mapping(address => bool)) public isApprovedForAll; | |
/*////////////////////////////////////////////////////////////// | |
METADATA LOGIC | |
//////////////////////////////////////////////////////////////*/ | |
function uri(uint256 id) public view virtual returns (string memory); | |
/*////////////////////////////////////////////////////////////// | |
ERC1155 LOGIC | |
//////////////////////////////////////////////////////////////*/ | |
function setApprovalForAll(address operator, bool approved) public virtual { | |
isApprovedForAll[msg.sender][operator] = approved; | |
emit ApprovalForAll(msg.sender, operator, approved); | |
} | |
function safeTransferFrom( | |
address from, | |
address to, | |
uint256 id, | |
uint256 amount, | |
bytes calldata data | |
) public virtual { | |
require(msg.sender == from || isApprovedForAll[from][msg.sender], "NOT_AUTHORIZED"); | |
balanceOf[from][id] -= amount; | |
balanceOf[to][id] += amount; | |
emit TransferSingle(msg.sender, from, to, id, amount); | |
require( | |
to.code.length == 0 | |
? to != address(0) | |
: ERC1155TokenReceiver(to).onERC1155Received(msg.sender, from, id, amount, data) == | |
ERC1155TokenReceiver.onERC1155Received.selector, | |
"UNSAFE_RECIPIENT" | |
); | |
} | |
function safeBatchTransferFrom( | |
address from, | |
address to, | |
uint256[] calldata ids, | |
uint256[] calldata amounts, | |
bytes calldata data | |
) public virtual { | |
require(ids.length == amounts.length, "LENGTH_MISMATCH"); | |
require(msg.sender == from || isApprovedForAll[from][msg.sender], "NOT_AUTHORIZED"); | |
// Storing these outside the loop saves ~15 gas per iteration. | |
uint256 id; | |
uint256 amount; | |
for (uint256 i = 0; i < ids.length; ) { | |
id = ids[i]; | |
amount = amounts[i]; | |
balanceOf[from][id] -= amount; | |
balanceOf[to][id] += amount; | |
// An array can't have a total length | |
// larger than the max uint256 value. | |
unchecked { | |
++i; | |
} | |
} | |
emit TransferBatch(msg.sender, from, to, ids, amounts); | |
require( | |
to.code.length == 0 | |
? to != address(0) | |
: ERC1155TokenReceiver(to).onERC1155BatchReceived(msg.sender, from, ids, amounts, data) == | |
ERC1155TokenReceiver.onERC1155BatchReceived.selector, | |
"UNSAFE_RECIPIENT" | |
); | |
} | |
function balanceOfBatch(address[] calldata owners, uint256[] calldata ids) | |
public | |
view | |
virtual | |
returns (uint256[] memory balances) | |
{ | |
require(owners.length == ids.length, "LENGTH_MISMATCH"); | |
balances = new uint256[](owners.length); | |
// Unchecked because the only math done is incrementing | |
// the array index counter which cannot possibly overflow. | |
unchecked { | |
for (uint256 i = 0; i < owners.length; ++i) { | |
balances[i] = balanceOf[owners[i]][ids[i]]; | |
} | |
} | |
} | |
/*////////////////////////////////////////////////////////////// | |
ERC165 LOGIC | |
//////////////////////////////////////////////////////////////*/ | |
function supportsInterface(bytes4 interfaceId) public view virtual returns (bool) { | |
return | |
interfaceId == 0x01ffc9a7 || // ERC165 Interface ID for ERC165 | |
interfaceId == 0xd9b67a26 || // ERC165 Interface ID for ERC1155 | |
interfaceId == 0x0e89341c; // ERC165 Interface ID for ERC1155MetadataURI | |
} | |
/*////////////////////////////////////////////////////////////// | |
INTERNAL MINT/BURN LOGIC | |
//////////////////////////////////////////////////////////////*/ | |
function _mint( | |
address to, | |
uint256 id, | |
uint256 amount, | |
bytes memory data | |
) internal virtual { | |
balanceOf[to][id] += amount; | |
emit TransferSingle(msg.sender, address(0), to, id, amount); | |
require( | |
to.code.length == 0 | |
? to != address(0) | |
: ERC1155TokenReceiver(to).onERC1155Received(msg.sender, address(0), id, amount, data) == | |
ERC1155TokenReceiver.onERC1155Received.selector, | |
"UNSAFE_RECIPIENT" | |
); | |
} | |
function _batchMint( | |
address to, | |
uint256[] memory ids, | |
uint256[] memory amounts, | |
bytes memory data | |
) internal virtual { | |
uint256 idsLength = ids.length; // Saves MLOADs. | |
require(idsLength == amounts.length, "LENGTH_MISMATCH"); | |
for (uint256 i = 0; i < idsLength; ) { | |
balanceOf[to][ids[i]] += amounts[i]; | |
// An array can't have a total length | |
// larger than the max uint256 value. | |
unchecked { | |
++i; | |
} | |
} | |
emit TransferBatch(msg.sender, address(0), to, ids, amounts); | |
require( | |
to.code.length == 0 | |
? to != address(0) | |
: ERC1155TokenReceiver(to).onERC1155BatchReceived(msg.sender, address(0), ids, amounts, data) == | |
ERC1155TokenReceiver.onERC1155BatchReceived.selector, | |
"UNSAFE_RECIPIENT" | |
); | |
} | |
function _batchBurn( | |
address from, | |
uint256[] memory ids, | |
uint256[] memory amounts | |
) internal virtual { | |
uint256 idsLength = ids.length; // Saves MLOADs. | |
require(idsLength == amounts.length, "LENGTH_MISMATCH"); | |
for (uint256 i = 0; i < idsLength; ) { | |
balanceOf[from][ids[i]] -= amounts[i]; | |
// An array can't have a total length | |
// larger than the max uint256 value. | |
unchecked { | |
++i; | |
} | |
} | |
emit TransferBatch(msg.sender, from, address(0), ids, amounts); | |
} | |
function _burn( | |
address from, | |
uint256 id, | |
uint256 amount | |
) internal virtual { | |
balanceOf[from][id] -= amount; | |
emit TransferSingle(msg.sender, from, address(0), id, amount); | |
} | |
} | |
/// @notice A generic interface for a contract which properly accepts ERC1155 tokens. | |
/// @author Solmate (https://github.com/Rari-Capital/solmate/blob/main/src/tokens/ERC1155.sol) | |
abstract contract ERC1155TokenReceiver { | |
function onERC1155Received( | |
address, | |
address, | |
uint256, | |
uint256, | |
bytes calldata | |
) external virtual returns (bytes4) { | |
return ERC1155TokenReceiver.onERC1155Received.selector; | |
} | |
function onERC1155BatchReceived( | |
address, | |
address, | |
uint256[] calldata, | |
uint256[] calldata, | |
bytes calldata | |
) external virtual returns (bytes4) { | |
return ERC1155TokenReceiver.onERC1155BatchReceived.selector; | |
} | |
} | |
/// @notice Single owner access control contract | |
abstract contract KaliOwnable { | |
event OwnershipTransferred(address indexed from, address indexed to); | |
event ClaimTransferred(address indexed from, address indexed to); | |
error NotOwner(); | |
error NotPendingOwner(); | |
address public owner; | |
address public pendingOwner; | |
modifier onlyOwner() { | |
if (msg.sender != owner) revert NotOwner(); | |
_; | |
} | |
function _init(address owner_) internal { | |
owner = owner_; | |
emit OwnershipTransferred(address(0), owner_); | |
} | |
function claimOwner() external payable { | |
if (msg.sender != pendingOwner) revert NotPendingOwner(); | |
emit OwnershipTransferred(owner, msg.sender); | |
owner = msg.sender; | |
delete pendingOwner; | |
} | |
function transferOwner(address to, bool direct) external payable onlyOwner { | |
if (direct) { | |
owner = to; | |
emit OwnershipTransferred(msg.sender, to); | |
} else { | |
pendingOwner = to; | |
emit ClaimTransferred(msg.sender, to); | |
} | |
} | |
} | |
/// @notice Mint membership and reputation for Curia Panel members. | |
contract CuriaPanel is ERC1155, KaliOwnable { | |
uint256 private idsCreated; | |
mapping(uint256 => string) public uris; | |
constructor() { | |
KaliOwnable._init(msg.sender); | |
} | |
function uri(uint256 id) public view override returns (string memory) { | |
return uris[id]; | |
} | |
function mint(address to, string calldata meta) external onlyOwner { | |
uint256 id; | |
unchecked { | |
id = idsCreated++; | |
} | |
if (bytes(meta).length != 0) { | |
uris[id] = meta; | |
emit URI(meta, id); | |
} | |
_mint( | |
to, | |
id, | |
0, | |
'' | |
); | |
} | |
function awardRep(address to, uint256 id, uint256 amount) external onlyOwner { | |
_mint( | |
to, | |
id, | |
amount, | |
'' | |
); | |
} | |
function safeTransferFrom(address,address,uint256,uint256,bytes memory) public pure override { | |
revert(); | |
} | |
function burn( | |
address from, | |
uint256 id, | |
uint256 amount | |
) external { | |
_burn( | |
from, | |
id, | |
amount | |
); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment