Skip to content

Commit

Permalink
(names, hub): names and symbols for Circles
Browse files Browse the repository at this point in the history
  • Loading branch information
benjaminbollen committed Mar 20, 2024
1 parent 31974ca commit 2eba2ad
Show file tree
Hide file tree
Showing 9 changed files with 203 additions and 29 deletions.
8 changes: 6 additions & 2 deletions src/errors/Errors.sol
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,6 @@ interface IHubErrors {

error CirclesHubGroupIsNotRegistered(address group, uint8 code);

error CirclesHubInvalidName(address caller, string name, uint8 code);

error CirclesHubInvalidTrustReceiver(address trustReceiver, uint8 code);

error CirclesHubGroupMintPolicyRejectedMint(
Expand Down Expand Up @@ -86,7 +84,13 @@ interface IStandardTreasuryErrors {
}

interface INameRegistryErrors {
error CirclesNamesInvalidName(address avatar, string name, uint8 code);

error CirclesNamesShortNameAlreadyAssigned(address avatar, uint72 shortName, uint8 code);

error CirclesNamesShortNameWithNonceTaken(address avatar, uint256 nonce, uint72 shortName, address takenByAvatar);

error CirclesNamesAvatarAlreadyHasCustomNameOrSymbol(address avatar, string nameOrSymbol, uint8 code);

error CirclesNamesOrganizationHasNoSymbol(address organization, uint8 code);
}
23 changes: 10 additions & 13 deletions src/hub/Hub.sol
Original file line number Diff line number Diff line change
Expand Up @@ -109,10 +109,6 @@ contract Hub is Circles, MetadataDefinitions, IHubErrors, ICirclesErrors {

mapping(address => address) public treasuries;

mapping(address => string) public names;

mapping(address => string) public symbols;

// mapping(uint256 => WrappedERC20) public tokenIDToInfERC20;

/**
Expand Down Expand Up @@ -276,6 +272,10 @@ contract Hub is Circles, MetadataDefinitions, IHubErrors, ICirclesErrors {
{
_registerGroup(msg.sender, _mint, standardTreasury, _name, _symbol);

// for groups register possible custom name and symbol
nameRegistry.registerName(msg.sender, _name);
nameRegistry.registerSymbol(msg.sender, _symbol);

// store the IPFS CIDv0 digest for the group metadata
nameRegistry.updateCidV0Digest(msg.sender, _cidV0Digest);

Expand All @@ -299,6 +299,10 @@ contract Hub is Circles, MetadataDefinitions, IHubErrors, ICirclesErrors {
) external {
_registerGroup(msg.sender, _mint, _treasury, _name, _symbol);

// for groups register possible custom name and symbol
nameRegistry.registerName(msg.sender, _name);
nameRegistry.registerSymbol(msg.sender, _symbol);

// store the IPFS CIDv0 digest for the group metadata
nameRegistry.updateCidV0Digest(msg.sender, _cidV0Digest);

Expand All @@ -311,13 +315,10 @@ contract Hub is Circles, MetadataDefinitions, IHubErrors, ICirclesErrors {
* @param _cidV0Digest IPFS CIDv0 digest for the organization metadata
*/
function registerOrganization(string calldata _name, bytes32 _cidV0Digest) external {
if (!nameRegistry.isValidName(_name)) {
revert CirclesHubInvalidName(msg.sender, _name, 0);
}
_insertAvatar(msg.sender);

// store the name for the organization
names[msg.sender] = _name;
// for organizations, only register possible custom name
nameRegistry.registerName(msg.sender, _name);

// store the IPFS CIDv0 digest for the organization metadata
nameRegistry.updateCidV0Digest(msg.sender, _cidV0Digest);
Expand Down Expand Up @@ -1035,10 +1036,6 @@ contract Hub is Circles, MetadataDefinitions, IHubErrors, ICirclesErrors {

// store the treasury for the group
treasuries[_avatar] = _treasury;

// store the name and symbol for the group
names[_avatar] = _name;
symbols[_avatar] = _symbol;
}

function _trust(address _truster, address _trustee, uint96 _expiry) internal {
Expand Down
4 changes: 4 additions & 0 deletions src/hub/IHub.sol
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@ import "../circles/ICircles.sol";

interface IHubV2 is IERC1155, ICircles {
function avatars(address avatar) external view returns (address);
function isHuman(address avatar) external view returns (bool);
function isGroup(address avatar) external view returns (bool);
function isOrganization(address avatar) external view returns (bool);

function migrate(address owner, address[] calldata avatars, uint256[] calldata amounts) external;
function mintPolicies(address avatar) external view returns (address);
function burn(uint256 id, uint256 amount, bytes calldata data) external;
Expand Down
38 changes: 36 additions & 2 deletions src/lift/DemurrageCircles.sol
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ pragma solidity >=0.8.13;
import "@openzeppelin/contracts/token/ERC1155/utils/ERC1155Holder.sol";
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "../hub/IHub.sol";
import "../names/INameRegistry.sol";
import "./ERC20DiscountedBalances.sol";

abstract contract DemurrageCircles is ERC20DiscountedBalances, ERC1155Holder {
Expand All @@ -13,8 +14,16 @@ abstract contract DemurrageCircles is ERC20DiscountedBalances, ERC1155Holder {

IHubV2 public hub;

INameRegistry public nameRegistry;

address public avatar;

// Events

event Deposit(address indexed account, uint256 amount, uint256 inflationaryAmount);

event Withdraw(address indexed account, uint256 amount, uint256 inflationaryAmount);

// Modifiers

modifier onlyHub(uint8 _code) {
Expand All @@ -33,16 +42,20 @@ abstract contract DemurrageCircles is ERC20DiscountedBalances, ERC1155Holder {

// Setup function

function setup(IHubV2 _hub, address _avatar) external {
function setup(IHubV2 _hub, INameRegistry _nameRegistry, address _avatar) external {
if (address(hub) != address(0)) {
revert CirclesProxyAlreadyInitialized();
}
if (address(_hub) == address(0)) {
revert CirclesAddressCannotBeZero(0);
}
if (_avatar == address(0)) {
if (address(_nameRegistry) == address(0)) {
// Must not be the zero address.
revert CirclesAddressCannotBeZero(1);
}
if (_avatar == address(0)) {
revert CirclesAddressCannotBeZero(2);
}
hub = _hub;
avatar = _avatar;
// read inflation day zero from hub
Expand All @@ -56,12 +69,28 @@ abstract contract DemurrageCircles is ERC20DiscountedBalances, ERC1155Holder {
function unwrap(uint256 _amount) external {
_burn(msg.sender, _amount);
hub.safeTransferFrom(address(this), msg.sender, toTokenId(avatar), _amount, "");

uint256 inflationaryAmount = _calculateInflationaryBalance(_amount, day(block.timestamp));

emit Withdraw(msg.sender, _amount, inflationaryAmount);
}

function totalSupply() external view override returns (uint256) {
return hub.balanceOf(address(this), toTokenId(avatar));
}

function name() external view returns (string memory) {
return string(abi.encodePacked(nameRegistry.name(avatar), "-F"));
}

function symbol() external view returns (string memory) {
return nameRegistry.symbol(avatar);
}

function decimals() external pure returns (uint8) {
return 18;
}

// Public functions

function onERC1155Received(address, address _from, uint256 _id, uint256 _amount, bytes memory)
Expand All @@ -72,6 +101,11 @@ abstract contract DemurrageCircles is ERC20DiscountedBalances, ERC1155Holder {
{
if (_id != toTokenId(avatar)) revert CirclesInvalidCirclesId(_id, 0);
_mint(_from, _amount);

uint256 inflationaryAmount = _calculateInflationaryBalance(_amount, day(block.timestamp));

emit Deposit(_from, _amount, inflationaryAmount);

return this.onERC1155Received.selector;
}

Expand Down
30 changes: 23 additions & 7 deletions src/lift/ERC20Lift.sol
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,20 @@ import "@openzeppelin/contracts/utils/Create2.sol";
import "../errors/Errors.sol";
import "../lift/IERC20Lift.sol";
import "../hub/IHub.sol";
import "../names/INameRegistry.sol";
import "../proxy/ProxyFactory.sol";

contract ERC20Lift is ProxyFactory, IERC20Lift, ICirclesErrors {
// Constants

bytes4 public constant ERC20_WRAPPER_SETUP_CALLPREFIX = bytes4(keccak256("setup(address,address)"));
bytes4 public constant ERC20_WRAPPER_SETUP_CALLPREFIX = bytes4(keccak256("setup(address,address,address)"));

// State variables

IHubV2 public immutable hub;

INameRegistry public immutable nameRegistry;

/**
* @dev The master copy of the ERC20 demurrage and inflation Circles contract.
*/
Expand All @@ -25,26 +28,39 @@ contract ERC20Lift is ProxyFactory, IERC20Lift, ICirclesErrors {

// Constructor

constructor(IHubV2 _hub, address _masterCopyERC20Demurrage, address _masterCopyERC20Inflation) {
constructor(
IHubV2 _hub,
INameRegistry _nameRegistry,
address _masterCopyERC20Demurrage,
address _masterCopyERC20Inflation
) {
if (address(_hub) == address(0)) {
// Must not be the zero address.
revert CirclesAddressCannotBeZero(0);
}
if (_masterCopyERC20Demurrage == address(0)) {
if (address(_nameRegistry) == address(0)) {
// Must not be the zero address.
revert CirclesAddressCannotBeZero(1);
}
if (_masterCopyERC20Demurrage == address(0)) {
// Must not be the zero address.
revert CirclesAddressCannotBeZero(3);
}
if (_masterCopyERC20Inflation == address(0)) {
// Must not be the zero address.
revert CirclesAddressCannotBeZero(2);
revert CirclesAddressCannotBeZero(4);
}

hub = _hub;

nameRegistry = _nameRegistry;

masterCopyERC20Wrapper[uint256(CirclesType.Demurrage)] = _masterCopyERC20Demurrage;
masterCopyERC20Wrapper[uint256(CirclesType.Inflation)] = _masterCopyERC20Inflation;
}

// External functions

// Public functions

function ensureERC20(address _avatar, CirclesType _circlesType) public returns (address) {
Expand All @@ -71,9 +87,9 @@ contract ERC20Lift is ProxyFactory, IERC20Lift, ICirclesErrors {
return erc20Wrapper;
}

function getDeterministicAddress(uint256 _tokenId, bytes32 _bytecodeHash) public view returns (address) {
return Create2.computeAddress(keccak256(abi.encodePacked(_tokenId)), _bytecodeHash);
}
// function getDeterministicAddress(uint256 _tokenId, bytes32 _bytecodeHash) public view returns (address) {
// return Create2.computeAddress(keccak256(abi.encodePacked(_tokenId)), _bytecodeHash);
// }

// Internal functions

Expand Down
23 changes: 21 additions & 2 deletions src/lift/InflationaryCircles.sol
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ pragma solidity >=0.8.13;
import "@openzeppelin/contracts/token/ERC1155/utils/ERC1155Holder.sol";
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "../hub/IHub.sol";
import "../names/INameRegistry.sol";
import "./ERC20InflationaryBalances.sol";

contract InflationaryCircles is ERC20InflationaryBalances, ERC1155Holder {
Expand All @@ -13,6 +14,8 @@ contract InflationaryCircles is ERC20InflationaryBalances, ERC1155Holder {

IHubV2 public hub;

INameRegistry public nameRegistry;

address public avatar;

// Events
Expand All @@ -39,7 +42,7 @@ contract InflationaryCircles is ERC20InflationaryBalances, ERC1155Holder {

// Setup function

function setup(IHubV2 _hub, address _avatar) external {
function setup(IHubV2 _hub, INameRegistry _nameRegistry, address _avatar) external {
if (address(hub) != address(0)) {
// Must not be initialized already.
revert CirclesProxyAlreadyInitialized();
Expand All @@ -48,10 +51,14 @@ contract InflationaryCircles is ERC20InflationaryBalances, ERC1155Holder {
// Must not be the zero address.
revert CirclesAddressCannotBeZero(0);
}
if (_avatar == address(0)) {
if (address(_nameRegistry) == address(0)) {
// Must not be the zero address.
revert CirclesAddressCannotBeZero(1);
}
if (_avatar == address(0)) {
// Must not be the zero address.
revert CirclesAddressCannotBeZero(2);
}
hub = _hub;
avatar = _avatar;
// read inflation day zero from hub
Expand All @@ -74,6 +81,18 @@ contract InflationaryCircles is ERC20InflationaryBalances, ERC1155Holder {
emit Withdraw(msg.sender, _amount, demurragedAmount);
}

function name() external view returns (string memory) {
return string(abi.encodePacked(nameRegistry.name(avatar), "-F"));
}

function symbol() external view returns (string memory) {
return nameRegistry.symbol(avatar);
}

function decimals() external pure returns (uint8) {
return 18;
}

// Public functions

function onERC1155Received(address, address _from, uint256 _id, uint256 _amount, bytes memory)
Expand Down
26 changes: 26 additions & 0 deletions src/names/Base58Converter.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity >=0.8.13;

contract Base58Converter {
string internal constant ALPHABET = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";

function toBase58(uint256 _data) internal pure returns (string memory) {
bytes memory b58 = new bytes(64); // More than enough length
uint256 i = 0;
while (_data > 0) {
uint256 mod = _data % 58;
b58[i++] = bytes(ALPHABET)[mod];
_data = _data / 58;
}
// Reverse the string since the encoding works backwards
return string(_reverse(b58, i));
}

function _reverse(bytes memory _b, uint256 _len) internal pure returns (bytes memory) {
bytes memory reversed = new bytes(_len);
for (uint256 i = 0; i < _len; i++) {
reversed[i] = _b[_len - 1 - i];
}
return reversed;
}
}
10 changes: 8 additions & 2 deletions src/names/INameRegistry.sol
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,12 @@ pragma solidity >=0.8.13;

interface INameRegistry {
function updateCidV0Digest(address avatar, bytes32 cidVoDigest) external;
function isValidName(string calldata _name) external pure returns (bool);
function isValidSymbol(string calldata _symbol) external pure returns (bool);
function registerName(address avatar, string calldata name) external;
function registerSymbol(address avatar, string calldata symbol) external;

function name(address avatar) external view returns (string memory);
function symbol(address avatar) external view returns (string memory);

function isValidName(string calldata name) external pure returns (bool);
function isValidSymbol(string calldata symbol) external pure returns (bool);
}
Loading

0 comments on commit 2eba2ad

Please sign in to comment.