Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Issuance, demurrage, personal mint #91

Merged
merged 2 commits into from
Feb 8, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 25 additions & 1 deletion src/circles/Circles.sol
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,28 @@ pragma solidity >=0.8.13;
import "openzeppelin-contracts/contracts/token/ERC1155/ERC1155.sol";
import "openzeppelin-contracts/contracts/token/ERC1155/utils/ERC1155Holder.sol";

abstract contract Circles is ERC1155 {}
contract Circles is ERC1155 {
// Constants

/**
* @dev ERC1155 tokens MUST be 18 decimals. Used to calculate the issuance rate.
*/
uint8 public constant DECIMALS = uint8(18);

/**
* @notice Issue one Circle per hour for each human.
*/
uint256 public constant ISSUANCE_PERIOD = 1 hours;

/**
* @notice Upon claiming, the maximum claim is upto two weeks
* of history. Unclaimed older Circles are unclaimable.
*/
uint256 public constant MAX_CLAIM_DURATION = 2 weeks;

// Constructor

constructor(string memory uri_) ERC1155(uri_) {}

// External functions
}
167 changes: 98 additions & 69 deletions src/hub/Hub.sol
Original file line number Diff line number Diff line change
Expand Up @@ -77,9 +77,9 @@ contract Hub is Circles {
address public constant CIRCLES_STOPPED_V1 = address(0x1);

/**
* @notice Indefinitely, or approximate future infinity with uint96.max
* @notice Indefinite future, or approximated with uint96.max
*/
uint96 public constant INDEFINITELY = type(uint96).max;
uint96 public constant INDEFINITE_FUTURE = type(uint96).max;

// State variables

Expand Down Expand Up @@ -124,8 +124,6 @@ contract Hub is Circles {
*/
mapping(address => MintTime) public mintTimes;

mapping(address => bool) public stopped;

mapping(uint256 => WrappedERC20) public tokenIDToInfERC20;

// Mint policy registered by avatar.
Expand Down Expand Up @@ -182,7 +180,7 @@ contract Hub is Circles {
* (todo: eg. "https://fallback.aboutcircles.com/v1/circles/{id}.json")
*/
constructor(IHubV1 _hubV1, address _standardTreasury, uint256 _bootstrapTime, string memory _fallbackUri)
ERC1155(_fallbackUri)
Circles(_fallbackUri)
{
require(address(_hubV1) != address(0), "Hub v1 address can not be zero.");
require(_standardTreasury != address(0), "Standard treasury address can not be zero.");
Expand Down Expand Up @@ -212,7 +210,7 @@ contract Hub is Circles {
function registerHuman(bytes32 _cidV0Digest) external duringBootstrap {
// only available for v1 users with stopped v1 mint, for initial bootstrap period
address v1CirclesStatus = _registerHuman(msg.sender);
require(v1CirclesStatus != CIRCLES_STOPPED_V1, "Avatar must have stopped v1 Circles contract.");
require(v1CirclesStatus == CIRCLES_STOPPED_V1, "Avatar must have stopped v1 Circles contract.");

// store the IPFS CIDv0 digest for the avatar metadata
tokenIdToCidV0Digest[_toTokenId(msg.sender)] = _cidV0Digest;
Expand Down Expand Up @@ -242,7 +240,7 @@ contract Hub is Circles {
_mint(_human, _toTokenId(_human), WELCOME_BONUS, "");

// set trust to indefinite future, but avatar can edit this later
_trust(msg.sender, _human, INDEFINITELY);
_trust(msg.sender, _human, INDEFINITE_FUTURE);

emit InviteHuman(msg.sender, _human);
}
Expand Down Expand Up @@ -320,6 +318,11 @@ contract Hub is Circles {
emit CidV0(msg.sender, _cidV0Digest);
}

/**
* Register organization allows to register an organization avatar.
* @param _name name of the organization
* @param _cidV0Digest IPFS CIDv0 digest for the organization metadata
*/
function registerOrganization(string calldata _name, bytes32 _cidV0Digest) external {
require(_isValidName(_name), "Invalid organization name.");
_insertAvatar(msg.sender);
Expand Down Expand Up @@ -387,6 +390,20 @@ contract Hub is Circles {
_mint(msg.sender, _toTokenId(_group), sumAmounts, "");
}

function stop() external {
require(isHuman(msg.sender), "Only human can call stop.");
MintTime storage mintTime = mintTimes[msg.sender];
// stop future mints of personal Circles
// by setting the last mint time to indefinite future.
mintTime.lastMintTime = INDEFINITE_FUTURE;
}

function stopped(address _human) external view returns (bool) {
require(isHuman(_human), "Only personal Circles can stopped or not stopped.");
MintTime storage mintTime = mintTimes[msg.sender];
return (mintTime.lastMintTime == INDEFINITE_FUTURE);
}

// check if path transfer can be fully ERC1155 compatible
// note: matrix math needs to consider mints, otherwise it won't add up

Expand Down Expand Up @@ -469,14 +486,26 @@ contract Hub is Circles {

// Public functions

/**
* Checks if an avatar is registered as a human.
* @param _human address of the human to check
*/
function isHuman(address _human) public view returns (bool) {
return mintTimes[_human].lastMintTime > 0;
}

/**
* Checks if an avatar is registered as a group.
* @param _group address of the group to check
*/
function isGroup(address _group) public view returns (bool) {
return mintPolicies[_group] != address(0);
}

/**
* Checks if an avatar is registered as an organization.
* @param _organization address of the organization to check
*/
function isOrganization(address _organization) public view returns (bool) {
return avatars[_organization] != address(0) && mintPolicies[_organization] == address(0)
&& mintTimes[_organization].lastMintTime == uint256(0);
Expand All @@ -494,6 +523,67 @@ contract Hub is Circles {
return super.uri(_id);
}

/**
* @dev checks whether string is a valid name by checking
* the length as max 32 bytes and the allowed characters: 0-9, A-Z, a-z, space,
* hyphen, underscore, period, parentheses, apostrophe,
* ampersand, plus and hash.
* This restricts the contract name to a subset of ASCII characters,
* and excludes unicode characters for other alphabets and emoticons.
* Instead the default ERC1155 metadata read from the IPFS CID registry,
* should provide the full display name with unicode characters.
* Names are not checked for uniqueness.
*/
function _isValidName(string memory _name) public pure returns (bool) {
bytes memory nameBytes = bytes(_name);
if (nameBytes.length > 32 || nameBytes.length == 0) return false; // Check length

for (uint256 i = 0; i < nameBytes.length; i++) {
bytes1 char = nameBytes[i];
if (
!(char >= 0x30 && char <= 0x39) // 0-9
&& !(char >= 0x41 && char <= 0x5A) // A-Z
&& !(char >= 0x61 && char <= 0x7A) // a-z
&& !(char == 0x20) // Space
&& !(char == 0x2D || char == 0x5F) // Hyphen (-), Underscore (_)
&& !(char == 0x2E) // Period (.)
&& !(char == 0x28 || char == 0x29) // Parentheses ( () )
&& !(char == 0x27) // Apostrophe (')
&& !(char == 0x26) // Ampersand (&)
&& !(char == 0x2B || char == 0x23) // Plus (+), Hash (#)
) {
return false;
}
}
return true;
}

/**
* @dev checks whether string is a valid symbol by checking
* the length as max 16 bytes and the allowed characters: 0-9, A-Z, a-z,
* hyphen, underscore.
*/
function _isValidSymbol(string memory _symbol) public pure returns (bool) {
bytes memory symbolBytes = bytes(_symbol);
if (symbolBytes.length == 0 || symbolBytes.length > 16) {
return false; // Check length is within range
}

for (uint256 i = 0; i < symbolBytes.length; i++) {
bytes1 char = symbolBytes[i];
if (
// allowed ASCII characters 0-9, A-Z, a-z, Hyphen (-), Underscore (_)
!(
(char >= 0x30 && char <= 0x39) || (char >= 0x41 && char <= 0x5A) || (char >= 0x61 && char <= 0x7A)
|| (char == 0x2D) || (char == 0x5F)
)
) {
return false;
}
}
return true;
}

// Internal functions

/**
Expand All @@ -514,7 +604,7 @@ contract Hub is Circles {
mintTime.lastMintTime = uint96(block.timestamp);

// trust self indefinitely, cannot be altered later
_trust(_human, _human, INDEFINITELY);
_trust(_human, _human, INDEFINITE_FUTURE);

return v1CirclesStatus;
}
Expand Down Expand Up @@ -605,67 +695,6 @@ contract Hub is Circles {

// Private functions

/**
* @dev checks whether string is a valid name by checking
* the length as max 32 bytes and the allowed characters: 0-9, A-Z, a-z, space,
* hyphen, underscore, period, parentheses, apostrophe,
* ampersand, plus and hash.
* This restricts the contract name to a subset of ASCII characters,
* and excludes unicode characters for other alphabets and emoticons.
* Instead the default ERC1155 metadata read from the IPFS CID registry,
* should provide the full display name with unicode characters.
* Names are not checked for uniqueness.
*/
function _isValidName(string memory _name) public pure returns (bool) {
bytes memory nameBytes = bytes(_name);
if (nameBytes.length > 32 || nameBytes.length == 0) return false; // Check length

for (uint256 i = 0; i < nameBytes.length; i++) {
bytes1 char = nameBytes[i];
if (
!(char >= 0x30 && char <= 0x39) // 0-9
&& !(char >= 0x41 && char <= 0x5A) // A-Z
&& !(char >= 0x61 && char <= 0x7A) // a-z
&& !(char == 0x20) // Space
&& !(char == 0x2D || char == 0x5F) // Hyphen (-), Underscore (_)
&& !(char == 0x2E) // Period (.)
&& !(char == 0x28 || char == 0x29) // Parentheses ( () )
&& !(char == 0x27) // Apostrophe (')
&& !(char == 0x26) // Ampersand (&)
&& !(char == 0x2B || char == 0x23) // Plus (+), Hash (#)
) {
return false;
}
}
return true;
}

/**
* @dev checks whether string is a valid symbol by checking
* the length as max 16 bytes and the allowed characters: 0-9, A-Z, a-z,
* hyphen, underscore.
*/
function _isValidSymbol(string memory _symbol) public pure returns (bool) {
bytes memory symbolBytes = bytes(_symbol);
if (symbolBytes.length == 0 || symbolBytes.length > 16) {
return false; // Check length is within range
}

for (uint256 i = 0; i < symbolBytes.length; i++) {
bytes1 char = symbolBytes[i];
if (
// allowed ASCII characters 0-9, A-Z, a-z, Hyphen (-), Underscore (_)
!(
(char >= 0x30 && char <= 0x39) || (char >= 0x41 && char <= 0x5A) || (char >= 0x61 && char <= 0x7A)
|| (char == 0x2D) || (char == 0x5F)
)
) {
return false;
}
}
return true;
}

/**
* @dev Internal function to upsert a trust marker for a truster and a trusted address.
* It will initialize the linked list for the truster if it does not exist yet.
Expand Down
Loading