From e81a9b1cb42d0f052694bc3fcbba67c0cacbec78 Mon Sep 17 00:00:00 2001 From: Benjamin Bollen Date: Wed, 10 Apr 2024 12:31:49 +0100 Subject: [PATCH 01/17] (deploy): update version number --- script/deployments/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/script/deployments/package.json b/script/deployments/package.json index 0cb09cf..2632384 100644 --- a/script/deployments/package.json +++ b/script/deployments/package.json @@ -1,6 +1,6 @@ { "name": "deploy-circles", - "version": "0.3.2-alpha", + "version": "0.3.3-alpha", "type": "module", "dependencies": { "dotenv": "^16.4.5", From f853e966cda7ed5feef75db3d44eedcca5cdb438 Mon Sep 17 00:00:00 2001 From: Benjamin Bollen Date: Wed, 10 Apr 2024 13:43:38 +0100 Subject: [PATCH 02/17] (ERC1155): add total supply to mint paths; also name() and symbol() --- src/circles/Circles.sol | 14 ++++++++++++++ src/circles/DiscountedBalances.sol | 25 +++++++++++++++---------- src/hub/Hub.sol | 18 +++++++++++++++--- src/lift/ERC20DiscountedBalances.sol | 2 +- 4 files changed, 45 insertions(+), 14 deletions(-) diff --git a/src/circles/Circles.sol b/src/circles/Circles.sol index a1a826e..2468b24 100644 --- a/src/circles/Circles.sol +++ b/src/circles/Circles.sol @@ -149,6 +149,20 @@ contract Circles is ERC1155 { emit PersonalMint(_human, issuance, startPeriod, endPeriod); } + function _mintAndUpdateTotalSupply(address _account, uint256 _id, uint256 _value, bytes memory _data) internal { + _mint(_account, _id, _value, _data); + DiscountedBalance storage totalSupplyBalance = discountedTotalSupplies[_id]; + uint64 today = day(block.timestamp); + uint256 newTotalSupply = + _calculateDiscountedBalance(totalSupplyBalance.balance, today - totalSupplyBalance.lastUpdatedDay) + _value; + if (newTotalSupply > MAX_VALUE) { + // DiscountedBalances: balance exceeds maximum value + revert CirclesERC1155AmountExceedsMaxUint190(_account, _id, newTotalSupply, 2); + } + totalSupplyBalance.balance = uint192(newTotalSupply); + totalSupplyBalance.lastUpdatedDay = today; + } + // Private functions /** diff --git a/src/circles/DiscountedBalances.sol b/src/circles/DiscountedBalances.sol index 9f6f5f3..e6e0aee 100644 --- a/src/circles/DiscountedBalances.sol +++ b/src/circles/DiscountedBalances.sol @@ -13,6 +13,11 @@ contract DiscountedBalances is Demurrage { */ mapping(uint256 => mapping(address => DiscountedBalance)) public discountedBalances; + /** + * @dev stores the total supply for each Circles identifier + */ + mapping(uint256 => DiscountedBalance) internal discountedTotalSupplies; + // Constructor /** @@ -36,17 +41,17 @@ contract DiscountedBalances is Demurrage { return _calculateDiscountedBalance(discountedBalance.balance, _day - discountedBalance.lastUpdatedDay); } - // Internal functions + /** + * @notice Total supply of a Circles identifier. + * @param _id Circles identifier for which to calculate the total supply + */ + function totalSupply(uint256 _id) public view returns (uint256) { + DiscountedBalance memory totalSupplyBalance = discountedTotalSupplies[_id]; + uint64 today = day(block.timestamp); + return _calculateDiscountedBalance(totalSupplyBalance.balance, today - totalSupplyBalance.lastUpdatedDay); + } - // /** - // * @dev Calculate the inflationary balance of a discounted balance - // * @param _account Address of the account to calculate the balance of - // * @param _id Circles identifier for which to calculate the balance - // */ - // function _inflationaryBalanceOf(address _account, uint256 _id) internal view returns (uint256) { - // DiscountedBalance memory discountedBalance = discountedBalances[_id][_account]; - // return _calculateInflationaryBalance(discountedBalance.balance, discountedBalance.lastUpdatedDay); - // } + // Internal functions /** * @dev Update the balance of an account for a given Circles identifier diff --git a/src/hub/Hub.sol b/src/hub/Hub.sol index db2c16b..5478899 100644 --- a/src/hub/Hub.sol +++ b/src/hub/Hub.sol @@ -65,6 +65,18 @@ contract Hub is Circles, MetadataDefinitions, IHubErrors, ICirclesErrors { // State variables + /** + * @notice The global name of Circles. + * todo, change this to "Circles" for the production deployment + */ + string public name = "Rings"; + + /** + * @notice The global symbol ticker for Circles. + * todo, change this to "CRC" for the production deployment + */ + string public symbol = "RING"; + /** * @notice The Hub v1 contract address. */ @@ -252,7 +264,7 @@ contract Hub is Circles, MetadataDefinitions, IHubErrors, ICirclesErrors { // todo: re-discuss desired approach to welcome bonus vs migration // invited receives the welcome bonus in their personal Circles - _mint(_human, toTokenId(_human), WELCOME_BONUS, ""); + _mintAndUpdateTotalSupply(_human, toTokenId(_human), WELCOME_BONUS, ""); } // set trust to indefinite future, but avatar can edit this later @@ -473,7 +485,7 @@ contract Hub is Circles, MetadataDefinitions, IHubErrors, ICirclesErrors { for (uint256 i = 0; i < _avatars.length; i++) { // mint the migrated balances to _owner - _mint(_owner, toTokenId(_avatars[i]), _amounts[i], ""); + _mintAndUpdateTotalSupply(_owner, toTokenId(_avatars[i]), _amounts[i], ""); } } @@ -665,7 +677,7 @@ contract Hub is Circles, MetadataDefinitions, IHubErrors, ICirclesErrors { safeBatchTransferFrom(_sender, treasuries[_group], _collateral, _amounts, dataWithGroup); // mint group Circles to the sender and send the original _data onwards - _mint(_sender, toTokenId(_group), sumAmounts, _data); + _mintAndUpdateTotalSupply(_sender, toTokenId(_group), sumAmounts, _data); } function _verifyFlowMatrix( diff --git a/src/lift/ERC20DiscountedBalances.sol b/src/lift/ERC20DiscountedBalances.sol index 12c6103..baf2c21 100644 --- a/src/lift/ERC20DiscountedBalances.sol +++ b/src/lift/ERC20DiscountedBalances.sol @@ -97,7 +97,7 @@ contract ERC20DiscountedBalances is ERC20Permit, Demurrage, IERC20 { ) + _value; if (newBalance > MAX_VALUE) { // Balance exceeds maximum value. - revert CirclesERC1155AmountExceedsMaxUint190(_account, 0, newBalance, 0); + revert CirclesERC1155AmountExceedsMaxUint190(_account, 0, newBalance, 1); } discountedBalance.balance = uint192(newBalance); discountedBalance.lastUpdatedDay = _day; From 98b0a36f41554e86c8adc574989dd7b3ed10a645 Mon Sep 17 00:00:00 2001 From: Benjamin Bollen Date: Fri, 12 Apr 2024 14:32:44 +0100 Subject: [PATCH 03/17] (Circles): update mint and burn for total supply --- src/circles/Circles.sol | 32 ++++++++++++++++++++++++++---- src/circles/DiscountedBalances.sol | 2 +- src/circles/ERC1155.sol | 25 ++++++++++++----------- src/hub/Hub.sol | 8 ++++---- test/groups/mintGroupCircles.t.sol | 1 - 5 files changed, 46 insertions(+), 22 deletions(-) diff --git a/src/circles/Circles.sol b/src/circles/Circles.sol index 2468b24..e0a9bef 100644 --- a/src/circles/Circles.sol +++ b/src/circles/Circles.sol @@ -1,10 +1,11 @@ // SPDX-License-Identifier: AGPL-3.0-only pragma solidity >=0.8.13; +import "../errors/Errors.sol"; import "../lib/Math64x64.sol"; import "./ERC1155.sol"; -contract Circles is ERC1155 { +contract Circles is ERC1155, ICirclesErrors { // Type declarations /** @@ -56,7 +57,7 @@ contract Circles is ERC1155 { * and the status of the v1 Circles minting. * @dev This is used to store the last mint time for each avatar. */ - mapping(address => MintTime) public mintTimes; + mapping(address => MintTime) internal mintTimes; // Events @@ -142,7 +143,7 @@ contract Circles is ERC1155 { return; } // mint personal Circles to the human - _mint(_human, toTokenId(_human), issuance, ""); + _mintAndUpdateTotalSupply(_human, toTokenId(_human), issuance, ""); // update the last mint time mintTimes[_human].lastMintTime = uint96(block.timestamp); @@ -151,8 +152,9 @@ contract Circles is ERC1155 { function _mintAndUpdateTotalSupply(address _account, uint256 _id, uint256 _value, bytes memory _data) internal { _mint(_account, _id, _value, _data); - DiscountedBalance storage totalSupplyBalance = discountedTotalSupplies[_id]; + uint64 today = day(block.timestamp); + DiscountedBalance storage totalSupplyBalance = discountedTotalSupplies[_id]; uint256 newTotalSupply = _calculateDiscountedBalance(totalSupplyBalance.balance, today - totalSupplyBalance.lastUpdatedDay) + _value; if (newTotalSupply > MAX_VALUE) { @@ -163,6 +165,28 @@ contract Circles is ERC1155 { totalSupplyBalance.lastUpdatedDay = today; } + function _burnAndUpdateTotalSupply(address _account, uint256 _id, uint256 _value) internal { + // _update will discount the balance before subtracting the value + _burn(_account, _id, _value); + + uint64 today = day(block.timestamp); + DiscountedBalance storage totalSupplyBalance = discountedTotalSupplies[_id]; + uint256 discountedTotalSupply = + _calculateDiscountedBalance(totalSupplyBalance.balance, today - totalSupplyBalance.lastUpdatedDay); + if (discountedTotalSupply < _value) { + // Logically impossible to burn more than the total supply + // however if the total supply nears dust, the discounting of the balance + // and the total supply might differ on the least significant bits. + // There is no good way to handle this, so user should burn a few attoCRC less, + // or wait a day for the total supply to be discounted to zero automatically. + revert CirclesLogicAssertion(4); + } + unchecked{ + totalSupplyBalance.balance = uint192(discountedTotalSupply - _value); + } + totalSupplyBalance.lastUpdatedDay = today; + } + // Private functions /** diff --git a/src/circles/DiscountedBalances.sol b/src/circles/DiscountedBalances.sol index e6e0aee..72f5c09 100644 --- a/src/circles/DiscountedBalances.sol +++ b/src/circles/DiscountedBalances.sol @@ -11,7 +11,7 @@ contract DiscountedBalances is Demurrage { * @dev stores the discounted balances of the accounts privately. * Mapping from Circles identifiers to accounts to the discounted balance. */ - mapping(uint256 => mapping(address => DiscountedBalance)) public discountedBalances; + mapping(uint256 => mapping(address => DiscountedBalance)) internal discountedBalances; /** * @dev stores the total supply for each Circles identifier diff --git a/src/circles/ERC1155.sol b/src/circles/ERC1155.sol index fec60c6..0e700be 100644 --- a/src/circles/ERC1155.sol +++ b/src/circles/ERC1155.sol @@ -341,12 +341,12 @@ abstract contract ERC1155 is DiscountedBalances, Context, ERC165, IERC1155, IERC * - If `to` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155BatchReceived} and return the * acceptance magic value. */ - function _mintBatch(address to, uint256[] memory ids, uint256[] memory values, bytes memory data) internal { - if (to == address(0)) { - revert ERC1155InvalidReceiver(address(0)); - } - _updateWithAcceptanceCheck(address(0), to, ids, values, data); - } + // function _mintBatch(address to, uint256[] memory ids, uint256[] memory values, bytes memory data) internal { + // if (to == address(0)) { + // revert ERC1155InvalidReceiver(address(0)); + // } + // _updateWithAcceptanceCheck(address(0), to, ids, values, data); + // } /** * @dev Destroys a `value` amount of tokens of type `id` from `from` @@ -376,13 +376,14 @@ abstract contract ERC1155 is DiscountedBalances, Context, ERC165, IERC1155, IERC * - `from` cannot be the zero address. * - `from` must have at least `value` amount of tokens of type `id`. * - `ids` and `values` must have the same length. + * // */ - function _burnBatch(address from, uint256[] memory ids, uint256[] memory values) internal { - if (from == address(0)) { - revert ERC1155InvalidSender(address(0)); - } - _updateWithAcceptanceCheck(from, address(0), ids, values, ""); - } + // function _burnBatch(address from, uint256[] memory ids, uint256[] memory values) internal { + // if (from == address(0)) { + // revert ERC1155InvalidSender(address(0)); + // } + // _updateWithAcceptanceCheck(from, address(0), ids, values, ""); + // } /** * @dev Approve `operator` to operate on all of `owner` tokens diff --git a/src/hub/Hub.sol b/src/hub/Hub.sol index 5478899..95e9ca5 100644 --- a/src/hub/Hub.sol +++ b/src/hub/Hub.sol @@ -21,7 +21,7 @@ import "./MetadataDefinitions.sol"; * It further allows to wrap any token into an inflationary or demurraged * ERC20 Circles contract. */ -contract Hub is Circles, MetadataDefinitions, IHubErrors, ICirclesErrors { +contract Hub is Circles, MetadataDefinitions, IHubErrors { // Type declarations /** @@ -260,7 +260,7 @@ contract Hub is Circles, MetadataDefinitions, IHubErrors, ICirclesErrors { if (block.timestamp > invitationOnlyTime) { // after the bootstrap period, the inviter must burn the invitation cost - _burn(msg.sender, toTokenId(msg.sender), INVITATION_COST); + _burnAndUpdateTotalSupply(msg.sender, toTokenId(msg.sender), INVITATION_COST); // todo: re-discuss desired approach to welcome bonus vs migration // invited receives the welcome bonus in their personal Circles @@ -480,7 +480,7 @@ contract Hub is Circles, MetadataDefinitions, IHubErrors, ICirclesErrors { // Only humans can migrate v1 tokens after the bootstrap period. revert CirclesHubMustBeHuman(_owner, 4); } - _burn(_owner, toTokenId(_owner), cost); + _burnAndUpdateTotalSupply(_owner, toTokenId(_owner), cost); } for (uint256 i = 0; i < _avatars.length; i++) { @@ -510,7 +510,7 @@ contract Hub is Circles, MetadataDefinitions, IHubErrors, ICirclesErrors { revert CirclesHubGroupMintPolicyRejectedBurn(msg.sender, group, _amount, _data, 0); } } - _burn(msg.sender, _id, _amount); + _burnAndUpdateTotalSupply(msg.sender, _id, _amount); } // Public functions diff --git a/test/groups/mintGroupCircles.t.sol b/test/groups/mintGroupCircles.t.sol index 8b7eff0..9080ff4 100644 --- a/test/groups/mintGroupCircles.t.sol +++ b/test/groups/mintGroupCircles.t.sol @@ -10,7 +10,6 @@ import "./setup.sol"; contract MintGroupCirclesTest is Test, GroupSetup, IHubErrors { // State variables address group; - address group2; // Constructor From 7b78c749b91175265f2cf934fcc4f51a2085358d Mon Sep 17 00:00:00 2001 From: Benjamin Bollen Date: Fri, 12 Apr 2024 15:00:59 +0100 Subject: [PATCH 04/17] (changelog): update changelog --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 01e9958..884fb6a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,10 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## latest + +- [PR136] totalSupply and name, symbol for ERC1155 (outside of specification) + ## v0.3.3 - [PR132] bug fix in groupMint(); initial test coverage for group mint From 553a81cc7834ec39c72951b7ef426835ddc8c137 Mon Sep 17 00:00:00 2001 From: Benjamin Bollen Date: Fri, 12 Apr 2024 15:36:54 +0100 Subject: [PATCH 05/17] (deploy): deployed version to chiado for testing --- ...hiado-0.3.3-alpha-7b78c74-240412-151200.log | 18 ++++++++++++++++++ ...facts-0.3.3-alpha-7b78c74-240412-151200.txt | 9 +++++++++ .../constructorArgs_BaseGroupMintPolicy.txt | 1 + .../constructorArgs_ERC20Lift.txt | 1 + .../constructorArgs_Hub.txt | 1 + ...onstructorArgs_MastercopyDemurrageERC20.txt | 1 + ...tructorArgs_MastercopyInflationaryERC20.txt | 1 + ...constructorArgs_MastercopyStandardVault.txt | 1 + .../constructorArgs_Migration.txt | 1 + .../constructorArgs_NameRegistry.txt | 1 + .../constructorArgs_StandardTreasury.txt | 1 + 11 files changed, 36 insertions(+) create mode 100644 script/deployments/chiado-0.3.3-alpha-7b78c74-240412-151200/chiado-0.3.3-alpha-7b78c74-240412-151200.log create mode 100644 script/deployments/chiado-0.3.3-alpha-7b78c74-240412-151200/chiado-artefacts-0.3.3-alpha-7b78c74-240412-151200.txt create mode 100644 script/deployments/chiado-0.3.3-alpha-7b78c74-240412-151200/constructorArgs_BaseGroupMintPolicy.txt create mode 100644 script/deployments/chiado-0.3.3-alpha-7b78c74-240412-151200/constructorArgs_ERC20Lift.txt create mode 100644 script/deployments/chiado-0.3.3-alpha-7b78c74-240412-151200/constructorArgs_Hub.txt create mode 100644 script/deployments/chiado-0.3.3-alpha-7b78c74-240412-151200/constructorArgs_MastercopyDemurrageERC20.txt create mode 100644 script/deployments/chiado-0.3.3-alpha-7b78c74-240412-151200/constructorArgs_MastercopyInflationaryERC20.txt create mode 100644 script/deployments/chiado-0.3.3-alpha-7b78c74-240412-151200/constructorArgs_MastercopyStandardVault.txt create mode 100644 script/deployments/chiado-0.3.3-alpha-7b78c74-240412-151200/constructorArgs_Migration.txt create mode 100644 script/deployments/chiado-0.3.3-alpha-7b78c74-240412-151200/constructorArgs_NameRegistry.txt create mode 100644 script/deployments/chiado-0.3.3-alpha-7b78c74-240412-151200/constructorArgs_StandardTreasury.txt diff --git a/script/deployments/chiado-0.3.3-alpha-7b78c74-240412-151200/chiado-0.3.3-alpha-7b78c74-240412-151200.log b/script/deployments/chiado-0.3.3-alpha-7b78c74-240412-151200/chiado-0.3.3-alpha-7b78c74-240412-151200.log new file mode 100644 index 0000000..f3a29b3 --- /dev/null +++ b/script/deployments/chiado-0.3.3-alpha-7b78c74-240412-151200/chiado-0.3.3-alpha-7b78c74-240412-151200.log @@ -0,0 +1,18 @@ +Chiado deployment +================= +Deployment Date: 2024-04-12 15:12:00 +Version: 0.3.3-alpha +Git Commit: 7b78c749b91175265f2cf934fcc4f51a2085358d +Deployer Address: 0x7619F26728Ced663E50E578EB6ff42430931564c, Intitial nonce: 108 +Compiler Version: v0.8.23+commit.f704f362 + +Deployed Contracts: +Hub: 0xFFfbD3E62203B888bb8E09c1fcAcE58242674964 +Migration: 0x9c7E2AfAc052743944d5d8F80Db90E1E14A663aE +NameRegistry: 0x0A1D308a39A6dF8972A972E586E4b4b3Dc73520f +ERC20Lift: 0xaA20efff0511175C091F156D095DD3b62951Be48 +StandardTreasury: 0x97c28CCC374570C2b10f7569197919418D702F99 +BaseGroupMintPolicy: 0x95fbD8D52c18b6AA453e9cB06851543f125DCedE +MastercopyDemurrageERC20: 0x44A47EC79B45A13e884328018320D90DE86Bb932 +MastercopyInflationaryERC20: 0xb4A1012a0564a25dF5a8311d1B6DF47b098BdfdB +MastercopyStandardVault: 0x8E0115358DC2Dc984c0C1d9530BEE19616492177 diff --git a/script/deployments/chiado-0.3.3-alpha-7b78c74-240412-151200/chiado-artefacts-0.3.3-alpha-7b78c74-240412-151200.txt b/script/deployments/chiado-0.3.3-alpha-7b78c74-240412-151200/chiado-artefacts-0.3.3-alpha-7b78c74-240412-151200.txt new file mode 100644 index 0000000..9b59f82 --- /dev/null +++ b/script/deployments/chiado-0.3.3-alpha-7b78c74-240412-151200/chiado-artefacts-0.3.3-alpha-7b78c74-240412-151200.txt @@ -0,0 +1,9 @@ +{"contractName":"Hub","deployedAddress":"0xFFfbD3E62203B888bb8E09c1fcAcE58242674964","sourcePath":"src/hub/Hub.sol:Hub","constructor-args":"0xdbF22D4e8962Db3b2F1d9Ff55be728A887e47710 0x0A1D308a39A6dF8972A972E586E4b4b3Dc73520f 0x9c7E2AfAc052743944d5d8F80Db90E1E14A663aE 0xaA20efff0511175C091F156D095DD3b62951Be48 0x97c28CCC374570C2b10f7569197919418D702F99 1675209600 31540000 https://fallback.aboutcircles.com/v1/circles/{id}.json","argumentsFile":"constructorArgs_Hub.txt"} +{"contractName":"Migration","deployedAddress":"0x9c7E2AfAc052743944d5d8F80Db90E1E14A663aE","sourcePath":"src/migration/Migration.sol:Migration","constructor-args":"0xdbF22D4e8962Db3b2F1d9Ff55be728A887e47710 0xFFfbD3E62203B888bb8E09c1fcAcE58242674964 1675209600","argumentsFile":"constructorArgs_Migration.txt"} +{"contractName":"NameRegistry","deployedAddress":"0x0A1D308a39A6dF8972A972E586E4b4b3Dc73520f","sourcePath":"src/names/NameRegistry.sol:NameRegistry","constructor-args":"0xFFfbD3E62203B888bb8E09c1fcAcE58242674964","argumentsFile":"constructorArgs_NameRegistry.txt"} +{"contractName":"ERC20Lift","deployedAddress":"0xaA20efff0511175C091F156D095DD3b62951Be48","sourcePath":"src/lift/ERC20Lift.sol:ERC20Lift","constructor-args":"0xFFfbD3E62203B888bb8E09c1fcAcE58242674964 0x0A1D308a39A6dF8972A972E586E4b4b3Dc73520f 0x44A47EC79B45A13e884328018320D90DE86Bb932 0xb4A1012a0564a25dF5a8311d1B6DF47b098BdfdB","argumentsFile":"constructorArgs_ERC20Lift.txt"} +{"contractName":"StandardTreasury","deployedAddress":"0x97c28CCC374570C2b10f7569197919418D702F99","sourcePath":"src/treasury/StandardTreasury.sol:StandardTreasury","constructor-args":"0xFFfbD3E62203B888bb8E09c1fcAcE58242674964 0x8E0115358DC2Dc984c0C1d9530BEE19616492177","argumentsFile":"constructorArgs_StandardTreasury.txt"} +{"contractName":"BaseGroupMintPolicy","deployedAddress":"0x95fbD8D52c18b6AA453e9cB06851543f125DCedE","sourcePath":"src/groups/BaseMintPolicy.sol:MintPolicy","constructor-args":"","argumentsFile":"constructorArgs_BaseGroupMintPolicy.txt"} +{"contractName":"MastercopyDemurrageERC20","deployedAddress":"0x44A47EC79B45A13e884328018320D90DE86Bb932","sourcePath":"src/lift/DemurrageCircles.sol:DemurrageCircles","constructor-args":"","argumentsFile":"constructorArgs_MastercopyDemurrageERC20.txt"} +{"contractName":"MastercopyInflationaryERC20","deployedAddress":"0xb4A1012a0564a25dF5a8311d1B6DF47b098BdfdB","sourcePath":"src/lift/InflationaryCircles.sol:InflationaryCircles","constructor-args":"","argumentsFile":"constructorArgs_MastercopyInflationaryERC20.txt"} +{"contractName":"MastercopyStandardVault","deployedAddress":"0x8E0115358DC2Dc984c0C1d9530BEE19616492177","sourcePath":"src/treasury/StandardVault.sol:StandardVault","constructor-args":"","argumentsFile":"constructorArgs_MastercopyStandardVault.txt"} diff --git a/script/deployments/chiado-0.3.3-alpha-7b78c74-240412-151200/constructorArgs_BaseGroupMintPolicy.txt b/script/deployments/chiado-0.3.3-alpha-7b78c74-240412-151200/constructorArgs_BaseGroupMintPolicy.txt new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/script/deployments/chiado-0.3.3-alpha-7b78c74-240412-151200/constructorArgs_BaseGroupMintPolicy.txt @@ -0,0 +1 @@ + diff --git a/script/deployments/chiado-0.3.3-alpha-7b78c74-240412-151200/constructorArgs_ERC20Lift.txt b/script/deployments/chiado-0.3.3-alpha-7b78c74-240412-151200/constructorArgs_ERC20Lift.txt new file mode 100644 index 0000000..c866ccf --- /dev/null +++ b/script/deployments/chiado-0.3.3-alpha-7b78c74-240412-151200/constructorArgs_ERC20Lift.txt @@ -0,0 +1 @@ +0xFFfbD3E62203B888bb8E09c1fcAcE58242674964 0x0A1D308a39A6dF8972A972E586E4b4b3Dc73520f 0x44A47EC79B45A13e884328018320D90DE86Bb932 0xb4A1012a0564a25dF5a8311d1B6DF47b098BdfdB diff --git a/script/deployments/chiado-0.3.3-alpha-7b78c74-240412-151200/constructorArgs_Hub.txt b/script/deployments/chiado-0.3.3-alpha-7b78c74-240412-151200/constructorArgs_Hub.txt new file mode 100644 index 0000000..81d94ae --- /dev/null +++ b/script/deployments/chiado-0.3.3-alpha-7b78c74-240412-151200/constructorArgs_Hub.txt @@ -0,0 +1 @@ +0xdbF22D4e8962Db3b2F1d9Ff55be728A887e47710 0x0A1D308a39A6dF8972A972E586E4b4b3Dc73520f 0x9c7E2AfAc052743944d5d8F80Db90E1E14A663aE 0xaA20efff0511175C091F156D095DD3b62951Be48 0x97c28CCC374570C2b10f7569197919418D702F99 1675209600 31540000 https://fallback.aboutcircles.com/v1/circles/{id}.json diff --git a/script/deployments/chiado-0.3.3-alpha-7b78c74-240412-151200/constructorArgs_MastercopyDemurrageERC20.txt b/script/deployments/chiado-0.3.3-alpha-7b78c74-240412-151200/constructorArgs_MastercopyDemurrageERC20.txt new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/script/deployments/chiado-0.3.3-alpha-7b78c74-240412-151200/constructorArgs_MastercopyDemurrageERC20.txt @@ -0,0 +1 @@ + diff --git a/script/deployments/chiado-0.3.3-alpha-7b78c74-240412-151200/constructorArgs_MastercopyInflationaryERC20.txt b/script/deployments/chiado-0.3.3-alpha-7b78c74-240412-151200/constructorArgs_MastercopyInflationaryERC20.txt new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/script/deployments/chiado-0.3.3-alpha-7b78c74-240412-151200/constructorArgs_MastercopyInflationaryERC20.txt @@ -0,0 +1 @@ + diff --git a/script/deployments/chiado-0.3.3-alpha-7b78c74-240412-151200/constructorArgs_MastercopyStandardVault.txt b/script/deployments/chiado-0.3.3-alpha-7b78c74-240412-151200/constructorArgs_MastercopyStandardVault.txt new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/script/deployments/chiado-0.3.3-alpha-7b78c74-240412-151200/constructorArgs_MastercopyStandardVault.txt @@ -0,0 +1 @@ + diff --git a/script/deployments/chiado-0.3.3-alpha-7b78c74-240412-151200/constructorArgs_Migration.txt b/script/deployments/chiado-0.3.3-alpha-7b78c74-240412-151200/constructorArgs_Migration.txt new file mode 100644 index 0000000..80db1de --- /dev/null +++ b/script/deployments/chiado-0.3.3-alpha-7b78c74-240412-151200/constructorArgs_Migration.txt @@ -0,0 +1 @@ +0xdbF22D4e8962Db3b2F1d9Ff55be728A887e47710 0xFFfbD3E62203B888bb8E09c1fcAcE58242674964 1675209600 diff --git a/script/deployments/chiado-0.3.3-alpha-7b78c74-240412-151200/constructorArgs_NameRegistry.txt b/script/deployments/chiado-0.3.3-alpha-7b78c74-240412-151200/constructorArgs_NameRegistry.txt new file mode 100644 index 0000000..9b3ccd8 --- /dev/null +++ b/script/deployments/chiado-0.3.3-alpha-7b78c74-240412-151200/constructorArgs_NameRegistry.txt @@ -0,0 +1 @@ +0xFFfbD3E62203B888bb8E09c1fcAcE58242674964 diff --git a/script/deployments/chiado-0.3.3-alpha-7b78c74-240412-151200/constructorArgs_StandardTreasury.txt b/script/deployments/chiado-0.3.3-alpha-7b78c74-240412-151200/constructorArgs_StandardTreasury.txt new file mode 100644 index 0000000..eef2fdd --- /dev/null +++ b/script/deployments/chiado-0.3.3-alpha-7b78c74-240412-151200/constructorArgs_StandardTreasury.txt @@ -0,0 +1 @@ +0xFFfbD3E62203B888bb8E09c1fcAcE58242674964 0x8E0115358DC2Dc984c0C1d9530BEE19616492177 From 288be7a15b76f938b2f1fa2ba741d0389b5699e4 Mon Sep 17 00:00:00 2001 From: Benjamin Bollen Date: Tue, 16 Apr 2024 17:24:47 +0100 Subject: [PATCH 06/17] (circles): forge fmt --- src/circles/Circles.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/circles/Circles.sol b/src/circles/Circles.sol index 13be875..cf9fbc9 100644 --- a/src/circles/Circles.sol +++ b/src/circles/Circles.sol @@ -181,7 +181,7 @@ contract Circles is ERC1155, ICirclesErrors { // or wait a day for the total supply to be discounted to zero automatically. revert CirclesLogicAssertion(4); } - unchecked{ + unchecked { totalSupplyBalance.balance = uint192(discountedTotalSupply - _value); } totalSupplyBalance.lastUpdatedDay = today; From 4030508b76a481d71459d1103f6861d4177a1ce7 Mon Sep 17 00:00:00 2001 From: Benjamin Bollen Date: Tue, 16 Apr 2024 17:33:39 +0100 Subject: [PATCH 07/17] (hub): merging all open 0.3.4 PRs and fixing total build size --- CHANGELOG.md | 4 +++- src/hub/Hub.sol | 50 ++++++++++++++++++++++++------------------------- 2 files changed, 28 insertions(+), 26 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 884fb6a..dc4faf7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,7 +6,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## latest -- [PR136] totalSupply and name, symbol for ERC1155 (outside of specification) +- [PR141] consented flow with bi-directional trust, with opt-out to v1 protocol of uni-directional trust +- [PR138] storage-based reentrency guard for operateFlowMatrix +- [PR136] total supply, name and symbol for ERC1155 (out of specification) ## v0.3.3 diff --git a/src/hub/Hub.sol b/src/hub/Hub.sol index 779e908..9bc71e9 100644 --- a/src/hub/Hub.sol +++ b/src/hub/Hub.sol @@ -67,17 +67,17 @@ contract Hub is Circles, MetadataDefinitions, IHubErrors { // State variables - /** - * @notice The global name of Circles. - * todo, change this to "Circles" for the production deployment - */ - string public name = "Rings"; - - /** - * @notice The global symbol ticker for Circles. - * todo, change this to "CRC" for the production deployment - */ - string public symbol = "RING"; + // /** + // * @notice The global name of Circles. + // * todo, change this to "Circles" for the production deployment + // */ + // string public name = "Rings"; + + // /** + // * @notice The global symbol ticker for Circles. + // * todo, change this to "CRC" for the production deployment + // */ + // string public symbol = "RING"; /** * @notice The Hub v1 contract address. @@ -193,23 +193,23 @@ contract Hub is Circles, MetadataDefinitions, IHubErrors { modifier nonReentrant(uint8 _code) { // todo: this should use transient storage slot // but didn't compile; investigate - // assembly { - // if tload(0) { revert(0, 0) } - // tstore(0, 1) - // } - // _; - // assembly { - // tstore(0, 0) - // } + assembly { + if tload(0) { revert(0, 0) } + tstore(0, 1) + } + _; + assembly { + tstore(0, 0) + } // for now, default to normal storage slot // replace this later with transient storage slot - if (_reentrancyGuard) { - revert CirclesReentrancyGuard(_code); - } - _reentrancyGuard = true; - _; - _reentrancyGuard = false; + // if (_reentrancyGuard) { + // revert CirclesReentrancyGuard(_code); + // } + // _reentrancyGuard = true; + // _; + // _reentrancyGuard = false; } // Constructor From 23778d843f06738e7bb7239f2524950e2fe35a85 Mon Sep 17 00:00:00 2001 From: Benjamin Bollen Date: Wed, 17 Apr 2024 13:50:34 +0100 Subject: [PATCH 08/17] (hub): assert that transient storage builds with solc, but not with forge --- src/hub/Hub.sol | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/hub/Hub.sol b/src/hub/Hub.sol index 779e908..06b37b5 100644 --- a/src/hub/Hub.sol +++ b/src/hub/Hub.sol @@ -191,8 +191,8 @@ contract Hub is Circles, MetadataDefinitions, IHubErrors { * see https://soliditylang.org/blog/2024/01/26/transient-storage/ */ modifier nonReentrant(uint8 _code) { - // todo: this should use transient storage slot - // but didn't compile; investigate + // todo: this should use a transient storage slot + // but doesn't compile through `forge build`, but does compile with solc directly // assembly { // if tload(0) { revert(0, 0) } // tstore(0, 1) From cafe0748e8177a264f2e96e539642564a1bddb69 Mon Sep 17 00:00:00 2001 From: Benjamin Bollen Date: Wed, 17 Apr 2024 14:02:55 +0100 Subject: [PATCH 09/17] (hub): forge fmt --- src/hub/Hub.sol | 1 - 1 file changed, 1 deletion(-) diff --git a/src/hub/Hub.sol b/src/hub/Hub.sol index 76c8d30..562b3c8 100644 --- a/src/hub/Hub.sol +++ b/src/hub/Hub.sol @@ -202,7 +202,6 @@ contract Hub is Circles, MetadataDefinitions, IHubErrors { // tstore(0, 0) // } - // for now, default to normal storage slot // replace this later with transient storage slot if (_reentrancyGuard) { From 727a45aad21106cb9993fae16c7fea8386733c13 Mon Sep 17 00:00:00 2001 From: Benjamin Bollen Date: Wed, 17 Apr 2024 17:06:39 +0100 Subject: [PATCH 10/17] (hub, nameRegistry): impl IPFS uri for erc1155, with fallback to constant --- src/circles/Circles.sol | 2 -- src/hub/Hub.sol | 28 +++++++++++++++++----------- src/names/Base58Converter.sol | 29 ++++++++++++++++++++++++++--- src/names/INameRegistry.sol | 3 ++- src/names/NameRegistry.sol | 25 ++++++++++++++++++++++--- 5 files changed, 67 insertions(+), 20 deletions(-) diff --git a/src/circles/Circles.sol b/src/circles/Circles.sol index cf9fbc9..dfb2571 100644 --- a/src/circles/Circles.sol +++ b/src/circles/Circles.sol @@ -75,8 +75,6 @@ contract Circles is ERC1155, ICirclesErrors { DiscountedBalances(_inflation_day_zero) {} - // External functions - // Public functions /** diff --git a/src/hub/Hub.sol b/src/hub/Hub.sol index 562b3c8..d7d3353 100644 --- a/src/hub/Hub.sol +++ b/src/hub/Hub.sol @@ -280,7 +280,7 @@ contract Hub is Circles, MetadataDefinitions, IHubErrors { } // store the IPFS CIDv0 digest for the avatar metadata - nameRegistry.updateCidV0Digest(msg.sender, _cidV0Digest); + nameRegistry.setCidV0Digest(msg.sender, _cidV0Digest); emit RegisterHuman(msg.sender); } @@ -332,7 +332,7 @@ contract Hub is Circles, MetadataDefinitions, IHubErrors { nameRegistry.registerCustomSymbol(msg.sender, _symbol); // store the IPFS CIDv0 digest for the group metadata - nameRegistry.updateCidV0Digest(msg.sender, _cidV0Digest); + nameRegistry.setCidV0Digest(msg.sender, _cidV0Digest); emit RegisterGroup(msg.sender, _mint, standardTreasury, _name, _symbol); } @@ -359,7 +359,7 @@ contract Hub is Circles, MetadataDefinitions, IHubErrors { nameRegistry.registerCustomSymbol(msg.sender, _symbol); // store the IPFS CIDv0 digest for the group metadata - nameRegistry.updateCidV0Digest(msg.sender, _cidV0Digest); + nameRegistry.setCidV0Digest(msg.sender, _cidV0Digest); emit RegisterGroup(msg.sender, _mint, _treasury, _name, _symbol); } @@ -376,7 +376,7 @@ contract Hub is Circles, MetadataDefinitions, IHubErrors { nameRegistry.registerCustomName(msg.sender, _name); // store the IPFS CIDv0 digest for the organization metadata - nameRegistry.updateCidV0Digest(msg.sender, _cidV0Digest); + nameRegistry.setCidV0Digest(msg.sender, _cidV0Digest); emit RegisterOrganization(msg.sender, _name); } @@ -661,12 +661,19 @@ contract Hub is Circles, MetadataDefinitions, IHubErrors { /** * uri returns the IPFS URI for the ERC1155 token. - * If the + * If the a CID v0 is set in the name registry, it is used and returned as "ipfs://{cidV0}". * @param _id tokenId of the ERC1155 token */ function uri(uint256 _id) public view override returns (string memory uri_) { - // todo: should fallback move into SDK rather than contract ? - // todo: we don't need to override this function if we keep this pattern + address group = _validateAddressFromId(_id, 1); + + // if a CID v0 is set in the name registry, use it and return "ipfs://{cidV0}" + string memory ipfsUri = nameRegistry.getIPFSUri(group); + if (bytes(ipfsUri).length > 0) { + return ipfsUri; + } + + // otherwise return a fallback constant URI // "https://fallback.aboutcircles.com/v1/profile/{id}.json" return super.uri(_id); } @@ -709,7 +716,7 @@ contract Hub is Circles, MetadataDefinitions, IHubErrors { // _groupMint is only called from the public groupMint function, // or from operateFlowMatrix, and both ensure the collateral ids are derived // from an address, so we can cast here without checks. - if (!isPermittedFlow(_group, address(uint160(_collateral[i])))) { + if (!isPermittedFlow(_group, _validateAddressFromId(_collateral[i], 2))) { // Group does not trust collateral. revert CirclesHubFlowEdgeIsNotPermitted(_group, _collateral[i], 0); } @@ -1115,12 +1122,11 @@ contract Hub is Circles, MetadataDefinitions, IHubErrors { } function _validateAddressFromId(uint256 _id, uint8 _code) internal pure returns (address) { - address avatar = address(uint160(_id)); - if (uint256(uint160(avatar)) != _id) { + if (_id > type(uint160).max) { // Invalid Circles identifier, not derived from address revert CirclesIdMustBeDerivedFromAddress(_id, _code); } - return avatar; + return address(uint160(_id)); } /** diff --git a/src/names/Base58Converter.sol b/src/names/Base58Converter.sol index a34383b..cdba0c9 100644 --- a/src/names/Base58Converter.sol +++ b/src/names/Base58Converter.sol @@ -2,20 +2,43 @@ pragma solidity >=0.8.24; contract Base58Converter { + // Constants + string internal constant ALPHABET = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"; + uint256 internal constant IPFS_CIDV0_LENGTH = 46; + + uint256 internal constant ALPHABET_LENGTH = 58; + + // Internal functions + 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; + while (_data > 0 || i == 0) { + uint256 mod = _data % ALPHABET_LENGTH; b58[i++] = bytes(ALPHABET)[mod]; - _data = _data / 58; + _data = _data / ALPHABET_LENGTH; } // Reverse the string since the encoding works backwards return string(_reverse(b58, i)); } + function toBase58WithPadding(uint256 _data) internal pure returns (string memory) { + bytes memory b58 = new bytes(IPFS_CIDV0_LENGTH); // Fixed length for CIDv0 + uint256 i = 0; + while (_data > 0 || i == 0) { + uint256 mod = _data % ALPHABET_LENGTH; + b58[i++] = bytes(ALPHABET)[mod]; + _data /= ALPHABET_LENGTH; + } + while (i < IPFS_CIDV0_LENGTH) { + // Ensure the output is exactly 46 characters + b58[i++] = bytes(ALPHABET)[0]; // '1' in base58 represents the value 0 + } + 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++) { diff --git a/src/names/INameRegistry.sol b/src/names/INameRegistry.sol index a79a237..6a888b4 100644 --- a/src/names/INameRegistry.sol +++ b/src/names/INameRegistry.sol @@ -2,12 +2,13 @@ pragma solidity >=0.8.24; interface INameRegistry { - function updateCidV0Digest(address avatar, bytes32 cidVoDigest) external; + function setCidV0Digest(address avatar, bytes32 cidVoDigest) external; function registerCustomName(address avatar, string calldata name) external; function registerCustomSymbol(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 getIPFSUri(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); diff --git a/src/names/NameRegistry.sol b/src/names/NameRegistry.sol index 9ab8d58..3a25710 100644 --- a/src/names/NameRegistry.sol +++ b/src/names/NameRegistry.sol @@ -27,6 +27,11 @@ contract NameRegistry is Base58Converter, INameRegistry, INameRegistryErrors, IC */ string public constant DEFAULT_CIRCLES_SYMBOL = "RING"; + /** + * @dev The IPFS protocol prefix + */ + string private constant IPFS_PROTOCOL = "ipfs://Qm"; + // State variables /** @@ -125,12 +130,26 @@ contract NameRegistry is Base58Converter, INameRegistry, INameRegistryErrors, IC emit RegisterShortName(msg.sender, shortName, _nonce); } - function updateCidV0Digest(address _avatar, bytes32 _cidV0Digest) external onlyHub(0) { + function setCidV0Digest(address _avatar, bytes32 _cidV0Digest) external onlyHub(0) { avatarToCidV0Digest[_avatar] = _cidV0Digest; emit CidV0(_avatar, _cidV0Digest); } + function updateCidV0Digest(bytes32 _cidV0Digest) external mustBeRegistered(msg.sender, 2) { + avatarToCidV0Digest[msg.sender] = _cidV0Digest; + + emit CidV0(msg.sender, _cidV0Digest); + } + + function getIPFSUri(address _avatar) external view returns (string memory) { + bytes32 cidV0Digest = avatarToCidV0Digest[_avatar]; + if (cidV0Digest == bytes32(0)) { + return ""; + } + return string(abi.encodePacked(IPFS_PROTOCOL, toBase58WithPadding(uint256(cidV0Digest)))); + } + function registerCustomName(address _avatar, string calldata _name) external onlyHub(1) { if (bytes(_name).length == 0) { // if name is left empty, it will default to default name "Circles-" @@ -153,7 +172,7 @@ contract NameRegistry is Base58Converter, INameRegistry, INameRegistryErrors, IC customSymbols[_avatar] = _symbol; } - function name(address _avatar) external view mustBeRegistered(_avatar, 1) returns (string memory) { + function name(address _avatar) external view mustBeRegistered(_avatar, 3) returns (string memory) { if (!hub.isHuman(_avatar)) { // groups and organizations can have set a custom name string memory customName = customNames[_avatar]; @@ -173,7 +192,7 @@ contract NameRegistry is Base58Converter, INameRegistry, INameRegistryErrors, IC return string(abi.encodePacked(DEFAULT_CIRCLES_NAME_PREFIX, base58ShortName)); } - function symbol(address _avatar) external view mustBeRegistered(_avatar, 2) returns (string memory) { + function symbol(address _avatar) external view mustBeRegistered(_avatar, 4) returns (string memory) { if (hub.isOrganization(_avatar)) { revert CirclesNamesOrganizationHasNoSymbol(_avatar, 0); } From 783b4a5af7c953af4edbd4c0fbf53f446845e500 Mon Sep 17 00:00:00 2001 From: Benjamin Bollen Date: Wed, 17 Apr 2024 17:48:53 +0100 Subject: [PATCH 11/17] (hub): make private values that can be read from verification constructor to reduce build space --- src/circles/Demurrage.sol | 2 +- src/hub/Hub.sol | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/circles/Demurrage.sol b/src/circles/Demurrage.sol index eb8da73..0e9aaff 100644 --- a/src/circles/Demurrage.sol +++ b/src/circles/Demurrage.sol @@ -92,7 +92,7 @@ contract Demurrage is ICirclesERC1155Errors { * or midnight prior of the same day of deployment, marking the start of the first day * where there was no inflation on one CRC per hour. */ - uint256 public inflationDayZero; + uint256 internal inflationDayZero; /** * @dev Store a lookup table T(n) for computing issuance. diff --git a/src/hub/Hub.sol b/src/hub/Hub.sol index d7d3353..fcc4c99 100644 --- a/src/hub/Hub.sol +++ b/src/hub/Hub.sol @@ -82,22 +82,22 @@ contract Hub is Circles, MetadataDefinitions, IHubErrors { /** * @notice The Hub v1 contract address. */ - IHubV1 public immutable hubV1; + IHubV1 internal immutable hubV1; /** * @notice The name registry contract address. */ - INameRegistry public nameRegistry; + INameRegistry internal nameRegistry; /** * @notice The address of the migration contract for v1 Circles. */ - address public migration; + address internal migration; /** * @notice The address of the Lift ERC20 contract. */ - IERC20Lift public liftERC20; + IERC20Lift internal liftERC20; /** * @notice The timestamp of the start of the invitation-only period. @@ -106,13 +106,13 @@ contract Hub is Circles, MetadataDefinitions, IHubErrors { * new avatars can be invited by registered avatars. After this time * only registered avatars can invite new avatars. */ - uint256 public immutable invitationOnlyTime; + uint256 internal immutable invitationOnlyTime; /** * @notice The standard treasury contract address used when * registering a (non-custom) group. */ - address public standardTreasury; + address internal standardTreasury; /** * @notice The mapping of registered avatar addresses to the next avatar address, From d82a290ed050e4d5fd108e972f90216876c16d9e Mon Sep 17 00:00:00 2001 From: Benjamin Bollen Date: Thu, 18 Apr 2024 21:53:45 +0100 Subject: [PATCH 12/17] (nameRegistry): refactor for better internal fcts; preparation to make MockNameRegistry for tests avoiding Hubv2 --- src/names/NameRegistry.sol | 89 ++++++++++++++++++--------------- test/names/MockNameRegistry.sol | 8 +++ test/names/NameRegistry.t.sol | 11 ++++ 3 files changed, 69 insertions(+), 39 deletions(-) create mode 100644 test/names/MockNameRegistry.sol create mode 100644 test/names/NameRegistry.t.sol diff --git a/src/names/NameRegistry.sol b/src/names/NameRegistry.sol index 3a25710..aac92b8 100644 --- a/src/names/NameRegistry.sol +++ b/src/names/NameRegistry.sol @@ -28,7 +28,7 @@ contract NameRegistry is Base58Converter, INameRegistry, INameRegistryErrors, IC string public constant DEFAULT_CIRCLES_SYMBOL = "RING"; /** - * @dev The IPFS protocol prefix + * @dev The IPFS protocol prefix for cid v0 resolution */ string private constant IPFS_PROTOCOL = "ipfs://Qm"; @@ -97,14 +97,7 @@ contract NameRegistry is Base58Converter, INameRegistry, INameRegistryErrors, IC * @notice Register a short name for the avatar */ function registerShortName() external mustBeRegistered(msg.sender, 0) { - (uint72 shortName, uint256 nonce) = searchShortName(msg.sender); - - // assign the name to the address - shortNames[msg.sender] = shortName; - // assign the address to the name - shortNameToAvatar[shortName] = msg.sender; - - emit RegisterShortName(msg.sender, shortName, nonce); + _registerShortName(); } /** @@ -112,42 +105,15 @@ contract NameRegistry is Base58Converter, INameRegistry, INameRegistryErrors, IC * @param _nonce nonce to be used in the calculation */ function registerShortNameWithNonce(uint256 _nonce) external mustBeRegistered(msg.sender, 1) { - if (shortNames[msg.sender] != uint72(0)) { - revert CirclesNamesShortNameAlreadyAssigned(msg.sender, shortNames[msg.sender], 0); - } - - uint72 shortName = calculateShortNameWithNonce(msg.sender, _nonce); - - if (shortNameToAvatar[shortName] != address(0)) { - revert CirclesNamesShortNameWithNonceTaken(msg.sender, _nonce, shortName, shortNameToAvatar[shortName]); - } - - // assign the name to the address - shortNames[msg.sender] = shortName; - // assign the address to the name - shortNameToAvatar[shortName] = msg.sender; - - emit RegisterShortName(msg.sender, shortName, _nonce); + _registerShortNameWithNonce(_nonce); } function setCidV0Digest(address _avatar, bytes32 _cidV0Digest) external onlyHub(0) { - avatarToCidV0Digest[_avatar] = _cidV0Digest; - - emit CidV0(_avatar, _cidV0Digest); + _setCidV0Digest(_avatar, _cidV0Digest); } function updateCidV0Digest(bytes32 _cidV0Digest) external mustBeRegistered(msg.sender, 2) { - avatarToCidV0Digest[msg.sender] = _cidV0Digest; - - emit CidV0(msg.sender, _cidV0Digest); - } - - function getIPFSUri(address _avatar) external view returns (string memory) { - bytes32 cidV0Digest = avatarToCidV0Digest[_avatar]; - if (cidV0Digest == bytes32(0)) { - return ""; - } - return string(abi.encodePacked(IPFS_PROTOCOL, toBase58WithPadding(uint256(cidV0Digest)))); + _setCidV0Digest(msg.sender, _cidV0Digest); } function registerCustomName(address _avatar, string calldata _name) external onlyHub(1) { @@ -209,6 +175,14 @@ contract NameRegistry is Base58Converter, INameRegistry, INameRegistryErrors, IC return DEFAULT_CIRCLES_SYMBOL; } + function getIPFSUri(address _avatar) external view returns (string memory) { + bytes32 cidV0Digest = avatarToCidV0Digest[_avatar]; + if (cidV0Digest == bytes32(0)) { + return ""; + } + return string(abi.encodePacked(IPFS_PROTOCOL, toBase58WithPadding(uint256(cidV0Digest)))); + } + // Public functions /** @@ -308,4 +282,41 @@ contract NameRegistry is Base58Converter, INameRegistry, INameRegistryErrors, IC } return true; } + + // Internal functions + + function _registerShortName() internal { + (uint72 shortName, uint256 nonce) = searchShortName(msg.sender); + + _storeShortName(msg.sender, shortName, nonce); + } + + function _registerShortNameWithNonce(uint256 _nonce) internal { + if (shortNames[msg.sender] != uint72(0)) { + revert CirclesNamesShortNameAlreadyAssigned(msg.sender, shortNames[msg.sender], 0); + } + + uint72 shortName = calculateShortNameWithNonce(msg.sender, _nonce); + + if (shortNameToAvatar[shortName] != address(0)) { + revert CirclesNamesShortNameWithNonceTaken(msg.sender, _nonce, shortName, shortNameToAvatar[shortName]); + } + + _storeShortName(msg.sender, shortName, _nonce); + } + + function _storeShortName(address _avatar, uint72 _shortName, uint256 _nonce) internal { + // assign the name to the address + shortNames[_avatar] = _shortName; + // assign the address to the name + shortNameToAvatar[_shortName] = _avatar; + + emit RegisterShortName(_avatar, _shortName, _nonce); + } + + function _setCidV0Digest(address _avatar, bytes32 _cidV0Digest) internal { + avatarToCidV0Digest[_avatar] = _cidV0Digest; + + emit CidV0(_avatar, _cidV0Digest); + } } diff --git a/test/names/MockNameRegistry.sol b/test/names/MockNameRegistry.sol new file mode 100644 index 0000000..dd331d6 --- /dev/null +++ b/test/names/MockNameRegistry.sol @@ -0,0 +1,8 @@ +// SPDX-License-Identifier: AGPL-3.0-only +pragma solidity >=0.8.24; + +import "../../src/names/NameRegistry.sol"; + +contract MockNameRegistry is NameRegistry { + constructor() NameRegistry(IHubV2(address(1))) {} +} diff --git a/test/names/NameRegistry.t.sol b/test/names/NameRegistry.t.sol new file mode 100644 index 0000000..5a07455 --- /dev/null +++ b/test/names/NameRegistry.t.sol @@ -0,0 +1,11 @@ +// SPDX-License-Identifier: AGPL-3.0-only +pragma solidity >=0.8.24; + +import {Test} from "forge-std/Test.sol"; +import {StdCheats} from "forge-std/StdCheats.sol"; +import "forge-std/console.sol"; +import "./MockNameRegistry.sol"; + +contract NamesTest is Test { + function setUp() public {} +} From d9b07acaacc785479e3fee781f1b7df1cfa6b8d5 Mon Sep 17 00:00:00 2001 From: Benjamin Bollen Date: Tue, 30 Apr 2024 15:00:39 +0100 Subject: [PATCH 13/17] (test/names): cutting short effort to handle decoding base58 in tests --- ...nsfer => TCIP010-erc1155-path-transfer.md} | 0 test/names/Base58Helper.sol | 82 +++++++++++++++++++ test/names/MockNameRegistry.sol | 36 ++++++++ test/names/NameRegistry.t.sol | 44 +++++++++- 4 files changed, 160 insertions(+), 2 deletions(-) rename specifications/{TCIP010-erc1155-path-transfer => TCIP010-erc1155-path-transfer.md} (100%) create mode 100644 test/names/Base58Helper.sol diff --git a/specifications/TCIP010-erc1155-path-transfer b/specifications/TCIP010-erc1155-path-transfer.md similarity index 100% rename from specifications/TCIP010-erc1155-path-transfer rename to specifications/TCIP010-erc1155-path-transfer.md diff --git a/test/names/Base58Helper.sol b/test/names/Base58Helper.sol new file mode 100644 index 0000000..f247e20 --- /dev/null +++ b/test/names/Base58Helper.sol @@ -0,0 +1,82 @@ +// SPDX-License-Identifier: AGPL-3.0-only +pragma solidity >=0.8.24; + +import "forge-std/console.sol"; + +/** + * Helper contract to do the base58 decoding in solidity unit tests; + * this should only be used for tests, in production this conversion to bytes + * should be done off-chain with a proper library. + */ +contract Base58Decode { + + function base58Decode(string memory source) public view returns (bytes memory) { + bytes memory bSource = bytes(source); + uint256 base = 58; + uint256 result = 0; + uint256 multi = 1; + + // Mapping of base58 characters to their integer values + uint8[58] memory base58Map = [ + 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, + 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5A, + 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6A, 0x6B, 0x6D, 0x6E, 0x6F, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A + ]; + + // Adjust to ignore the "Qm" prefix if it exists and length is 34 bytes + uint256 startIndex = 0; + if (bSource.length == 46 && bSource[0] == 0x51 && bSource[1] == 0x6D) { // 'Q' == 0x51 and 'm' == 0x6D + startIndex = 2; + } else if (bSource.length != 44) { + revert("Invalid CIDv0 length for IPFS decoding."); + } + + console.log("startIndex:", startIndex); + console.log("CIDv0 inside length:", bSource.length); + + // for (uint256 i = startIndex; i < bSource.length; i++) { + // uint256 value = indexOf(uint8(bSource[i]), base58Map); + // result += value * multi; + // if (i ) + // multi *= base; + // console.log("index:", i - startIndex + 1); + // console.log("charater:", uint8(bSource[i]), "value:", value); + // console.log("result:", result, "multi:", multi); + // } + + uint256 j = 0; + for (uint256 i = bSource.length; i > startIndex; i--) { + uint256 value = indexOf(uint8(bSource[i - 1]), base58Map); + result += value * multi; + if (i > startIndex + 1) { + // skip the last iteration to avoid overflow + // 58^44 > 2^256 - 1 + multi *= base; + console.log("iteration:", j++); + } else { + console.log("last iteration"); + } + console.log("index:", bSource.length - i + 1); + console.log("charater:", uint8(bSource[i - 1]), "value:", value); + console.log("result:", result, "multi:", multi); + } + + return toBytes(result); + } + + function indexOf(uint8 char, uint8[58] memory map) private pure returns (uint256) { + for(uint i = 0; i < map.length; i++) { + if(map[i] == char) { + return i; + } + } + revert("Character not in base58 map."); + } + + function toBytes(uint256 x) private pure returns (bytes memory b) { + b = new bytes(32); + assembly { + mstore(add(b, 32), x) + } + } +} diff --git a/test/names/MockNameRegistry.sol b/test/names/MockNameRegistry.sol index dd331d6..7c53eda 100644 --- a/test/names/MockNameRegistry.sol +++ b/test/names/MockNameRegistry.sol @@ -5,4 +5,40 @@ import "../../src/names/NameRegistry.sol"; contract MockNameRegistry is NameRegistry { constructor() NameRegistry(IHubV2(address(1))) {} + + // External functions + + function registerShortNameNoChecks() external { + _registerShortName(); + } + + function registerShortNameWithNonceNoChecks(uint256 _nonce) external { + _registerShortNameWithNonce(_nonce); + } + + function registerCustomNameNoChecks(address _avatar, string calldata _name) external { + if (bytes(_name).length == 0) { + // if name is left empty, it will default to default name "Circles-" + return; + } + if (!isValidName(_name)) { + revert CirclesNamesInvalidName(_avatar, _name, 0); + } + customNames[_avatar] = _name; + } + + function registerCustomSymbolNoChecks(address _avatar, string calldata _symbol) external { + if (bytes(_symbol).length == 0) { + // if symbol is left empty, it will default to default symbol "CRC" + return; + } + if (!isValidSymbol(_symbol)) { + revert CirclesNamesInvalidName(_avatar, _symbol, 1); + } + customSymbols[_avatar] = _symbol; + } + + function setCidV0DigestNoChecks(address _avatar, bytes32 _dicV0Digest) external { + _setCidV0Digest(_avatar, _dicV0Digest); + } } diff --git a/test/names/NameRegistry.t.sol b/test/names/NameRegistry.t.sol index 5a07455..0847bdd 100644 --- a/test/names/NameRegistry.t.sol +++ b/test/names/NameRegistry.t.sol @@ -4,8 +4,48 @@ pragma solidity >=0.8.24; import {Test} from "forge-std/Test.sol"; import {StdCheats} from "forge-std/StdCheats.sol"; import "forge-std/console.sol"; +import "../setup/HumanRegistration.sol"; import "./MockNameRegistry.sol"; +import "./base58Helper.sol"; -contract NamesTest is Test { - function setUp() public {} +contract NamesTest is Test, HumanRegistration, Base58Decode { + // Constants + + // IPFS hash for Ubuntu 20.04, random CIDv0 + string constant IPFS_CID_V0 = "QmPK1s3pNYLi9ERiq3BDxKa4XosgWwFRQUydHUtz4YgpqB"; + // using https://cid.ipfs.tech/#QmPK1s3pNYLi9ERiq3BDxKa4XosgWwFRQUydHUtz4YgpqB + // this is the 32 bytes hash digest + bytes32 constant DECODED_UINT = 0x0E7071C59DF3B9454D1D18A15270AA36D54F89606A576DC621757AFD44AD1D2E; + + // State variables + + MockNameRegistry mockNameRegistry; + + bytes32 cidBytes; + + // Constructor + + constructor() HumanRegistration(4) {} + + // Setup + + function setUp() public { + mockNameRegistry = new MockNameRegistry(); + // Convert CIDv0 to bytes32 (should be done off-chain in production) + console.log("CIDv0 length", bytes(IPFS_CID_V0).length); + cidBytes = convertCidV0ToBytes32(IPFS_CID_V0); + } + + // Tests + + function testCidV0Digest() public { + mockNameRegistry.setCidV0DigestNoChecks(addresses[0], cidBytes); + } + + // Helper functions + + function convertCidV0ToBytes32(string memory _cidV0) internal view returns (bytes32) { + bytes memory decodedBytes = base58Decode(_cidV0); + console.log("CIDv0 length:", decodedBytes.length); + } } From 53a796c2ccf389dd36e1f261bc915dd3a98a80c8 Mon Sep 17 00:00:00 2001 From: Benjamin Bollen Date: Tue, 30 Apr 2024 19:02:39 +0100 Subject: [PATCH 14/17] (NameRegistry): simplify to metadata digest storage, test short names --- src/circles/Demurrage.sol | 2 +- src/hub/Hub.sol | 57 ++++++++++---------------- src/names/Base58Converter.sol | 36 +++++++++-------- src/names/INameRegistry.sol | 4 +- src/names/NameRegistry.sol | 47 +++++++++++----------- test/names/Base58Helper.sol | 71 +++++++++++++++++++++++++++++---- test/names/MockNameRegistry.sol | 10 ++++- test/names/NameRegistry.t.sol | 31 ++++++++------ 8 files changed, 156 insertions(+), 102 deletions(-) diff --git a/src/circles/Demurrage.sol b/src/circles/Demurrage.sol index 0e9aaff..eb8da73 100644 --- a/src/circles/Demurrage.sol +++ b/src/circles/Demurrage.sol @@ -92,7 +92,7 @@ contract Demurrage is ICirclesERC1155Errors { * or midnight prior of the same day of deployment, marking the start of the first day * where there was no inflation on one CRC per hour. */ - uint256 internal inflationDayZero; + uint256 public inflationDayZero; /** * @dev Store a lookup table T(n) for computing issuance. diff --git a/src/hub/Hub.sol b/src/hub/Hub.sol index fcc4c99..b264553 100644 --- a/src/hub/Hub.sol +++ b/src/hub/Hub.sol @@ -222,8 +222,8 @@ contract Hub is Circles, MetadataDefinitions, IHubErrors { * or in unix time 1602786330 (deployment at 6:25:30 pm UTC) - 66330 (offset to midnight) = 1602720000. * @param _standardTreasury address of the standard treasury contract * @param _bootstrapTime duration of the bootstrap period (for v1 registration) in seconds - * @param _fallbackUri fallback URI string for the ERC1155 metadata, - * (todo: eg. "https://fallback.aboutcircles.com/v1/circles/{id}.json") + * @param _gatewayUrl gateway URL string for the ERC1155 metadata mirroring IPFS metadata storage + * (eg. "https://gateway.aboutcircles.com/v2/circles/{id}.json") */ constructor( IHubV1 _hubV1, @@ -233,8 +233,8 @@ contract Hub is Circles, MetadataDefinitions, IHubErrors { address _standardTreasury, uint256 _inflationDayZero, uint256 _bootstrapTime, - string memory _fallbackUri - ) Circles(_inflationDayZero, _fallbackUri) { + string memory _gatewayUrl + ) Circles(_inflationDayZero, _gatewayUrl) { if (address(_hubV1) == address(0)) { revert CirclesAddressCannotBeZero(0); } @@ -269,18 +269,20 @@ contract Hub is Circles, MetadataDefinitions, IHubErrors { /** * @notice Register human allows to register an avatar for a human, * if they have a stopped v1 Circles contract, during the bootstrap period. - * @param _cidV0Digest (optional) IPFS CIDv0 digest for the avatar metadata + * @param _metatdataDigest (optional) sha256 metadata digest for the avatar metadata * should follow ERC1155 metadata standard. */ - function registerHuman(bytes32 _cidV0Digest) external onlyDuringBootstrap(0) { + function registerHuman(bytes32 _metatdataDigest) external onlyDuringBootstrap(0) { // only available for v1 users with stopped v1 mint, for initial bootstrap period address v1CirclesStatus = _registerHuman(msg.sender); if (v1CirclesStatus != CIRCLES_STOPPED_V1) { revert CirclesHubRegisterAvatarV1MustBeStopped(msg.sender, 0); } - // store the IPFS CIDv0 digest for the avatar metadata - nameRegistry.setCidV0Digest(msg.sender, _cidV0Digest); + // store the metatdata digest for the avatar metadata + if (_metatdataDigest != bytes32(0)) { + nameRegistry.setMetadataDigest(msg.sender, _metatdataDigest); + } emit RegisterHuman(msg.sender); } @@ -320,9 +322,9 @@ contract Hub is Circles, MetadataDefinitions, IHubErrors { * @param _mint mint address will be called before minting group circles * @param _name immutable name of the group Circles * @param _symbol immutable symbol of the group Circles - * @param _cidV0Digest IPFS CIDv0 digest for the group metadata + * @param _metatdataDigest sha256 digest for the group metadata */ - function registerGroup(address _mint, string calldata _name, string calldata _symbol, bytes32 _cidV0Digest) + function registerGroup(address _mint, string calldata _name, string calldata _symbol, bytes32 _metatdataDigest) external { _registerGroup(msg.sender, _mint, standardTreasury, _name, _symbol); @@ -332,7 +334,7 @@ contract Hub is Circles, MetadataDefinitions, IHubErrors { nameRegistry.registerCustomSymbol(msg.sender, _symbol); // store the IPFS CIDv0 digest for the group metadata - nameRegistry.setCidV0Digest(msg.sender, _cidV0Digest); + nameRegistry.setMetadataDigest(msg.sender, _metatdataDigest); emit RegisterGroup(msg.sender, _mint, standardTreasury, _name, _symbol); } @@ -343,14 +345,14 @@ contract Hub is Circles, MetadataDefinitions, IHubErrors { * @param _treasury treasury address for receiving collateral * @param _name immutable name of the group Circles * @param _symbol immutable symbol of the group Circles - * @param _cidV0Digest IPFS CIDv0 digest for the group metadata + * @param _metatdataDigest metadata digest for the group metadata */ function registerCustomGroup( address _mint, address _treasury, string calldata _name, string calldata _symbol, - bytes32 _cidV0Digest + bytes32 _metatdataDigest ) external { _registerGroup(msg.sender, _mint, _treasury, _name, _symbol); @@ -358,8 +360,8 @@ contract Hub is Circles, MetadataDefinitions, IHubErrors { nameRegistry.registerCustomName(msg.sender, _name); nameRegistry.registerCustomSymbol(msg.sender, _symbol); - // store the IPFS CIDv0 digest for the group metadata - nameRegistry.setCidV0Digest(msg.sender, _cidV0Digest); + // store the metatdata digest for the group metadata + nameRegistry.setMetadataDigest(msg.sender, _metatdataDigest); emit RegisterGroup(msg.sender, _mint, _treasury, _name, _symbol); } @@ -367,16 +369,16 @@ contract Hub is Circles, MetadataDefinitions, IHubErrors { /** * @notice Register organization allows to register an organization avatar. * @param _name name of the organization - * @param _cidV0Digest IPFS CIDv0 digest for the organization metadata + * @param _metatdataDigest Metadata digest for the organization metadata */ - function registerOrganization(string calldata _name, bytes32 _cidV0Digest) external { + function registerOrganization(string calldata _name, bytes32 _metatdataDigest) external { _insertAvatar(msg.sender); // for organizations, only register possible custom name nameRegistry.registerCustomName(msg.sender, _name); // store the IPFS CIDv0 digest for the organization metadata - nameRegistry.setCidV0Digest(msg.sender, _cidV0Digest); + nameRegistry.setMetadataDigest(msg.sender, _metatdataDigest); emit RegisterOrganization(msg.sender, _name); } @@ -659,25 +661,6 @@ contract Hub is Circles, MetadataDefinitions, IHubErrors { return uint256(trustMarkers[_circlesAvatar][_to].expiry) >= block.timestamp; } - /** - * uri returns the IPFS URI for the ERC1155 token. - * If the a CID v0 is set in the name registry, it is used and returned as "ipfs://{cidV0}". - * @param _id tokenId of the ERC1155 token - */ - function uri(uint256 _id) public view override returns (string memory uri_) { - address group = _validateAddressFromId(_id, 1); - - // if a CID v0 is set in the name registry, use it and return "ipfs://{cidV0}" - string memory ipfsUri = nameRegistry.getIPFSUri(group); - if (bytes(ipfsUri).length > 0) { - return ipfsUri; - } - - // otherwise return a fallback constant URI - // "https://fallback.aboutcircles.com/v1/profile/{id}.json" - return super.uri(_id); - } - // Internal functions /** diff --git a/src/names/Base58Converter.sol b/src/names/Base58Converter.sol index cdba0c9..844475c 100644 --- a/src/names/Base58Converter.sol +++ b/src/names/Base58Converter.sol @@ -6,15 +6,19 @@ contract Base58Converter { string internal constant ALPHABET = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"; - uint256 internal constant IPFS_CIDV0_LENGTH = 46; + // uint256 internal constant IPFS_CIDV0_LENGTH = 46; uint256 internal constant ALPHABET_LENGTH = 58; // Internal functions function toBase58(uint256 _data) internal pure returns (string memory) { - bytes memory b58 = new bytes(64); // More than enough length + // Initialize enough length, only called in view functions + // so no need to optimize gas costs + bytes memory b58 = new bytes(64); uint256 i = 0; + // Ensure the loop runs at least once, + // even if the input is 0 return "1" while (_data > 0 || i == 0) { uint256 mod = _data % ALPHABET_LENGTH; b58[i++] = bytes(ALPHABET)[mod]; @@ -24,20 +28,20 @@ contract Base58Converter { return string(_reverse(b58, i)); } - function toBase58WithPadding(uint256 _data) internal pure returns (string memory) { - bytes memory b58 = new bytes(IPFS_CIDV0_LENGTH); // Fixed length for CIDv0 - uint256 i = 0; - while (_data > 0 || i == 0) { - uint256 mod = _data % ALPHABET_LENGTH; - b58[i++] = bytes(ALPHABET)[mod]; - _data /= ALPHABET_LENGTH; - } - while (i < IPFS_CIDV0_LENGTH) { - // Ensure the output is exactly 46 characters - b58[i++] = bytes(ALPHABET)[0]; // '1' in base58 represents the value 0 - } - return string(_reverse(b58, i)); - } + // function toBase58WithPadding(uint256 _data) internal pure returns (string memory) { + // bytes memory b58 = new bytes(IPFS_CIDV0_LENGTH); // Fixed length for CIDv0 + // uint256 i = 0; + // while (_data > 0 || i == 0) { + // uint256 mod = _data % ALPHABET_LENGTH; + // b58[i++] = bytes(ALPHABET)[mod]; + // _data /= ALPHABET_LENGTH; + // } + // while (i < IPFS_CIDV0_LENGTH) { + // // Ensure the output is exactly 46 characters + // b58[i++] = bytes(ALPHABET)[0]; // '1' in base58 represents the value 0 + // } + // return string(_reverse(b58, i)); + // } function _reverse(bytes memory _b, uint256 _len) internal pure returns (bytes memory) { bytes memory reversed = new bytes(_len); diff --git a/src/names/INameRegistry.sol b/src/names/INameRegistry.sol index 6a888b4..496c121 100644 --- a/src/names/INameRegistry.sol +++ b/src/names/INameRegistry.sol @@ -2,13 +2,13 @@ pragma solidity >=0.8.24; interface INameRegistry { - function setCidV0Digest(address avatar, bytes32 cidVoDigest) external; + function setMetadataDigest(address avatar, bytes32 metadataDigest) external; function registerCustomName(address avatar, string calldata name) external; function registerCustomSymbol(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 getIPFSUri(address _avatar) external view returns (string memory); + function getMetadataDigest(address _avatar) external view returns (bytes32); function isValidName(string calldata name) external pure returns (bool); function isValidSymbol(string calldata symbol) external pure returns (bool); diff --git a/src/names/NameRegistry.sol b/src/names/NameRegistry.sol index aac92b8..380adce 100644 --- a/src/names/NameRegistry.sol +++ b/src/names/NameRegistry.sol @@ -55,15 +55,16 @@ contract NameRegistry is Base58Converter, INameRegistry, INameRegistryErrors, IC mapping(address => string) public customSymbols; /** - * @notice avatarToCidV0Digest is a mapping of avatar to the IPFS CIDv0 digest. + * @notice avatarToMetaDataDigest is a mapping of avatar to the sha256 digest + * of their latest ERC1155 metadata. */ - mapping(address => bytes32) public avatarToCidV0Digest; + mapping(address => bytes32) public avatarToMetaDataDigest; // Events event RegisterShortName(address indexed avatar, uint72 shortName, uint256 nonce); - event CidV0(address indexed avatar, bytes32 cidV0Digest); + event UpdateMetadataDigest(address indexed avatar, bytes32 metadataDigest); // Modifiers @@ -108,12 +109,12 @@ contract NameRegistry is Base58Converter, INameRegistry, INameRegistryErrors, IC _registerShortNameWithNonce(_nonce); } - function setCidV0Digest(address _avatar, bytes32 _cidV0Digest) external onlyHub(0) { - _setCidV0Digest(_avatar, _cidV0Digest); + function setMetadataDigest(address _avatar, bytes32 _metadataDigest) external onlyHub(0) { + _setMetadataDigest(_avatar, _metadataDigest); } - function updateCidV0Digest(bytes32 _cidV0Digest) external mustBeRegistered(msg.sender, 2) { - _setCidV0Digest(msg.sender, _cidV0Digest); + function updateMetadataDigest(bytes32 _metadataDigest) external mustBeRegistered(msg.sender, 2) { + _setMetadataDigest(msg.sender, _metadataDigest); } function registerCustomName(address _avatar, string calldata _name) external onlyHub(1) { @@ -149,13 +150,7 @@ contract NameRegistry is Base58Converter, INameRegistry, INameRegistryErrors, IC // otherwise, use the default name for groups and organizations } // for personal Circles use default name - uint72 shortName = shortNames[_avatar]; - if (shortName == uint72(0)) { - string memory base58FullAddress = toBase58(uint256(uint160(_avatar))); - return string(abi.encodePacked(DEFAULT_CIRCLES_NAME_PREFIX, base58FullAddress)); - } - string memory base58ShortName = toBase58(uint256(shortName)); - return string(abi.encodePacked(DEFAULT_CIRCLES_NAME_PREFIX, base58ShortName)); + return _getShortOrLongName(_avatar); } function symbol(address _avatar) external view mustBeRegistered(_avatar, 4) returns (string memory) { @@ -175,12 +170,8 @@ contract NameRegistry is Base58Converter, INameRegistry, INameRegistryErrors, IC return DEFAULT_CIRCLES_SYMBOL; } - function getIPFSUri(address _avatar) external view returns (string memory) { - bytes32 cidV0Digest = avatarToCidV0Digest[_avatar]; - if (cidV0Digest == bytes32(0)) { - return ""; - } - return string(abi.encodePacked(IPFS_PROTOCOL, toBase58WithPadding(uint256(cidV0Digest)))); + function getMetadataDigest(address _avatar) external view returns (bytes32) { + return avatarToMetaDataDigest[_avatar]; } // Public functions @@ -314,9 +305,19 @@ contract NameRegistry is Base58Converter, INameRegistry, INameRegistryErrors, IC emit RegisterShortName(_avatar, _shortName, _nonce); } - function _setCidV0Digest(address _avatar, bytes32 _cidV0Digest) internal { - avatarToCidV0Digest[_avatar] = _cidV0Digest; + function _getShortOrLongName(address _avatar) internal view returns (string memory) { + uint72 shortName = shortNames[_avatar]; + if (shortName == uint72(0)) { + string memory base58FullAddress = toBase58(uint256(uint160(_avatar))); + return string(abi.encodePacked(DEFAULT_CIRCLES_NAME_PREFIX, base58FullAddress)); + } + string memory base58ShortName = toBase58(uint256(shortName)); + return string(abi.encodePacked(DEFAULT_CIRCLES_NAME_PREFIX, base58ShortName)); + } + + function _setMetadataDigest(address _avatar, bytes32 _metadataDigest) internal { + avatarToMetaDataDigest[_avatar] = _metadataDigest; - emit CidV0(_avatar, _cidV0Digest); + emit UpdateMetadataDigest(_avatar, _metadataDigest); } } diff --git a/test/names/Base58Helper.sol b/test/names/Base58Helper.sol index f247e20..c99c130 100644 --- a/test/names/Base58Helper.sol +++ b/test/names/Base58Helper.sol @@ -6,10 +6,9 @@ import "forge-std/console.sol"; /** * Helper contract to do the base58 decoding in solidity unit tests; * this should only be used for tests, in production this conversion to bytes - * should be done off-chain with a proper library. + * should be done off-chain with a proper library. */ contract Base58Decode { - function base58Decode(string memory source) public view returns (bytes memory) { bytes memory bSource = bytes(source); uint256 base = 58; @@ -18,14 +17,70 @@ contract Base58Decode { // Mapping of base58 characters to their integer values uint8[58] memory base58Map = [ - 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, - 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5A, - 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6A, 0x6B, 0x6D, 0x6E, 0x6F, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A + 0x31, + 0x32, + 0x33, + 0x34, + 0x35, + 0x36, + 0x37, + 0x38, + 0x39, + 0x41, + 0x42, + 0x43, + 0x44, + 0x45, + 0x46, + 0x47, + 0x48, + 0x4A, + 0x4B, + 0x4C, + 0x4D, + 0x4E, + 0x50, + 0x51, + 0x52, + 0x53, + 0x54, + 0x55, + 0x56, + 0x57, + 0x58, + 0x59, + 0x5A, + 0x61, + 0x62, + 0x63, + 0x64, + 0x65, + 0x66, + 0x67, + 0x68, + 0x69, + 0x6A, + 0x6B, + 0x6D, + 0x6E, + 0x6F, + 0x70, + 0x71, + 0x72, + 0x73, + 0x74, + 0x75, + 0x76, + 0x77, + 0x78, + 0x79, + 0x7A ]; // Adjust to ignore the "Qm" prefix if it exists and length is 34 bytes uint256 startIndex = 0; - if (bSource.length == 46 && bSource[0] == 0x51 && bSource[1] == 0x6D) { // 'Q' == 0x51 and 'm' == 0x6D + if (bSource.length == 46 && bSource[0] == 0x51 && bSource[1] == 0x6D) { + // 'Q' == 0x51 and 'm' == 0x6D startIndex = 2; } else if (bSource.length != 44) { revert("Invalid CIDv0 length for IPFS decoding."); @@ -65,8 +120,8 @@ contract Base58Decode { } function indexOf(uint8 char, uint8[58] memory map) private pure returns (uint256) { - for(uint i = 0; i < map.length; i++) { - if(map[i] == char) { + for (uint256 i = 0; i < map.length; i++) { + if (map[i] == char) { return i; } } diff --git a/test/names/MockNameRegistry.sol b/test/names/MockNameRegistry.sol index 7c53eda..c0b851b 100644 --- a/test/names/MockNameRegistry.sol +++ b/test/names/MockNameRegistry.sol @@ -4,6 +4,8 @@ pragma solidity >=0.8.24; import "../../src/names/NameRegistry.sol"; contract MockNameRegistry is NameRegistry { + // Constructor + constructor() NameRegistry(IHubV2(address(1))) {} // External functions @@ -38,7 +40,11 @@ contract MockNameRegistry is NameRegistry { customSymbols[_avatar] = _symbol; } - function setCidV0DigestNoChecks(address _avatar, bytes32 _dicV0Digest) external { - _setCidV0Digest(_avatar, _dicV0Digest); + function setMetadataDigestNoChecks(address _avatar, bytes32 _metadataDigest) external { + _setMetadataDigest(_avatar, _metadataDigest); + } + + function getShortOrLongName(address _avatar) external view returns (string memory) { + return _getShortOrLongName(_avatar); } } diff --git a/test/names/NameRegistry.t.sol b/test/names/NameRegistry.t.sol index 0847bdd..ff64998 100644 --- a/test/names/NameRegistry.t.sol +++ b/test/names/NameRegistry.t.sol @@ -12,17 +12,15 @@ contract NamesTest is Test, HumanRegistration, Base58Decode { // Constants // IPFS hash for Ubuntu 20.04, random CIDv0 - string constant IPFS_CID_V0 = "QmPK1s3pNYLi9ERiq3BDxKa4XosgWwFRQUydHUtz4YgpqB"; + // string constant IPFS_CID_V0 = "ipfs://QmPK1s3pNYLi9ERiq3BDxKa4XosgWwFRQUydHUtz4YgpqB"; // using https://cid.ipfs.tech/#QmPK1s3pNYLi9ERiq3BDxKa4XosgWwFRQUydHUtz4YgpqB // this is the 32 bytes hash digest - bytes32 constant DECODED_UINT = 0x0E7071C59DF3B9454D1D18A15270AA36D54F89606A576DC621757AFD44AD1D2E; + bytes32 constant SHA256_DIGEST = 0x0E7071C59DF3B9454D1D18A15270AA36D54F89606A576DC621757AFD44AD1D2E; // State variables MockNameRegistry mockNameRegistry; - bytes32 cidBytes; - // Constructor constructor() HumanRegistration(4) {} @@ -31,21 +29,28 @@ contract NamesTest is Test, HumanRegistration, Base58Decode { function setUp() public { mockNameRegistry = new MockNameRegistry(); - // Convert CIDv0 to bytes32 (should be done off-chain in production) - console.log("CIDv0 length", bytes(IPFS_CID_V0).length); - cidBytes = convertCidV0ToBytes32(IPFS_CID_V0); } // Tests - function testCidV0Digest() public { - mockNameRegistry.setCidV0DigestNoChecks(addresses[0], cidBytes); + function testShortName() public { + // without a short name registered, first get the long name + string memory longName = mockNameRegistry.getShortOrLongName(addresses[0]); + assertEq(longName, "Rings-3fNX29VBXc9WSxAT2dG3RYSfj6uX"); + + // now register a short name + vm.prank(addresses[0]); + mockNameRegistry.registerShortNameNoChecks(); + + // and get the short name + string memory shortName = mockNameRegistry.getShortOrLongName(addresses[0]); + assertEq(shortName, "Rings-Q6sQpEYS9Dg1"); } - // Helper functions + function testMetadataDigest() public { + mockNameRegistry.setMetadataDigestNoChecks(addresses[0], SHA256_DIGEST); + bytes32 digest = mockNameRegistry.getMetadataDigest(addresses[0]); - function convertCidV0ToBytes32(string memory _cidV0) internal view returns (bytes32) { - bytes memory decodedBytes = base58Decode(_cidV0); - console.log("CIDv0 length:", decodedBytes.length); + assertEq(digest, SHA256_DIGEST); } } From b818894f0e5eec8c2fbe2b743a466aebbd9f267e Mon Sep 17 00:00:00 2001 From: Benjamin Bollen Date: Tue, 30 Apr 2024 20:22:44 +0100 Subject: [PATCH 15/17] (NameRegistry): test base58 conversion for short names --- src/names/Base58Converter.sol | 32 +++++++++++++++--------------- src/names/NameRegistry.sol | 16 +++++++-------- test/names/MockNameRegistry.sol | 8 ++++++++ test/names/NameRegistry.t.sol | 35 +++++++++++++++++++++++++++++++++ 4 files changed, 67 insertions(+), 24 deletions(-) diff --git a/src/names/Base58Converter.sol b/src/names/Base58Converter.sol index 844475c..31a2628 100644 --- a/src/names/Base58Converter.sol +++ b/src/names/Base58Converter.sol @@ -6,13 +6,13 @@ contract Base58Converter { string internal constant ALPHABET = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"; - // uint256 internal constant IPFS_CIDV0_LENGTH = 46; + uint256 internal constant FIXED_SHORT_NAME_LENGTH = 12; uint256 internal constant ALPHABET_LENGTH = 58; // Internal functions - function toBase58(uint256 _data) internal pure returns (string memory) { + function _toBase58(uint256 _data) internal pure returns (string memory) { // Initialize enough length, only called in view functions // so no need to optimize gas costs bytes memory b58 = new bytes(64); @@ -28,20 +28,20 @@ contract Base58Converter { return string(_reverse(b58, i)); } - // function toBase58WithPadding(uint256 _data) internal pure returns (string memory) { - // bytes memory b58 = new bytes(IPFS_CIDV0_LENGTH); // Fixed length for CIDv0 - // uint256 i = 0; - // while (_data > 0 || i == 0) { - // uint256 mod = _data % ALPHABET_LENGTH; - // b58[i++] = bytes(ALPHABET)[mod]; - // _data /= ALPHABET_LENGTH; - // } - // while (i < IPFS_CIDV0_LENGTH) { - // // Ensure the output is exactly 46 characters - // b58[i++] = bytes(ALPHABET)[0]; // '1' in base58 represents the value 0 - // } - // return string(_reverse(b58, i)); - // } + function _toBase58WithPadding(uint256 _data) internal pure returns (string memory) { + bytes memory b58 = new bytes(FIXED_SHORT_NAME_LENGTH); // Fixed length for short name + uint256 i = 0; + while (_data > 0 || i == 0) { + uint256 mod = _data % ALPHABET_LENGTH; + b58[i++] = bytes(ALPHABET)[mod]; + _data /= ALPHABET_LENGTH; + } + while (i < FIXED_SHORT_NAME_LENGTH) { + // Ensure the output is exactly 12 characters + b58[i++] = bytes(ALPHABET)[0]; // '1' in base58 represents the value 0 + } + return string(_reverse(b58, i)); + } function _reverse(bytes memory _b, uint256 _len) internal pure returns (bytes memory) { bytes memory reversed = new bytes(_len); diff --git a/src/names/NameRegistry.sol b/src/names/NameRegistry.sol index 380adce..e7dddf8 100644 --- a/src/names/NameRegistry.sol +++ b/src/names/NameRegistry.sol @@ -27,10 +27,10 @@ contract NameRegistry is Base58Converter, INameRegistry, INameRegistryErrors, IC */ string public constant DEFAULT_CIRCLES_SYMBOL = "RING"; - /** - * @dev The IPFS protocol prefix for cid v0 resolution - */ - string private constant IPFS_PROTOCOL = "ipfs://Qm"; + // /** + // * @dev The IPFS protocol prefix for cid v0 resolution + // */ + // string private constant IPFS_PROTOCOL = "ipfs://Qm"; // State variables @@ -209,7 +209,7 @@ contract NameRegistry is Base58Converter, INameRegistry, INameRegistryErrors, IC function calculateShortNameWithNonce(address _avatar, uint256 _nonce) public pure returns (uint72 shortName_) { // use keccak256 to generate a pseudo-random number bytes32 digest = keccak256(abi.encodePacked(_avatar, _nonce)); - // take the modulo of the digest to get a number between 0 and MAX_NAME + // take the modulo of the digest to get a number between 0 and MAX_SHORT_NAME shortName_ = uint72(uint256(digest) % (MAX_SHORT_NAME + 1)); } @@ -284,7 +284,7 @@ contract NameRegistry is Base58Converter, INameRegistry, INameRegistryErrors, IC function _registerShortNameWithNonce(uint256 _nonce) internal { if (shortNames[msg.sender] != uint72(0)) { - revert CirclesNamesShortNameAlreadyAssigned(msg.sender, shortNames[msg.sender], 0); + revert CirclesNamesShortNameAlreadyAssigned(msg.sender, shortNames[msg.sender], 1); } uint72 shortName = calculateShortNameWithNonce(msg.sender, _nonce); @@ -308,10 +308,10 @@ contract NameRegistry is Base58Converter, INameRegistry, INameRegistryErrors, IC function _getShortOrLongName(address _avatar) internal view returns (string memory) { uint72 shortName = shortNames[_avatar]; if (shortName == uint72(0)) { - string memory base58FullAddress = toBase58(uint256(uint160(_avatar))); + string memory base58FullAddress = _toBase58(uint256(uint160(_avatar))); return string(abi.encodePacked(DEFAULT_CIRCLES_NAME_PREFIX, base58FullAddress)); } - string memory base58ShortName = toBase58(uint256(shortName)); + string memory base58ShortName = _toBase58(uint256(shortName)); return string(abi.encodePacked(DEFAULT_CIRCLES_NAME_PREFIX, base58ShortName)); } diff --git a/test/names/MockNameRegistry.sol b/test/names/MockNameRegistry.sol index c0b851b..cecc2af 100644 --- a/test/names/MockNameRegistry.sol +++ b/test/names/MockNameRegistry.sol @@ -47,4 +47,12 @@ contract MockNameRegistry is NameRegistry { function getShortOrLongName(address _avatar) external view returns (string memory) { return _getShortOrLongName(_avatar); } + + function toBase58(uint256 _data) external pure returns (string memory) { + return _toBase58(_data); + } + + function toBase58WithPadding(uint256 _data) external pure returns (string memory) { + return _toBase58WithPadding(_data); + } } diff --git a/test/names/NameRegistry.t.sol b/test/names/NameRegistry.t.sol index ff64998..05f0e02 100644 --- a/test/names/NameRegistry.t.sol +++ b/test/names/NameRegistry.t.sol @@ -45,6 +45,41 @@ contract NamesTest is Test, HumanRegistration, Base58Decode { // and get the short name string memory shortName = mockNameRegistry.getShortOrLongName(addresses[0]); assertEq(shortName, "Rings-Q6sQpEYS9Dg1"); + + // can't register a second time + vm.expectRevert(); + vm.prank(addresses[0]); + mockNameRegistry.registerShortNameWithNonceNoChecks(839892892); + } + + function testShortNameWithNonce() public { + vm.prank(addresses[0]); + mockNameRegistry.registerShortNameWithNonceNoChecks(839892892); + string memory shortName = mockNameRegistry.getShortOrLongName(addresses[0]); + assertEq(shortName, "Rings-uNJGyf6sN6vY"); + } + + function testBase58Conversion() public { + assertEq(mockNameRegistry.toBase58(0), "1"); + assertEq(mockNameRegistry.toBase58(mockNameRegistry.MAX_SHORT_NAME()), "zzzzzzzzzzzz"); + // longer names are not possible as tha calculation takes modulo MAX_SHORT_NAME + 1 + assertEq(mockNameRegistry.toBase58(mockNameRegistry.MAX_SHORT_NAME() + 1), "2111111111111"); + + assertEq(mockNameRegistry.toBase58(845156846445168), "7bmqZRAo1"); + assertEq(mockNameRegistry.toBase58(912670482714768333), "37sj6xwGEtL"); + + assertEq(mockNameRegistry.toBase58WithPadding(0), "111111111111"); + assertEq(mockNameRegistry.toBase58WithPadding(845156846445168), "1117bmqZRAo1"); + } + + function testCustomName() public { + mockNameRegistry.registerCustomNameNoChecks(addresses[0], "Circles"); + assertEq(mockNameRegistry.customNames(addresses[0]), "Circles"); + } + + function testInvalidCustomNames() public { + vm.expectRevert(); + mockNameRegistry.registerCustomNameNoChecks(addresses[0], "WeirdName=NotAllowed"); } function testMetadataDigest() public { From 76fc41076ca180691163f1f10c52da8f33a8030e Mon Sep 17 00:00:00 2001 From: Benjamin Bollen Date: Thu, 2 May 2024 18:48:19 +0200 Subject: [PATCH 16/17] (NameRegistry): short names should always be padded to 12 characters --- src/names/NameRegistry.sol | 2 +- test/names/MockNameRegistry.sol | 4 ++++ test/names/NameRegistry.t.sol | 10 ++++++++++ 3 files changed, 15 insertions(+), 1 deletion(-) diff --git a/src/names/NameRegistry.sol b/src/names/NameRegistry.sol index e7dddf8..7200332 100644 --- a/src/names/NameRegistry.sol +++ b/src/names/NameRegistry.sol @@ -311,7 +311,7 @@ contract NameRegistry is Base58Converter, INameRegistry, INameRegistryErrors, IC string memory base58FullAddress = _toBase58(uint256(uint160(_avatar))); return string(abi.encodePacked(DEFAULT_CIRCLES_NAME_PREFIX, base58FullAddress)); } - string memory base58ShortName = _toBase58(uint256(shortName)); + string memory base58ShortName = _toBase58WithPadding(uint256(shortName)); return string(abi.encodePacked(DEFAULT_CIRCLES_NAME_PREFIX, base58ShortName)); } diff --git a/test/names/MockNameRegistry.sol b/test/names/MockNameRegistry.sol index cecc2af..3b27ac2 100644 --- a/test/names/MockNameRegistry.sol +++ b/test/names/MockNameRegistry.sol @@ -55,4 +55,8 @@ contract MockNameRegistry is NameRegistry { function toBase58WithPadding(uint256 _data) external pure returns (string memory) { return _toBase58WithPadding(_data); } + + function storeShortName(address _avatar, uint72 _shortName) external { + _storeShortName(_avatar, _shortName, 0); + } } diff --git a/test/names/NameRegistry.t.sol b/test/names/NameRegistry.t.sol index 05f0e02..ce4ae3e 100644 --- a/test/names/NameRegistry.t.sol +++ b/test/names/NameRegistry.t.sol @@ -59,6 +59,16 @@ contract NamesTest is Test, HumanRegistration, Base58Decode { assertEq(shortName, "Rings-uNJGyf6sN6vY"); } + function testShortNameWithPadding() public { + // 42 converts to "j" in base58 + assertEq(mockNameRegistry.toBase58(42), "j"); + + // but as a short name it shold be padded to 12 characters + mockNameRegistry.storeShortName(addresses[0], 42); + string memory shortName = mockNameRegistry.getShortOrLongName(addresses[0]); + assertEq(shortName, "Rings-11111111111j"); + } + function testBase58Conversion() public { assertEq(mockNameRegistry.toBase58(0), "1"); assertEq(mockNameRegistry.toBase58(mockNameRegistry.MAX_SHORT_NAME()), "zzzzzzzzzzzz"); From b37a433699189293b41fa52bc292108831282a4a Mon Sep 17 00:00:00 2001 From: Benjamin Bollen Date: Fri, 3 May 2024 14:46:42 +0200 Subject: [PATCH 17/17] (test/names): remove unused base58 decoder --- test/names/Base58Helper.sol | 137 ---------------------------------- test/names/NameRegistry.t.sol | 3 +- 2 files changed, 1 insertion(+), 139 deletions(-) delete mode 100644 test/names/Base58Helper.sol diff --git a/test/names/Base58Helper.sol b/test/names/Base58Helper.sol deleted file mode 100644 index c99c130..0000000 --- a/test/names/Base58Helper.sol +++ /dev/null @@ -1,137 +0,0 @@ -// SPDX-License-Identifier: AGPL-3.0-only -pragma solidity >=0.8.24; - -import "forge-std/console.sol"; - -/** - * Helper contract to do the base58 decoding in solidity unit tests; - * this should only be used for tests, in production this conversion to bytes - * should be done off-chain with a proper library. - */ -contract Base58Decode { - function base58Decode(string memory source) public view returns (bytes memory) { - bytes memory bSource = bytes(source); - uint256 base = 58; - uint256 result = 0; - uint256 multi = 1; - - // Mapping of base58 characters to their integer values - uint8[58] memory base58Map = [ - 0x31, - 0x32, - 0x33, - 0x34, - 0x35, - 0x36, - 0x37, - 0x38, - 0x39, - 0x41, - 0x42, - 0x43, - 0x44, - 0x45, - 0x46, - 0x47, - 0x48, - 0x4A, - 0x4B, - 0x4C, - 0x4D, - 0x4E, - 0x50, - 0x51, - 0x52, - 0x53, - 0x54, - 0x55, - 0x56, - 0x57, - 0x58, - 0x59, - 0x5A, - 0x61, - 0x62, - 0x63, - 0x64, - 0x65, - 0x66, - 0x67, - 0x68, - 0x69, - 0x6A, - 0x6B, - 0x6D, - 0x6E, - 0x6F, - 0x70, - 0x71, - 0x72, - 0x73, - 0x74, - 0x75, - 0x76, - 0x77, - 0x78, - 0x79, - 0x7A - ]; - - // Adjust to ignore the "Qm" prefix if it exists and length is 34 bytes - uint256 startIndex = 0; - if (bSource.length == 46 && bSource[0] == 0x51 && bSource[1] == 0x6D) { - // 'Q' == 0x51 and 'm' == 0x6D - startIndex = 2; - } else if (bSource.length != 44) { - revert("Invalid CIDv0 length for IPFS decoding."); - } - - console.log("startIndex:", startIndex); - console.log("CIDv0 inside length:", bSource.length); - - // for (uint256 i = startIndex; i < bSource.length; i++) { - // uint256 value = indexOf(uint8(bSource[i]), base58Map); - // result += value * multi; - // if (i ) - // multi *= base; - // console.log("index:", i - startIndex + 1); - // console.log("charater:", uint8(bSource[i]), "value:", value); - // console.log("result:", result, "multi:", multi); - // } - - uint256 j = 0; - for (uint256 i = bSource.length; i > startIndex; i--) { - uint256 value = indexOf(uint8(bSource[i - 1]), base58Map); - result += value * multi; - if (i > startIndex + 1) { - // skip the last iteration to avoid overflow - // 58^44 > 2^256 - 1 - multi *= base; - console.log("iteration:", j++); - } else { - console.log("last iteration"); - } - console.log("index:", bSource.length - i + 1); - console.log("charater:", uint8(bSource[i - 1]), "value:", value); - console.log("result:", result, "multi:", multi); - } - - return toBytes(result); - } - - function indexOf(uint8 char, uint8[58] memory map) private pure returns (uint256) { - for (uint256 i = 0; i < map.length; i++) { - if (map[i] == char) { - return i; - } - } - revert("Character not in base58 map."); - } - - function toBytes(uint256 x) private pure returns (bytes memory b) { - b = new bytes(32); - assembly { - mstore(add(b, 32), x) - } - } -} diff --git a/test/names/NameRegistry.t.sol b/test/names/NameRegistry.t.sol index ce4ae3e..5e32234 100644 --- a/test/names/NameRegistry.t.sol +++ b/test/names/NameRegistry.t.sol @@ -6,9 +6,8 @@ import {StdCheats} from "forge-std/StdCheats.sol"; import "forge-std/console.sol"; import "../setup/HumanRegistration.sol"; import "./MockNameRegistry.sol"; -import "./base58Helper.sol"; -contract NamesTest is Test, HumanRegistration, Base58Decode { +contract NamesTest is Test, HumanRegistration { // Constants // IPFS hash for Ubuntu 20.04, random CIDv0