Skip to content

Commit

Permalink
Merge pull request #141 from CirclesUBI/20240412-consented-flow
Browse files Browse the repository at this point in the history
20240412 consented flow
  • Loading branch information
jaensen authored May 2, 2024
2 parents 3085a41 + 908823c commit 9461fb2
Show file tree
Hide file tree
Showing 19 changed files with 113 additions and 27 deletions.
2 changes: 1 addition & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ 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
## v0.3.3

- [PR132] bug fix in groupMint(); initial test coverage for group mint
- [PR130] remove the code from the first draft proposal (December 2023)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
Chiado deployment
=================
Deployment Date: 2024-04-10 11:45:50
Version: 0.3.3-alpha
Git Commit: 2f46ada912ee53dfb5e44709b1821f879a61276e
Deployer Address: 0x7619F26728Ced663E50E578EB6ff42430931564c, Intitial nonce: 99
Compiler Version: v0.8.23+commit.f704f362

Deployed Contracts:
Hub: 0xDA02CDB5279B3a1eF27Be3d91aE924495E6A5569
Migration: 0xd4CB24d1A9bA4e24feDb22a34f502f70c4E929FF
NameRegistry: 0xc6CABB6C7B8C2fF1871A90d7dDeDE8D8a7e9Dc85
ERC20Lift: 0x44E51009CAf77917e2E06aE731863Cf59C3337dc
StandardTreasury: 0x49c358aFe75F5302A04Becf6F8273B4d55d46a2D
BaseGroupMintPolicy: 0xfC180f31Be6f6a0663EDb450B64E2E2695ed8f02
MastercopyDemurrageERC20: 0x4a117EE75FC792B613D96ee6BAd576881F6e2B91
MastercopyInflationaryERC20: 0xEAe752Caaca396A2E494a8B4F6e6d42dD134539b
MastercopyStandardVault: 0x1762C81FadB183495bCFC439CdF3e5d4f8Ff421f
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{"contractName":"Hub","deployedAddress":"0xDA02CDB5279B3a1eF27Be3d91aE924495E6A5569","sourcePath":"src/hub/Hub.sol:Hub","constructor-args":"0xdbF22D4e8962Db3b2F1d9Ff55be728A887e47710 0xc6CABB6C7B8C2fF1871A90d7dDeDE8D8a7e9Dc85 0xd4CB24d1A9bA4e24feDb22a34f502f70c4E929FF 0x44E51009CAf77917e2E06aE731863Cf59C3337dc 0x49c358aFe75F5302A04Becf6F8273B4d55d46a2D 1675209600 31540000 https://fallback.aboutcircles.com/v1/circles/{id}.json","argumentsFile":"constructorArgs_Hub.txt"}
{"contractName":"Migration","deployedAddress":"0xd4CB24d1A9bA4e24feDb22a34f502f70c4E929FF","sourcePath":"src/migration/Migration.sol:Migration","constructor-args":"0xdbF22D4e8962Db3b2F1d9Ff55be728A887e47710 0xDA02CDB5279B3a1eF27Be3d91aE924495E6A5569 1675209600","argumentsFile":"constructorArgs_Migration.txt"}
{"contractName":"NameRegistry","deployedAddress":"0xc6CABB6C7B8C2fF1871A90d7dDeDE8D8a7e9Dc85","sourcePath":"src/names/NameRegistry.sol:NameRegistry","constructor-args":"0xDA02CDB5279B3a1eF27Be3d91aE924495E6A5569","argumentsFile":"constructorArgs_NameRegistry.txt"}
{"contractName":"ERC20Lift","deployedAddress":"0x44E51009CAf77917e2E06aE731863Cf59C3337dc","sourcePath":"src/lift/ERC20Lift.sol:ERC20Lift","constructor-args":"0xDA02CDB5279B3a1eF27Be3d91aE924495E6A5569 0xc6CABB6C7B8C2fF1871A90d7dDeDE8D8a7e9Dc85 0x4a117EE75FC792B613D96ee6BAd576881F6e2B91 0xEAe752Caaca396A2E494a8B4F6e6d42dD134539b","argumentsFile":"constructorArgs_ERC20Lift.txt"}
{"contractName":"StandardTreasury","deployedAddress":"0x49c358aFe75F5302A04Becf6F8273B4d55d46a2D","sourcePath":"src/treasury/StandardTreasury.sol:StandardTreasury","constructor-args":"0xDA02CDB5279B3a1eF27Be3d91aE924495E6A5569 0x1762C81FadB183495bCFC439CdF3e5d4f8Ff421f","argumentsFile":"constructorArgs_StandardTreasury.txt"}
{"contractName":"BaseGroupMintPolicy","deployedAddress":"0xfC180f31Be6f6a0663EDb450B64E2E2695ed8f02","sourcePath":"src/groups/BaseMintPolicy.sol:MintPolicy","constructor-args":"","argumentsFile":"constructorArgs_BaseGroupMintPolicy.txt"}
{"contractName":"MastercopyDemurrageERC20","deployedAddress":"0x4a117EE75FC792B613D96ee6BAd576881F6e2B91","sourcePath":"src/lift/DemurrageCircles.sol:DemurrageCircles","constructor-args":"","argumentsFile":"constructorArgs_MastercopyDemurrageERC20.txt"}
{"contractName":"MastercopyInflationaryERC20","deployedAddress":"0xEAe752Caaca396A2E494a8B4F6e6d42dD134539b","sourcePath":"src/lift/InflationaryCircles.sol:InflationaryCircles","constructor-args":"","argumentsFile":"constructorArgs_MastercopyInflationaryERC20.txt"}
{"contractName":"MastercopyStandardVault","deployedAddress":"0x1762C81FadB183495bCFC439CdF3e5d4f8Ff421f","sourcePath":"src/treasury/StandardVault.sol:StandardVault","constructor-args":"","argumentsFile":"constructorArgs_MastercopyStandardVault.txt"}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@

Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
0xDA02CDB5279B3a1eF27Be3d91aE924495E6A5569 0xc6CABB6C7B8C2fF1871A90d7dDeDE8D8a7e9Dc85 0x4a117EE75FC792B613D96ee6BAd576881F6e2B91 0xEAe752Caaca396A2E494a8B4F6e6d42dD134539b
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
0xdbF22D4e8962Db3b2F1d9Ff55be728A887e47710 0xc6CABB6C7B8C2fF1871A90d7dDeDE8D8a7e9Dc85 0xd4CB24d1A9bA4e24feDb22a34f502f70c4E929FF 0x44E51009CAf77917e2E06aE731863Cf59C3337dc 0x49c358aFe75F5302A04Becf6F8273B4d55d46a2D 1675209600 31540000 https://fallback.aboutcircles.com/v1/circles/{id}.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@

Original file line number Diff line number Diff line change
@@ -0,0 +1 @@

Original file line number Diff line number Diff line change
@@ -0,0 +1 @@

Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
0xdbF22D4e8962Db3b2F1d9Ff55be728A887e47710 0xDA02CDB5279B3a1eF27Be3d91aE924495E6A5569 1675209600
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
0xDA02CDB5279B3a1eF27Be3d91aE924495E6A5569
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
0xDA02CDB5279B3a1eF27Be3d91aE924495E6A5569 0x1762C81FadB183495bCFC439CdF3e5d4f8Ff421f
2 changes: 1 addition & 1 deletion script/deployments/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "deploy-circles",
"version": "0.3.2-alpha",
"version": "0.3.4-alpha",
"type": "module",
"dependencies": {
"dotenv": "^16.4.5",
Expand Down
2 changes: 1 addition & 1 deletion src/errors/Errors.sol
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ interface IHubErrors {

error CirclesHubOperatorNotApprovedForSource(address operator, address source, uint16 streamId, uint8 code);

error CirclesHubCirclesAreNotTrustedByReceiver(address receiver, uint256 circlesId, uint8 code);
error CirclesHubFlowEdgeIsNotPermitted(address receiver, uint256 circlesId, uint8 code);

error CirclesHubOnClosedPathOnlyPersonalCirclesCanReturnToAvatar(address failedReceiver, uint256 circlesId);

Expand Down
43 changes: 35 additions & 8 deletions src/hub/Hub.sol
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,8 @@ contract Hub is Circles, MetadataDefinitions, IHubErrors, ICirclesErrors {
*/
address private constant SENTINEL = address(0x1);

bytes32 private constant ADVANCED_FLAG_OPTOUT_CONSENTEDFLOW = bytes32(uint256(1));

// State variables

/**
Expand Down Expand Up @@ -117,6 +119,12 @@ contract Hub is Circles, MetadataDefinitions, IHubErrors, ICirclesErrors {
*/
mapping(address => address) public treasuries;

/**
* @notice By default the advanced usage flags should remain set to zero.
* Only for advanced purposes people can consider enabling flags.
*/
mapping(address => bytes32) public advancedUsageFlags;

/**
* @notice The iterable mapping of directional trust relations between avatars and
* their expiry times.
Expand Down Expand Up @@ -501,9 +509,7 @@ contract Hub is Circles, MetadataDefinitions, IHubErrors, ICirclesErrors {
_burn(msg.sender, _id, _amount);
}

// Public functions

function wrap(address _avatar, uint256 _amount, CirclesType _type) public returns (address) {
function wrap(address _avatar, uint256 _amount, CirclesType _type) external returns (address) {
if (!isHuman(_avatar) && !isGroup(_avatar)) {
// Avatar must be human or group.
revert CirclesAvatarMustBeRegistered(_avatar, 2);
Expand Down Expand Up @@ -550,6 +556,14 @@ contract Hub is Circles, MetadataDefinitions, IHubErrors, ICirclesErrors {
_matchNettedFlows(streamsNettedFlow, matrixNettedFlow);
}

function setAdvancedUsageFlag(bytes32 _flag) external {
if (avatars[msg.sender] == address(0)) {
// Only registered avatars can set advanced usage flags.
revert CirclesAvatarMustBeRegistered(msg.sender, 3);
}
advancedUsageFlags[msg.sender] = _flag;
}

// Public functions

/**
Expand Down Expand Up @@ -587,6 +601,18 @@ contract Hub is Circles, MetadataDefinitions, IHubErrors, ICirclesErrors {
return uint256(trustMarkers[_truster][_trustee].expiry) >= block.timestamp;
}

function isPermittedFlow(address _to, address _circlesAvatar) public view returns (bool) {
// if receiver does not trust the Circles being sent, then the flow is not consented regardless
if (uint256(trustMarkers[_to][_circlesAvatar].expiry) < block.timestamp) return false;
// if the advanced usage flag is set to opt-out of consented flow,
// then the uni-directional trust is sufficient
if (advancedUsageFlags[_circlesAvatar] & ADVANCED_FLAG_OPTOUT_CONSENTEDFLOW != bytes32(0)) {
return true;
}
// however, by default the consented flow requires bi-directional trust from center to receiver
return uint256(trustMarkers[_circlesAvatar][_to].expiry) >= block.timestamp;
}

/**
* uri returns the IPFS URI for the ERC1155 token.
* If the
Expand Down Expand Up @@ -637,9 +663,9 @@ contract Hub is Circles, MetadataDefinitions, IHubErrors, ICirclesErrors {
// _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 (!isTrusted(_group, address(uint160(_collateral[i])))) {
if (!isPermittedFlow(_group, address(uint160(_collateral[i])))) {
// Group does not trust collateral.
revert CirclesHubCirclesAreNotTrustedByReceiver(_group, _collateral[i], 0);
revert CirclesHubFlowEdgeIsNotPermitted(_group, _collateral[i], 0);
}

if (_amounts[i] == 0) {
Expand Down Expand Up @@ -723,9 +749,10 @@ contract Hub is Circles, MetadataDefinitions, IHubErrors, ICirclesErrors {
int256 flow = int256(uint256(_flow[i].amount));

// check the receiver trusts the Circles being sent
if (!isTrusted(to, circlesId)) {
// Receiver does not trust Circles being sent
revert CirclesHubCirclesAreNotTrustedByReceiver(to, toTokenId(circlesId), 1);
// and that the center trusts the receiver (unless center opt-ed out)
if (!isPermittedFlow(to, circlesId)) {
// Flow edge is not permitted.
revert CirclesHubFlowEdgeIsNotPermitted(to, toTokenId(circlesId), 1);
}
if (_closedPath && (to != circlesId || isGroup(circlesId))) {
// Closed paths can only return personal Circles to source.
Expand Down
16 changes: 14 additions & 2 deletions test/groups/compositeMintGroups.sol
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,9 @@ contract CompositeMintGroupsTest is Test, GroupSetup, IHubErrors {
for (uint256 i = 0; i < 5; i++) {
vm.prank(group0);
hub.trust(addresses[i], INDEFINITE_FUTURE);
// and each human trusts the group
vm.prank(addresses[i]);
hub.trust(group0, INDEFINITE_FUTURE);

collateral[0] = addresses[i];
amounts[0] = 1 * CRC;
Expand All @@ -50,6 +53,9 @@ contract CompositeMintGroupsTest is Test, GroupSetup, IHubErrors {
for (uint256 i = 0; i < 35; i++) {
vm.prank(group1);
hub.trust(addresses[i], INDEFINITE_FUTURE);
// and each human trusts the group
vm.prank(addresses[i]);
hub.trust(group1, INDEFINITE_FUTURE);

collateral[0] = addresses[i];
amounts[0] = 1 * CRC;
Expand All @@ -61,6 +67,9 @@ contract CompositeMintGroupsTest is Test, GroupSetup, IHubErrors {
for (uint256 i = 0; i < 15; i++) {
vm.prank(group2);
hub.trust(addresses[i], INDEFINITE_FUTURE);
// and each human trusts the group
vm.prank(addresses[i]);
hub.trust(group2, INDEFINITE_FUTURE);

collateral[0] = addresses[i];
amounts[0] = 1 * CRC;
Expand All @@ -74,8 +83,11 @@ contract CompositeMintGroupsTest is Test, GroupSetup, IHubErrors {
function testCompositeGroupMint() public {
// everyone already has some group Circles
// now let G1 trust G0
vm.prank(addresses[36]);
hub.trust(addresses[35], INDEFINITE_FUTURE);
vm.prank(group1);
hub.trust(group0, INDEFINITE_FUTURE);
// reversly let G0 trust G1 for consented flow
vm.prank(group0);
hub.trust(group1, INDEFINITE_FUTURE);

// now Alice mints with G0 as collateral for G1
address[] memory collateral = new address[](1);
Expand Down
6 changes: 5 additions & 1 deletion test/groups/mintGroupCircles.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,12 @@ contract MintGroupCirclesTest is Test, GroupSetup, IHubErrors {

// G1 trusts first 5 humans
for (uint256 i = 0; i < 5; i++) {
vm.prank(addresses[35]);
vm.prank(group);
hub.trust(addresses[i], INDEFINITE_FUTURE);

// and each human trusts the group
vm.prank(addresses[i]);
hub.trust(group, INDEFINITE_FUTURE);
}
}

Expand Down
27 changes: 20 additions & 7 deletions test/hub/PathTransferHub.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ contract HubPathTransferTest is Test, TimeCirclesSetup, HumanRegistration, Appro
// and 365 days as bootstrap time
mockHub = new MockPathTransferHub(INFLATION_DAY_ZERO, 365 days);

// register 4 humans
// register 8 humans
for (uint256 i = 0; i < N; i++) {
vm.prank(addresses[i]);
mockHub.registerHumanUnrestricted();
Expand All @@ -53,26 +53,39 @@ contract HubPathTransferTest is Test, TimeCirclesSetup, HumanRegistration, Appro
assertEq(mockHub.isTrusted(addresses[i], addresses[i - 1]), true);
assertEq(mockHub.isTrusted(addresses[i - 1], addresses[i]), false);
}

// for consented flow, the origin of the Circles needs to trust the receiver too
// Alice trusts Bob, Bob trusts Charlie, Charlie trusts David
for (uint256 i = 0; i < N - 1; i++) {
vm.prank(addresses[i]);
mockHub.trust(addresses[i + 1], expiry);
assertEq(mockHub.isTrusted(addresses[i], addresses[i + 1]), true);
assertEq(mockHub.isTrusted(addresses[i + 1], addresses[i]), true);
}
}

// Tests

function testOperateFlowMatrix() public {
function testOperateFlowMatrixConsentedFlow() public {
// Alice <-> Bob <-> Charlie <-> David
// first four avatars have a linear bi-directional trust
uint256 M = N;

// Flow matrix for transferring Circles from Alice to David
// with indication of which Circles are being sent
// A B C D
// A-B -5A 5A . .
// B-C . -5B 5B .
// C-D . . -5C 5C

address[] memory flowVertices = new address[](N);
Hub.FlowEdge[] memory flow = new Hub.FlowEdge[](N - 1);
address[] memory flowVertices = new address[](M);
Hub.FlowEdge[] memory flow = new Hub.FlowEdge[](M - 1);

// allocate three coordinates per flow edge
uint16[] memory coordinates = new uint16[]((N - 1) * 3);
uint16[] memory coordinates = new uint16[]((M - 1) * 3);

// the flow vertices need to be provided in ascending order\
for (uint256 i = 0; i < N; i++) {
for (uint256 i = 0; i < M; i++) {
flowVertices[i] = sortedAddresses[i];
}

Expand All @@ -81,7 +94,7 @@ contract HubPathTransferTest is Test, TimeCirclesSetup, HumanRegistration, Appro
uint256 index = 0;

// for each row in the flow matrix specify the coordinates and amount
for (uint256 i = 0; i < N - 1; i++) {
for (uint256 i = 0; i < M - 1; i++) {
// flow is the amount of Circles to send, here constant for each edge
flow[i].amount = uint240(5 * CRC);
flow[i].streamSinkId = uint16(0);
Expand Down
6 changes: 0 additions & 6 deletions test/lift/ERC20Lift.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,6 @@ contract ERC20LiftTest is Test, TimeCirclesSetup, HumanRegistration {
hub.personalMintWithoutV1Check();

uint256 aliceBalance = hub.balanceOf(addresses[0], uint256(uint160(addresses[0])));
console.log("Alice balance: ", aliceBalance);

// test the master contracts in Lift
// ERC20Lift lift = mockDeployment.erc20Lift();
Expand Down Expand Up @@ -91,11 +90,6 @@ contract ERC20LiftTest is Test, TimeCirclesSetup, HumanRegistration {
// somewhat cheekily test here that the demurrage works in ERC20 too
// todo: split this out into proper unit tests, rather than stories

(uint192 balance, uint64 lastUpdatedDay) = aliceERC20.discountedBalances(addresses[0]);
console.log("ERC1155 balance: ", hub.balanceOf(addresses[0], uint256(uint160(addresses[0]))));
console.log("balance: ", balance);
console.log("lastUpdatedDay: ", lastUpdatedDay);

// skip time
skipTime(2 days);
// assert Alice has 15 CRC in her ERC20
Expand Down

0 comments on commit 9461fb2

Please sign in to comment.