Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

20240412 consented flow #141

Merged
merged 10 commits into from
May 2, 2024
Merged
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
Loading