From 7975a450e3a4306f25102d95656694e6a52dbf7f Mon Sep 17 00:00:00 2001 From: Benjamin Bollen Date: Tue, 30 Jan 2024 17:42:09 +0000 Subject: [PATCH] Complexity reduction session with Martin, Daniel, Ingo and Ben MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Martin Köppelmann Co-authored-by: Daniel Janz Co-authored-by: Ingo Collatz --- src/graph/Graph.sol | 6 +- src/multitoken-graph/Graph.sol | 31 ----- src/multitoken-graph/Hub.sol | 210 +++++++++++++++++++++++++++++++++ src/proxy/Proxy.sol | 1 + 4 files changed, 215 insertions(+), 33 deletions(-) delete mode 100644 src/multitoken-graph/Graph.sol create mode 100644 src/multitoken-graph/Hub.sol diff --git a/src/graph/Graph.sol b/src/graph/Graph.sol index 183a8e2..63908f9 100644 --- a/src/graph/Graph.sol +++ b/src/graph/Graph.sol @@ -400,6 +400,7 @@ contract Graph is ProxyFactory, IGraph { uint256[] calldata _flow, bytes calldata _packedCoordinates ) public { + // todo: sender does not have to be registered; can be anyone // first unpack the coordinates to array of uint16 uint16[] memory coordinates = _unpackCoordinates(_packedCoordinates, _flow.length); @@ -431,9 +432,9 @@ contract Graph is ProxyFactory, IGraph { } function operateFlowMatrix( - int256[] calldata _intendedNettedFlow, + int256[] calldata _transferIntents, address[] calldata _flowVertices, - uint256[] calldata _flow, + uint256[] calldata _flow, // consider adding a group mint targets array bytes calldata _packedCoordinates ) public { // first unpack the coordinates to array of uint16 @@ -445,6 +446,7 @@ contract Graph is ProxyFactory, IGraph { ); // check that all flow vertices have the calling operator enabled. + // todo: only check for net-senders require(isGraphOperatorForSet(msg.sender, _flowVertices), "Graph operator must be enabled for all vertices."); // if each vertex in the intended netted flow is zero, then it is a closed path diff --git a/src/multitoken-graph/Graph.sol b/src/multitoken-graph/Graph.sol deleted file mode 100644 index f76763e..0000000 --- a/src/multitoken-graph/Graph.sol +++ /dev/null @@ -1,31 +0,0 @@ -// SPDX-License-Identifier: AGPL-3.0-only -pragma solidity >=0.8.13; - -import "@openzeppelin/contracts/token/ERC1155/ERC1155.sol"; - -contract Graph is ERC1155 { - // State variables - - // linked list for registered avatars - mapping(address => address) public avatars; - - // linked list for registered groups - mapping(address => address) public groups; - - // linked list for registered organizations - mapping(address => address) public organizations; - - mapping(uint256 => bytes32) public avatarIpfsUris; - - // Constructor - - constructor() ERC1155("https://fallback.aboutcircles.com/v1/profile/{id}.json") {} - - - - function uri(uint256 _id) external view override returns (string memory uri_) { - if (avatarIpfsUris[_id] != bytes32(0)) { - return uri_ = string(abi.encodedPacked("ipfs://f0", bytes32ToHex(avatarIpfsUris[_id]); - } - } -} \ No newline at end of file diff --git a/src/multitoken-graph/Hub.sol b/src/multitoken-graph/Hub.sol new file mode 100644 index 0000000..5c5b306 --- /dev/null +++ b/src/multitoken-graph/Hub.sol @@ -0,0 +1,210 @@ +// SPDX-License-Identifier: AGPL-3.0-only +pragma solidity >=0.8.13; + +import "@openzeppelin/contracts/token/ERC1155/ERC1155.sol"; + +contract Graph is ERC1155 { + // Constants + + // People registering can mint up to one Circle per hour. + // todo: should this be implemented without branching, ie. all be contracts + address public constant PERSONAL_MINT = address(0x1); + + // Organizations can register with a no-mint policy. + address public constant NO_MINT = address(0x2); + + address public constant SENTINEL = address(0x1); + + // State variables + + // Standard mint for Circle groups. + address public immutable standardGroupMint; + + // linked list for registered avatars, used by all people, + // groups and organizations. + mapping(address => address) public avatars; + + mapping(address => uint256) public lastMintTimes; + + mapping(address => bool) public stopped; + + // Mint policy registered by avatar. + mapping(address => address) public mintPolicies; + + mapping(address => address) public treasuries; + + // todo: ok for linked list, expiry 96 bits + mapping(address => mapping(address => uint256)) public trustMarkers; + + // todo: do address + mapping(uint256 => bytes32) public avatarIpfsUris; + + // Modifiers + + modifier isHuman(address _human) { + require( + lastMintTimes[_human] > 0, + "" + ); + _; + } + + modifier isGroup(address _group) { + require( + mintPolicies[_group] != address(0), + "" + ); + _; + } + + modifier isOrganization(address _organization) { + require( + avatars[_organization] != address(0) && + mintPolicies[_organization] == address(0) && + lastMintTimes[_organization] == uint256(0), + "" + ); + _; + } + + + // Constructor + + constructor(address _standardGroupMint) ERC1155("https://fallback.aboutcircles.com/v1/profile/{id}.json") { + standardGroupMint = _standardGroupMint; + } + + // External functions + + function registerHuman() external { + require(trusts(_inviter, msg.sender), ""); + // todo: v1 stopped & enable migration + require(...); + insertAvatar(msg.sender); + // mintPolicies[msg.sender] = PERSONAL_MINT; + lastMintTimes[msg.sender] = block.timestamp; + // treasuries[msg.sender] = address(0); + + // todo: let's welcome mint re-introduced; 3 days not demurraged + } + + function registerGroup(address _treasury, string _name, string _symbol) { + _registerGroup(msg.sender, standardGroupMint, _treasury); + } + + function registerCustomGroup(address _mint, address _treasury) external { + // msg.sender controls membership + // minting: policy only + // redemption: treasury contract (ideally generated from a factory - outside protocol) + require(...); + _registerGroup(msg.sender, _mint, _treasury); + } + + function registerOrganization(string _name) { + insertAvatar(msg.sender); + lastMintTimes[msg.sender] = 0; + } + + + function trust(address _trustReceiver, uint256 _expiry) external { + // todo: make iterable; don't require expiry > block.timestamp + // possibly: if _expiry < block.timestamp, set expiry = block.timestamp; + trustMarkers[msg.sender][_trustReceiver] = _expiry; + } + + // todo: happy with this name? + function personalMint() external isHuman(msg.sender) { + // do daily demurrage over claimable period; max 2week + + } + + // graph transfers SHOULD allow personal -> group conversion en route + + // msg.sender holds collateral, and MUST be accepted by group + // maybe less + function groupMint(address _group, address[] calldata _collateral, uint256[] calldata _amounts) { + // check group and collateral exist + // de-demurrage amounts + // loop over collateral + + require( + mintPolicies[_group].beforeMintPolicy(msg.sender, _group, _collateral, _amounts), ""); + + sendBatchTransfer(to: _group.treasury) // treasury.on1155Received should only implement but nothing protocol related + + _mint(msg.sender, _group, sumAmounts); + } + + // check if path transfer can be fully ERC1155 compatible + // note: matrix math needs to consider mints, otherwise it won't add up + + function singleSourcePathTransfer() { + require(msg.sender == _source) + // todo: sender does not have to be registered; can be anyone + // can have multiple receivers + // can allow zero-nett amounts, ie. closed paths are ok + + // consider adding a group mint targets array + + // emit Transfer intent events + } + + function operatorPathTransfer() { + // msg.sender = oeprator + require("nett sources have approved operator"); + } + + function wrapInflationaryERC20() { + // pass on name() but not modified + } + + function unwrapInflationaryERC20() { + + } + + function wrapDemurrageERC20() { + // call on Hub for demurrage calculation in ERC20 contract + + // dont do a global allowance; but do do an ERC20Permit + + // do do a auto-factory of deterministic contract address + // and how? + } + + // do some unique name hash finding for personal circles + // register with a salt for avoiding malicious blockage + + function uri(uint256 _id) external view override returns (string memory uri_) { + if (avatarIpfsUris[_id] != bytes32(0)) { + return uri_ = string(abi.encodedPacked("ipfs://f0", bytes32ToHex(avatarIpfsUris[_id]); + } else { + return super. + } + } + + function setUri(bytes32 _ipfsCid) external { + // msg.sender -> tokenId + avatarIpfsUris[uint256(msg.sender)] = _ipfsCid; + } + + + // Internal functions + + function toDemurrageAmount(uint256 _amount, uint256 _timestamp) { + // timestamp should be "stepfunction" the timestamp + // todo: ask where the best time step is + + if (_time