From 45ada47249d8aef4d1fd578ab534775b6141b475 Mon Sep 17 00:00:00 2001 From: sirpy Date: Thu, 7 Nov 2024 15:40:06 +0200 Subject: [PATCH 1/5] add: manager fee. ubi minamount --- .../DirectPayments/DirectPaymentsFactory.sol | 2 +- .../DirectPayments/DirectPaymentsLibrary.sol | 54 +++ .../DirectPayments/DirectPaymentsPool.sol | 45 +-- .../GoodCollective/GoodCollectiveSuperApp.sol | 76 ++++- .../IGoodCollectiveSuperApp.sol | 5 + packages/contracts/contracts/Interfaces.sol | 26 ++ packages/contracts/contracts/UBI/UBIPool.sol | 132 ++++---- .../contracts/UBI/UBIPoolFactory.sol | 12 +- .../contracts/utils/HelperLibrary.sol | 79 +++-- .../DirectPayments.claim.test.ts | 1 + .../DirectPayments.superapp.test.ts | 5 +- .../DirectPayments.superappfees.test.ts | 1 + ...DirectPayments.superappmanagerfees.test.ts | 309 ++++++++++++++++++ .../DirectPaymentsFactory.test.ts | 1 + .../test/UBIPool/UBIPoolFactory.test.ts | 7 +- 15 files changed, 601 insertions(+), 154 deletions(-) create mode 100644 packages/contracts/contracts/DirectPayments/DirectPaymentsLibrary.sol create mode 100644 packages/contracts/contracts/Interfaces.sol create mode 100644 packages/contracts/test/DirectPayments/DirectPayments.superappmanagerfees.test.ts diff --git a/packages/contracts/contracts/DirectPayments/DirectPaymentsFactory.sol b/packages/contracts/contracts/DirectPayments/DirectPaymentsFactory.sol index d57c4736..ea91de72 100644 --- a/packages/contracts/contracts/DirectPayments/DirectPaymentsFactory.sol +++ b/packages/contracts/contracts/DirectPayments/DirectPaymentsFactory.sol @@ -11,7 +11,7 @@ import "@openzeppelin/contracts/proxy/beacon/UpgradeableBeacon.sol"; import { AccessControlUpgradeable } from "@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol"; import { UUPSUpgradeable } from "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol"; -import "hardhat/console.sol"; +// import "hardhat/console.sol"; contract DirectPaymentsFactory is AccessControlUpgradeable, UUPSUpgradeable { error NOT_PROJECT_OWNER(); diff --git a/packages/contracts/contracts/DirectPayments/DirectPaymentsLibrary.sol b/packages/contracts/contracts/DirectPayments/DirectPaymentsLibrary.sol new file mode 100644 index 00000000..536bb6d4 --- /dev/null +++ b/packages/contracts/contracts/DirectPayments/DirectPaymentsLibrary.sol @@ -0,0 +1,54 @@ +// SPDX-License-Identifier: MIT +pragma solidity >=0.8.0; + +import "./DirectPaymentsPool.sol"; + +library DirectPayemntsLibrary { + function _updateMemberLimits( + DirectPaymentsPool.LimitsData storage memberStats, + uint128 reward, + uint64 curMonth + ) internal { + if (memberStats.lastReward + 60 * 60 * 24 < block.timestamp) //more than a day passed since last reward + { + memberStats.daily = reward; + } else { + memberStats.daily += reward; + } + + if (memberStats.lastMonth < curMonth) //month switched + { + memberStats.monthly = reward; + } else { + memberStats.monthly += reward; + } + + memberStats.total += reward; + memberStats.lastReward = uint64(block.timestamp); + memberStats.lastMonth = curMonth; + } + + function _updateGlobalLimits( + DirectPaymentsPool.LimitsData storage globalLimits, + uint128 reward, + uint64 curMonth + ) internal { + if (globalLimits.lastReward + 60 * 60 * 24 < block.timestamp) //more than a day passed since last reward + { + globalLimits.daily = reward; + } else { + globalLimits.daily += reward; + } + + if (globalLimits.lastMonth < curMonth) //month switched + { + globalLimits.monthly = reward; + } else { + globalLimits.monthly += reward; + } + + globalLimits.total += reward; + globalLimits.lastReward = uint64(block.timestamp); + globalLimits.lastMonth = curMonth; + } +} diff --git a/packages/contracts/contracts/DirectPayments/DirectPaymentsPool.sol b/packages/contracts/contracts/DirectPayments/DirectPaymentsPool.sol index da3a9a24..4599cbc7 100644 --- a/packages/contracts/contracts/DirectPayments/DirectPaymentsPool.sol +++ b/packages/contracts/contracts/DirectPayments/DirectPaymentsPool.sol @@ -9,6 +9,7 @@ import { IERC721ReceiverUpgradeable } from "@openzeppelin/contracts-upgradeable/ import { ProvableNFT } from "./ProvableNFT.sol"; import { DirectPaymentsFactory } from "./DirectPaymentsFactory.sol"; +import { DirectPayemntsLibrary } from "./DirectPaymentsLibrary.sol"; import "../GoodCollective/GoodCollectiveSuperApp.sol"; interface IMembersValidator { @@ -85,6 +86,7 @@ contract DirectPaymentsPool is IIdentityV2 uniquenessValidator; IERC20Upgradeable rewardToken; bool allowRewardOverride; + uint32 managerFeeBps; } struct SafetyLimits { @@ -124,6 +126,10 @@ contract DirectPaymentsPool is return IRegistry(address(registry)); } + function getManagerFee() public view override returns (address feeRecipient, uint32 feeBps) { + return (settings.manager, settings.managerFeeBps); + } + /** * @dev Initializes the contract with the given settings and limits. * @param _nft The ProvableNFT contract address. @@ -259,24 +265,7 @@ contract DirectPaymentsPool is return false; } - uint64 curMonth = _month(); - if (memberLimits[member].lastReward + 60 * 60 * 24 < block.timestamp) //more than a day passed since last reward - { - memberLimits[member].daily = reward; - } else { - memberLimits[member].daily += reward; - } - - if (memberLimits[member].lastMonth < curMonth) //month switched - { - memberLimits[member].monthly = reward; - } else { - memberLimits[member].monthly += reward; - } - - memberLimits[member].total += reward; - memberLimits[member].lastReward = uint64(block.timestamp); - memberLimits[member].lastMonth = curMonth; + DirectPayemntsLibrary._updateMemberLimits(memberLimits[member], reward, _month()); if ( memberLimits[member].daily > limits.maxMemberPerDay || @@ -291,25 +280,7 @@ contract DirectPaymentsPool is * @param reward The amount of rewards to enforce and update limits for. */ function _enforceAndUpdateGlobalLimits(uint128 reward) internal { - uint64 curMonth = _month(); - - if (globalLimits.lastReward + 60 * 60 * 24 < block.timestamp) //more than a day passed since last reward - { - globalLimits.daily = reward; - } else { - globalLimits.daily += reward; - } - - if (globalLimits.lastMonth < curMonth) //month switched - { - globalLimits.monthly = reward; - } else { - globalLimits.monthly += reward; - } - - globalLimits.total += reward; - globalLimits.lastReward = uint64(block.timestamp); - globalLimits.lastMonth = curMonth; + DirectPayemntsLibrary._updateGlobalLimits(globalLimits, reward, _month()); if (globalLimits.monthly > limits.maxTotalPerMonth) revert OVER_GLOBAL_LIMITS(); } diff --git a/packages/contracts/contracts/GoodCollective/GoodCollectiveSuperApp.sol b/packages/contracts/contracts/GoodCollective/GoodCollectiveSuperApp.sol index b4e4c2d4..bcd515d4 100644 --- a/packages/contracts/contracts/GoodCollective/GoodCollectiveSuperApp.sol +++ b/packages/contracts/contracts/GoodCollective/GoodCollectiveSuperApp.sol @@ -14,6 +14,8 @@ import "@uniswap/swap-router-contracts/contracts/interfaces/IV3SwapRouter.sol"; import "../DirectPayments/DirectPaymentsFactory.sol"; import "../utils/HelperLibrary.sol"; +// import "hardhat/console.sol"; + abstract contract GoodCollectiveSuperApp is SuperAppBaseFlow { int96 public constant MIN_FLOW_RATE = 386e9; @@ -76,6 +78,8 @@ abstract contract GoodCollectiveSuperApp is SuperAppBaseFlow { function getRegistry() public view virtual returns (IRegistry); + function getManagerFee() public view virtual returns (address admin, uint32 feeBps); + /** * @dev Sets the address of the super token and registers the app with the host * @param _superToken The address of the super token contract @@ -114,7 +118,15 @@ abstract contract GoodCollectiveSuperApp is SuperAppBaseFlow { function getRealtimeStats() public view - returns (uint256 netIncome, uint256 totalFees, int96 incomeFlowRate, int96 feeRate) + returns ( + uint256 netIncome, + uint256 totalFees, + uint256 protocolFees, + uint256 managerFees, + int96 incomeFlowRate, + int96 feeRate, + int96 managerFeeRate + ) { return HelperLibrary.getRealtimeStats(stats, superToken); } @@ -249,6 +261,7 @@ abstract contract GoodCollectiveSuperApp is SuperAppBaseFlow { bytes calldata _ctx ) internal virtual override returns (bytes memory /*newCtx*/) { // Update the supporter's information + return _updateSupporter(_sender, _previousFlowRate, _lastUpdated, _ctx); } @@ -267,10 +280,18 @@ abstract contract GoodCollectiveSuperApp is SuperAppBaseFlow { ) internal returns (bytes memory newCtx) { newCtx = _ctx; bool _isFlow = _ctx.length > 0; - HelperLibrary.updateStats(stats, superToken, getRegistry(), _isFlow ? 0 : uint256(_previousFlowRateOrAmount)); + (address feeRecipient, uint32 feeBps) = getManagerFee(); + HelperLibrary.updateStats( + stats, + superToken, + getRegistry(), + feeBps, + _isFlow ? 0 : uint256(_previousFlowRateOrAmount) + ); // Get the current flow rate for the supporter int96 flowRate = superToken.getFlowRate(_supporter, address(this)); uint256 prevContribution = supporters[_supporter].contribution; + if (_isFlow) { //enforce minimal flow rate if (flowRate > 0 && flowRate < MIN_FLOW_RATE) revert MIN_FLOWRATE(flowRate); @@ -278,21 +299,54 @@ abstract contract GoodCollectiveSuperApp is SuperAppBaseFlow { supporters[_supporter].lastUpdated = uint128(block.timestamp); supporters[_supporter].flowRate = flowRate; supporters[_supporter].contribution += - uint96(int96(_previousFlowRateOrAmount)) * + uint256(_previousFlowRateOrAmount) * (block.timestamp - _lastUpdated); + + // address feeRecipient; + // uint32 feeBps; + if (address(getRegistry()) != address(0)) { + feeRecipient = getRegistry().feeRecipient(); + feeBps = getRegistry().feeBps(); + // console.log("taking fees %s %s", feeRecipient, feeBps); + + newCtx = HelperLibrary.takeFeeFlow( + cfaV1, + superToken, + stats.lastFeeRecipient, + feeRecipient, + feeBps, + flowRate - int96(_previousFlowRateOrAmount), // we use diff, because manager takes fee from many streams not just this one + newCtx + ); + stats.lastFeeRecipient = feeRecipient; + } + // console.log("protocol fee stream ok"); + (feeRecipient, feeBps) = getManagerFee(); + newCtx = HelperLibrary.takeFeeFlow( cfaV1, - stats, superToken, - getRegistry(), - flowRate - int96(_previousFlowRateOrAmount), - _ctx + stats.lastManagerFeeRecipient, + feeRecipient, + feeBps, + flowRate - int96(_previousFlowRateOrAmount), // we use diff, because manager takes fee from many streams not just this one + newCtx ); + + stats.lastManagerFeeRecipient = feeRecipient; + // console.log("admin fee stream ok"); // we update the last rate after we do all changes to our own flows stats.lastIncomeRate = superToken.getNetFlowRate(address(this)); } else { + if (address(getRegistry()) != address(0)) { + feeRecipient = getRegistry().feeRecipient(); + feeBps = getRegistry().feeBps(); + _takeFeeSingle(feeRecipient, feeBps, uint256(_previousFlowRateOrAmount)); + } + (feeRecipient, feeBps) = getManagerFee(); + _takeFeeSingle(feeRecipient, feeBps, uint256(_previousFlowRateOrAmount)); + supporters[_supporter].contribution += uint256(_previousFlowRateOrAmount); - _takeFeeSingle(uint256(_previousFlowRateOrAmount)); } emit SupporterUpdated( @@ -305,12 +359,10 @@ abstract contract GoodCollectiveSuperApp is SuperAppBaseFlow { ); } - function _takeFeeSingle(uint256 _amount) internal { - if (address(getRegistry()) == address(0)) return; - address recipient = getRegistry().feeRecipient(); + function _takeFeeSingle(address recipient, uint32 feeBps, uint256 _amount) internal { if (recipient == address(0)) return; - uint256 fee = (_amount * getRegistry().feeBps()) / 10000; + uint256 fee = (_amount * feeBps) / 10000; TransferHelper.safeTransfer(address(superToken), recipient, fee); } diff --git a/packages/contracts/contracts/GoodCollective/IGoodCollectiveSuperApp.sol b/packages/contracts/contracts/GoodCollective/IGoodCollectiveSuperApp.sol index 146e5096..6f3fc5d1 100644 --- a/packages/contracts/contracts/GoodCollective/IGoodCollectiveSuperApp.sol +++ b/packages/contracts/contracts/GoodCollective/IGoodCollectiveSuperApp.sol @@ -14,5 +14,10 @@ interface IGoodCollectiveSuperApp { uint256 lastUpdate; address lastFeeRecipient; int96 lastIncomeRate; + address lastManagerFeeRecipient; + uint256 protocolFees; + uint256 managerFees; } + + function getAdminFee() external view returns (address admin, uint32 feeBps); } diff --git a/packages/contracts/contracts/Interfaces.sol b/packages/contracts/contracts/Interfaces.sol new file mode 100644 index 00000000..c01d1c3b --- /dev/null +++ b/packages/contracts/contracts/Interfaces.sol @@ -0,0 +1,26 @@ +// SPDX-License-Identifier: MIT + +pragma solidity >=0.8.0; + +import { IERC20Upgradeable } from "@openzeppelin/contracts-upgradeable/token/ERC20/IERC20Upgradeable.sol"; + +interface IMembersValidator { + function isMemberValid( + address pool, + address operator, + address member, + bytes memory extraData + ) external returns (bool); +} + +interface IIdentityV2 { + function getWhitelistedRoot(address member) external view returns (address); +} + +struct PoolSettings { + address manager; + IMembersValidator membersValidator; + IIdentityV2 uniquenessValidator; + IERC20Upgradeable rewardToken; + uint32 managerFeeBps; +} diff --git a/packages/contracts/contracts/UBI/UBIPool.sol b/packages/contracts/contracts/UBI/UBIPool.sol index 5cc9fcb9..50db4416 100644 --- a/packages/contracts/contracts/UBI/UBIPool.sol +++ b/packages/contracts/contracts/UBI/UBIPool.sol @@ -9,19 +9,7 @@ import { IERC721ReceiverUpgradeable } from "@openzeppelin/contracts-upgradeable/ import "../GoodCollective/GoodCollectiveSuperApp.sol"; import "./UBIPoolFactory.sol"; - -interface IMembersValidator { - function isMemberValid( - address pool, - address operator, - address member, - bytes memory extraData - ) external returns (bool); -} - -interface IIdentityV2 { - function getWhitelistedRoot(address member) external view returns (address); -} +import "../Interfaces.sol"; contract UBIPool is AccessControlUpgradeable, GoodCollectiveSuperApp, UUPSUpgradeable { using SafeERC20Upgradeable for IERC20Upgradeable; @@ -33,7 +21,8 @@ contract UBIPool is AccessControlUpgradeable, GoodCollectiveSuperApp, UUPSUpgrad error ALREADY_CLAIMED(address whitelistedRoot); error INVALID_0_VALUE(); error EMPTY_MANAGER(); - error MAX_CLAIMERS_REACHED(); + error MAX_MEMBERS_REACHED(); + error MAX_PERIOD_CLAIMERS_REACHED(uint256 claimers); bytes32 public constant MANAGER_ROLE = keccak256("MANAGER_ROLE"); bytes32 public constant MEMBER_ROLE = keccak256("MEMBER_ROLE"); @@ -53,13 +42,6 @@ contract UBIPool is AccessControlUpgradeable, GoodCollectiveSuperApp, UUPSUpgrad event UBICycleCalculated(uint256 day, uint256 pool, uint256 cycleLength, uint256 dailyUBIPool); event UBIClaimed(address indexed whitelistedRoot, address indexed claimer, uint256 amount); - // Define functions - struct PoolSettings { - address manager; - IMembersValidator membersValidator; - IIdentityV2 uniquenessValidator; - IERC20Upgradeable rewardToken; - } struct UBISettings { //number of days of each UBI pool cycle @@ -70,9 +52,15 @@ contract UBIPool is AccessControlUpgradeable, GoodCollectiveSuperApp, UUPSUpgrad uint32 minActiveUsers; // can you trigger claim for someone else bool claimForEnabled; + // max daily claim amount uint maxClaimAmount; - uint32 maxClaimers; + // max number of members in a pool + uint32 maxMembers; bool onlyMembers; + // max number of members that can claim in a day maxPeriodClaimers <= maxMembers + uint32 maxPeriodClaimers; + // min daily claim amount, daily amount will be 0 if uint256) lastClaimed; - uint32 claimersCount; + uint32 membersCount; } PoolSettings public settings; @@ -112,6 +100,10 @@ contract UBIPool is AccessControlUpgradeable, GoodCollectiveSuperApp, UUPSUpgrad return IRegistry(address(registry)); } + function getManagerFee() public view override returns (address feeRecipient, uint32 feeBps) { + return (settings.manager, settings.managerFeeBps); + } + /** * @dev Initializes the contract with the given settings and limits. * @param _settings The PoolSettings struct containing pool settings. @@ -152,32 +144,18 @@ contract UBIPool is AccessControlUpgradeable, GoodCollectiveSuperApp, UUPSUpgrad // once every claim cycle uint256 currentDay = getCurrentDay(); if (currentDay >= status.currentDay + ubiSettings.claimPeriodDays) { - status.currentDay = currentDay; - uint32 cycleLength = ubiSettings.cycleLengthDays; - uint256 currentBalance = settings.rewardToken.balanceOf(address(this)); - //start early cycle if daily pool size is +%5 previous pool or not enough until end of cycle - uint256 nextDailyPool = currentBalance / cycleLength; - bool shouldStartEarlyCycle = nextDailyPool > (status.dailyCyclePool * 105) / 100 || - (currentDayInCycle() <= cycleLength && - currentBalance < (status.dailyCyclePool * (cycleLength - currentDayInCycle()))); - - if ( - currentDayInCycle() >= status.currentCycleLength || shouldStartEarlyCycle - ) //start of cycle or first time - { + (uint256 nextDailyPool, uint256 nextDailyUbi, bool newCycle) = _calcNextDailyUBI(); + if (newCycle) { + uint256 currentBalance = settings.rewardToken.balanceOf(address(this)); status.dailyCyclePool = nextDailyPool; - status.currentCycleLength = cycleLength; + status.currentCycleLength = ubiSettings.cycleLengthDays; status.startOfCycle = currentDay; - emit UBICycleCalculated(currentDay, currentBalance, cycleLength, nextDailyPool); + emit UBICycleCalculated(currentDay, currentBalance, ubiSettings.cycleLengthDays, nextDailyPool); } uint256 prevPeriodClaimers = status.periodClaimers; - status.dailyUbi = min( - ubiSettings.maxClaimAmount, - status.dailyCyclePool / max((prevPeriodClaimers * 10500) / 10000, ubiSettings.minActiveUsers) - ); - //update minActiveUsers as claimers grow - ubiSettings.minActiveUsers = uint32(max(prevPeriodClaimers / 2, ubiSettings.minActiveUsers)); + status.dailyUbi = nextDailyUbi; + if (status.dailyUbi <= ubiSettings.minClaimAmount) status.dailyUbi = 0; emit UBICalculated( currentDay, @@ -193,6 +171,32 @@ contract UBIPool is AccessControlUpgradeable, GoodCollectiveSuperApp, UUPSUpgrad return status.dailyUbi; } + function _calcNextDailyUBI() internal view returns (uint256 nextPeriodPool, uint256 nextDailyUbi, bool newCycle) { + uint256 currentBalance = settings.rewardToken.balanceOf(address(this)); + //start early cycle if we can increase the daily UBI pool + uint256 nextDailyPool = currentBalance / ubiSettings.cycleLengthDays; + bool shouldStartEarlyCycle = nextDailyPool > (status.dailyCyclePool * 105) / 100 || + (currentDayInCycle() <= status.currentCycleLength && + currentBalance < (status.dailyCyclePool * (status.currentCycleLength - currentDayInCycle()))); + + nextPeriodPool = status.dailyCyclePool; + nextDailyUbi; + if ( + (currentDayInCycle() + 1) >= status.currentCycleLength || shouldStartEarlyCycle + ) //start of cycle or first time + { + nextPeriodPool = currentBalance / ubiSettings.cycleLengthDays; + newCycle = true; + } + + nextDailyUbi = min( + ubiSettings.maxClaimAmount, + nextPeriodPool / max((status.periodClaimers * 10500) / 10000, ubiSettings.minActiveUsers) + ); + + if (nextDailyUbi < ubiSettings.minClaimAmount) nextDailyUbi = 0; + } + /** * @dev returns the day count since start of current cycle */ @@ -231,12 +235,15 @@ contract UBIPool is AccessControlUpgradeable, GoodCollectiveSuperApp, UUPSUpgrad if (whitelistedRoot == address(0)) revert NOT_WHITELISTED(claimer); // if open for anyone but has limits, we add the first claimers as members to handle the max claimers - if ((ubiSettings.maxClaimers > 0 && ubiSettings.onlyMembers == false)) _grantRole(MEMBER_ROLE, claimer); + if ((ubiSettings.maxMembers > 0 && ubiSettings.onlyMembers == false)) _grantRole(MEMBER_ROLE, claimer); // check membership if has claimers limits or limited to members only - if ((ubiSettings.maxClaimers > 0 || ubiSettings.onlyMembers) && hasRole(MEMBER_ROLE, claimer) == false) + if ((ubiSettings.maxMembers > 0 || ubiSettings.onlyMembers) && hasRole(MEMBER_ROLE, claimer) == false) revert NOT_MEMBER(claimer); + if (ubiSettings.maxPeriodClaimers > 0 && status.periodClaimers >= ubiSettings.maxPeriodClaimers) + revert MAX_PERIOD_CLAIMERS_REACHED(status.periodClaimers); + // calculats the formula up today ie on day 0 there are no active users, on day 1 any user // (new or active) will trigger the calculation with the active users count of the day before // and so on. the new or inactive users that will become active today, will not take into account @@ -287,17 +294,17 @@ contract UBIPool is AccessControlUpgradeable, GoodCollectiveSuperApp, UUPSUpgrad function _grantRole(bytes32 role, address account) internal virtual override { if (role == MEMBER_ROLE && hasRole(MEMBER_ROLE, account) == false) { - if (ubiSettings.maxClaimers > 0 && status.claimersCount > ubiSettings.maxClaimers) - revert MAX_CLAIMERS_REACHED(); + if (ubiSettings.maxMembers > 0 && status.membersCount > ubiSettings.maxMembers) + revert MAX_MEMBERS_REACHED(); registry.addMember(account); - status.claimersCount += 1; + status.membersCount += 1; } super._grantRole(role, account); } function _revokeRole(bytes32 role, address account) internal virtual override { if (role == MEMBER_ROLE && hasRole(MEMBER_ROLE, account)) { - status.claimersCount -= 1; + status.membersCount -= 1; registry.removeMember(account); } super._revokeRole(role, account); @@ -345,28 +352,8 @@ contract UBIPool is AccessControlUpgradeable, GoodCollectiveSuperApp, UUPSUpgrad ) revert INVALID_0_VALUE(); } - function estimateNextDailyUBI() public view returns (uint256) { - uint256 currentBalance = settings.rewardToken.balanceOf(address(this)); - //start early cycle if we can increase the daily UBI pool - uint256 nextDailyPool = currentBalance / ubiSettings.cycleLengthDays; - bool shouldStartEarlyCycle = nextDailyPool > (status.dailyCyclePool * 105) / 100 || - (currentDayInCycle() <= status.currentCycleLength && - currentBalance < (status.dailyCyclePool * (status.currentCycleLength - currentDayInCycle()))); - - uint256 _dailyCyclePool = status.dailyCyclePool; - uint256 _dailyUbi; - if ( - (currentDayInCycle() + 1) >= status.currentCycleLength || shouldStartEarlyCycle - ) //start of cycle or first time - { - _dailyCyclePool = currentBalance / ubiSettings.cycleLengthDays; - } - - _dailyUbi = min( - ubiSettings.maxClaimAmount, - _dailyCyclePool / max((status.periodClaimers * 10500) / 10000, ubiSettings.minActiveUsers) - ); - return _dailyUbi; + function estimateNextDailyUBI() public view returns (uint256 nextDailyUbi) { + (, nextDailyUbi, ) = _calcNextDailyUBI(); } function checkEntitlement() public view returns (uint256) { @@ -384,6 +371,7 @@ contract UBIPool is AccessControlUpgradeable, GoodCollectiveSuperApp, UUPSUpgrad // current day has already been updated which means // that the dailyUbi has been updated if (status.currentDay == getCurrentDay() && status.dailyUbi > 0) { + if (ubiSettings.maxPeriodClaimers > 0 && status.periodClaimers >= ubiSettings.maxPeriodClaimers) return 0; return hasClaimed(_member) ? 0 : status.dailyUbi; } return estimateNextDailyUBI(); diff --git a/packages/contracts/contracts/UBI/UBIPoolFactory.sol b/packages/contracts/contracts/UBI/UBIPoolFactory.sol index a1480c35..1930896b 100644 --- a/packages/contracts/contracts/UBI/UBIPoolFactory.sol +++ b/packages/contracts/contracts/UBI/UBIPoolFactory.sol @@ -10,7 +10,9 @@ import "@openzeppelin/contracts/proxy/beacon/UpgradeableBeacon.sol"; import { AccessControlUpgradeable } from "@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol"; import { UUPSUpgradeable } from "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol"; -import "hardhat/console.sol"; +import "../Interfaces.sol"; + +// import "hardhat/console.sol"; contract UBIPoolFactory is AccessControlUpgradeable, UUPSUpgradeable { error NOT_PROJECT_OWNER(); @@ -20,7 +22,7 @@ contract UBIPoolFactory is AccessControlUpgradeable, UUPSUpgradeable { address indexed pool, string indexed projectId, string ipfs, - UBIPool.PoolSettings poolSettings, + PoolSettings poolSettings, UBIPool.UBISettings poolLimits ); @@ -84,7 +86,7 @@ contract UBIPoolFactory is AccessControlUpgradeable, UUPSUpgradeable { function createManagedPool( string memory _projectId, string memory _ipfs, - UBIPool.PoolSettings memory _settings, + PoolSettings memory _settings, UBIPool.UBISettings memory _limits ) external onlyProjectOwnerOrNon(_projectId) returns (UBIPool pool) { return _createPool(_projectId, _ipfs, _settings, _limits, true); @@ -93,7 +95,7 @@ contract UBIPoolFactory is AccessControlUpgradeable, UUPSUpgradeable { function createPool( string memory _projectId, string memory _ipfs, - UBIPool.PoolSettings memory _settings, + PoolSettings memory _settings, UBIPool.UBISettings memory _limits ) external onlyProjectOwnerOrNon(_projectId) returns (UBIPool pool) { return _createPool(_projectId, _ipfs, _settings, _limits, false); @@ -102,7 +104,7 @@ contract UBIPoolFactory is AccessControlUpgradeable, UUPSUpgradeable { function _createPool( string memory _projectId, string memory _ipfs, - UBIPool.PoolSettings memory _settings, + PoolSettings memory _settings, UBIPool.UBISettings memory _limits, bool useBeacon ) internal returns (UBIPool pool) { diff --git a/packages/contracts/contracts/utils/HelperLibrary.sol b/packages/contracts/contracts/utils/HelperLibrary.sol index 84053ba2..7a0d451d 100644 --- a/packages/contracts/contracts/utils/HelperLibrary.sol +++ b/packages/contracts/contracts/utils/HelperLibrary.sol @@ -10,6 +10,8 @@ import { CFAv1Library } from "@superfluid-finance/ethereum-contracts/contracts/a import "../GoodCollective/IGoodCollectiveSuperApp.sol"; +// import "hardhat/console.sol"; + library HelperLibrary { using SuperTokenV1Library for ISuperToken; using CFAv1Library for CFAv1Library.InitData; @@ -81,14 +83,33 @@ library HelperLibrary { function getRealtimeStats( IGoodCollectiveSuperApp.Stats memory stats, ISuperToken superToken - ) external view returns (uint256 netIncome, uint256 totalFees, int96 incomeFlowRate, int96 feeRate) { + ) + external + view + returns ( + uint256 netIncome, + uint256 totalFees, + uint256 protocolFees, + uint256 managerFees, + int96 incomeFlowRate, + int96 feeRate, + int96 managerFeeRate + ) + { incomeFlowRate = stats.lastIncomeRate; netIncome = stats.netIncome + uint96(stats.lastIncomeRate) * (block.timestamp - stats.lastUpdate); feeRate = superToken.getFlowRate(address(this), stats.lastFeeRecipient); - totalFees = - stats.totalFees + + managerFeeRate = superToken.getFlowRate(address(this), stats.lastManagerFeeRecipient); + + protocolFees = + stats.protocolFees + uint96(superToken.getFlowRate(address(this), stats.lastFeeRecipient)) * (block.timestamp - stats.lastUpdate); + managerFees = + stats.managerFees + + uint96(superToken.getFlowRate(address(this), stats.lastManagerFeeRecipient)) * + (block.timestamp - stats.lastUpdate); + totalFees = protocolFees + managerFees; } // this should be called before any flow rate changes @@ -96,54 +117,66 @@ library HelperLibrary { IGoodCollectiveSuperApp.Stats storage stats, ISuperToken superToken, IRegistry registry, + uint32 managerFeeBps, uint256 _amount ) external { - //use last rate before the current possible rate update - stats.netIncome += uint96(stats.lastIncomeRate) * (block.timestamp - stats.lastUpdate); uint feeBps; if (address(registry) != address(0)) { feeBps = registry.feeBps(); + } + //use last rate before the current possible rate update + stats.netIncome += uint96(stats.lastIncomeRate) * (block.timestamp - stats.lastUpdate); + if (stats.lastFeeRecipient != address(0)) { //fees sent to last recipient, the flowRate to recipient still wasnt updated. - stats.totalFees += + stats.protocolFees += uint96(superToken.getFlowRate(address(this), stats.lastFeeRecipient)) * (block.timestamp - stats.lastUpdate); } + if (stats.lastManagerFeeRecipient != address(0)) { + //fees sent to last recipient, the flowRate to recipient still wasnt updated. + stats.managerFees += + uint96(superToken.getFlowRate(address(this), stats.lastManagerFeeRecipient)) * + (block.timestamp - stats.lastUpdate); + } + if (_amount > 0) { - stats.netIncome += (_amount * (10000 - feeBps)) / 10000; - stats.totalFees += (_amount * feeBps) / 10000; + stats.netIncome += (_amount * (10000 - feeBps - managerFeeBps)) / 10000; + stats.protocolFees += (_amount * feeBps) / 10000; + stats.managerFees += (_amount * managerFeeBps) / 10000; } + stats.totalFees = stats.managerFees + stats.protocolFees; stats.lastUpdate = block.timestamp; } function takeFeeFlow( CFAv1Library.InitData storage cfaV1, - IGoodCollectiveSuperApp.Stats storage stats, ISuperToken superToken, - IRegistry registry, + address prevRecipient, + address recipient, + uint32 feeBps, int96 _diffRate, bytes memory _ctx ) public returns (bytes memory newCtx) { newCtx = _ctx; - if (address(registry) == address(0)) return newCtx; - address recipient = registry.feeRecipient(); - int96 curFeeRate = superToken.getFlowRate(address(this), stats.lastFeeRecipient); + if (address(recipient) == address(0)) return newCtx; + int96 curFeeRate = superToken.getFlowRate(address(this), prevRecipient); bool newRecipient; - if (recipient != stats.lastFeeRecipient) { + if (recipient != prevRecipient) { newRecipient = true; - if (stats.lastFeeRecipient != address(0)) { + if (prevRecipient != address(0)) { //delete old recipient flow - if (curFeeRate > 0) - newCtx = cfaV1.deleteFlowWithCtx(newCtx, address(this), stats.lastFeeRecipient, superToken); //passing in the ctx which is sent to the callback here + if (curFeeRate > 0) newCtx = cfaV1.deleteFlowWithCtx(newCtx, address(this), prevRecipient, superToken); //passing in the ctx which is sent to the callback here } - stats.lastFeeRecipient = recipient; } if (recipient == address(0)) return newCtx; - int96 newFeeRate = curFeeRate + (_diffRate * int32(registry.feeBps())) / 10000; - if (newFeeRate <= 0 && newRecipient == false) { - newCtx = cfaV1.deleteFlowWithCtx(newCtx, address(this), recipient, superToken); //passing in the ctx which is sent to the callback here - } else if (curFeeRate > 0 && newRecipient == false) { - newCtx = cfaV1.updateFlowWithCtx(newCtx, recipient, superToken, newFeeRate); //passing in the ctx which is sent to the callback here + int96 newFeeRate = curFeeRate + (_diffRate * int32(feeBps)) / 10000; + if (newRecipient == false && curFeeRate > 0) { + if (newFeeRate <= 0) { + newCtx = cfaV1.deleteFlowWithCtx(newCtx, address(this), recipient, superToken); //passing in the ctx which is sent to the callback here + } else { + newCtx = cfaV1.updateFlowWithCtx(newCtx, recipient, superToken, newFeeRate); //passing in the ctx which is sent to the callback here + } } else if (newFeeRate > 0) newCtx = cfaV1.createFlowWithCtx(newCtx, recipient, superToken, newFeeRate); //passing in the ctx which is sent to the callback here } } diff --git a/packages/contracts/test/DirectPayments/DirectPayments.claim.test.ts b/packages/contracts/test/DirectPayments/DirectPayments.claim.test.ts index 4a66d57e..ea252c4f 100644 --- a/packages/contracts/test/DirectPayments/DirectPayments.claim.test.ts +++ b/packages/contracts/test/DirectPayments/DirectPayments.claim.test.ts @@ -55,6 +55,7 @@ describe('DirectPaymentsPool Claim', () => { membersValidator: ethers.constants.AddressZero, rewardToken: gdframework.GoodDollar.address, allowRewardOverride: false, + managerFeeBps: 0 }; poolLimits = { diff --git a/packages/contracts/test/DirectPayments/DirectPayments.superapp.test.ts b/packages/contracts/test/DirectPayments/DirectPayments.superapp.test.ts index 4c480320..02da9a65 100644 --- a/packages/contracts/test/DirectPayments/DirectPayments.superapp.test.ts +++ b/packages/contracts/test/DirectPayments/DirectPayments.superapp.test.ts @@ -64,6 +64,7 @@ describe('DirectPaymentsPool Superapp', () => { membersValidator: ethers.constants.AddressZero, rewardToken: gdframework.GoodDollar.address, allowRewardOverride: false, + managerFeeBps: 0 }; poolLimits = { @@ -135,9 +136,9 @@ describe('DirectPaymentsPool Superapp', () => { await mine(2, { interval: 5 }); expect(await gdframework.GoodDollar.balanceOf(pool.address)).gte(Number(baseFlowRate) * 5); - await st.deleteFlow({ receiver: pool.address, sender: signer.address }).exec(signer); + await expect(st.deleteFlow({ receiver: pool.address, sender: signer.address }).exec(signer)).not.reverted const supporter = await pool.supporters(signer.address); - expect(supporter.contribution).gt(Number(baseFlowRate) * 5); + expect(supporter.contribution).gte(Number(baseFlowRate) * 5); expect(supporter.lastUpdated).gt(0); expect(supporter.flowRate).equal(0); }); diff --git a/packages/contracts/test/DirectPayments/DirectPayments.superappfees.test.ts b/packages/contracts/test/DirectPayments/DirectPayments.superappfees.test.ts index 42a22ada..4e2a787b 100644 --- a/packages/contracts/test/DirectPayments/DirectPayments.superappfees.test.ts +++ b/packages/contracts/test/DirectPayments/DirectPayments.superappfees.test.ts @@ -64,6 +64,7 @@ describe('DirectPaymentsPool Superapp with Fees', () => { membersValidator: ethers.constants.AddressZero, rewardToken: gdframework.GoodDollar.address, allowRewardOverride: false, + managerFeeBps: 0 }; poolLimits = { diff --git a/packages/contracts/test/DirectPayments/DirectPayments.superappmanagerfees.test.ts b/packages/contracts/test/DirectPayments/DirectPayments.superappmanagerfees.test.ts new file mode 100644 index 00000000..b377f4af --- /dev/null +++ b/packages/contracts/test/DirectPayments/DirectPayments.superappmanagerfees.test.ts @@ -0,0 +1,309 @@ +import { deploySuperGoodDollar } from '@gooddollar/goodprotocol'; +import { loadFixture, mine, time } from '@nomicfoundation/hardhat-network-helpers'; +import { deployTestFramework } from '@superfluid-finance/ethereum-contracts/dev-scripts/deploy-test-framework'; +import { Framework } from '@superfluid-finance/sdk-core'; + +import { expect } from 'chai'; +import { DirectPaymentsPool, ProvableNFT, DirectPaymentsFactory } from 'typechain-types'; +import { ethers, upgrades, network } from 'hardhat'; +import ERC20ABI from '@openzeppelin/contracts/build/contracts/ERC20PresetMinterPauser.json'; + +type SignerWithAddress = Awaited>; + +describe('Superapp with Manager Fees', () => { + let pool: DirectPaymentsPool; + let nft: ProvableNFT; + let signer: SignerWithAddress; + let signers: SignerWithAddress[]; + let poolSettings: DirectPaymentsPool.PoolSettingsStruct; + let poolLimits: DirectPaymentsPool.SafetyLimitsStruct; + let gdframework: Awaited>; + let sf: Framework; + let factory: DirectPaymentsFactory; + const baseFlowRate = ethers.BigNumber.from(400e9).toString(); + + const nftSample = { + nftUri: 'uri', + nftType: 2, + version: 2, + events: [ + { + subtype: 1, + quantity: 1, + timestamp: 1, + eventUri: 'uri2', + contributers: ['0xdA030751FF448Cf127911f0518a2B9b012f72424'], + }, + ], + }; + + before(async () => { + const { frameworkDeployer } = await deployTestFramework(); + const sfFramework = await frameworkDeployer.getFramework(); + // initialize framework + const opts = { + chainId: network.config.chainId || 31337, + provider: ethers.provider, + resolverAddress: sfFramework.resolver, + protocolReleaseVersion: 'test', + }; + sf = await Framework.create(opts); + + signers = await ethers.getSigners(); + gdframework = await deploySuperGoodDollar(signers[0], sfFramework, [ + ethers.constants.AddressZero, + ethers.constants.AddressZero, + ]); + signer = signers[0]; + poolSettings = { + nftType: 1, + uniquenessValidator: ethers.constants.AddressZero, + rewardPerEvent: [100, 300], + validEvents: [1, 2], + manager: signers[3].address, + membersValidator: ethers.constants.AddressZero, + rewardToken: gdframework.GoodDollar.address, + allowRewardOverride: false, + managerFeeBps: 200 + }; + + poolLimits = { + maxMemberPerDay: 300, + maxMemberPerMonth: 1000, + maxTotalPerMonth: 3000, + }; + + const f = await ethers.getContractFactory('DirectPaymentsFactory'); + const swapMock = await ethers.deployContract('SwapRouterMock', [gdframework.GoodDollar.address]); + + const helper = await ethers.deployContract('HelperLibrary'); + + const dpimpl = await ethers.deployContract('DirectPaymentsPool', [sfFramework['host'], swapMock.address], { + libraries: { HelperLibrary: helper.address }, + }); + + nft = (await upgrades.deployProxy(await ethers.getContractFactory('ProvableNFT'), ['nft', 'cc'], { + kind: 'uups', + })) as ProvableNFT; + + factory = (await upgrades.deployProxy(f, [signer.address, dpimpl.address, nft.address, signers[1].address, 1000], { + unsafeAllowLinkedLibraries: true, + kind: 'uups', + })) as DirectPaymentsFactory; + + await nft.grantRole(ethers.constants.HashZero, factory.address); + }); + + const fixture = async () => { + const tx = await factory.createPool('testfees', 'ipfs', poolSettings, poolLimits); + const poolAddr = (await tx.wait()).events?.find((_) => _.event === 'PoolCreated')?.args?.[0]; + pool = await ethers.getContractAt('DirectPaymentsPool', poolAddr); + }; + + beforeEach(async function () { + await loadFixture(fixture); + }); + + it('should receive fee from stream G$s', async () => { + + const expectedProtocolFee = + (Number(baseFlowRate) * (await factory.feeBps())) / 10000; + const expectedManagerFee = (Number(baseFlowRate) * Number(poolSettings.managerFeeBps)) / 10000; + + + await gdframework.GoodDollar.mint(signer.address, ethers.constants.WeiPerEther); + const st = await sf.loadSuperToken(gdframework.GoodDollar.address); + await st.createFlow({ receiver: pool.address, sender: signer.address, flowRate: baseFlowRate }).exec(signer); + const feeFlow = await st.getFlow({ + sender: pool.address, + receiver: await factory.feeRecipient(), + providerOrSigner: ethers.provider, + }); + + expect(Number(feeFlow.flowRate)).eq(expectedProtocolFee); + const managerFeeFlow = await st.getFlow({ + sender: pool.address, + receiver: poolSettings.manager as any, + providerOrSigner: ethers.provider, + }); + expect(Number(managerFeeFlow.flowRate)).eq(expectedManagerFee); + const stats = await pool.stats(); + expect(stats.netIncome).eq(0); + expect(stats.totalFees).eq(0); + + await time.increase(10) + const realTimeStats = await pool.getRealtimeStats(); + + expect(realTimeStats.totalFees).gte((expectedManagerFee + expectedProtocolFee) * 10); + expect(realTimeStats.protocolFees).gte(expectedProtocolFee * 10) + expect(realTimeStats.managerFees).gte(expectedManagerFee * 10) + expect(realTimeStats.netIncome).gte((Number(baseFlowRate) - expectedManagerFee - expectedProtocolFee) * 10); + }); + + it('should decrease fee when stopped streaming', async () => { + const expectedProtocolFee = + (Number(baseFlowRate) * (await factory.feeBps())) / 10000; + const expectedManagerFee = (Number(baseFlowRate) * Number(poolSettings.managerFeeBps)) / 10000; + + await gdframework.GoodDollar.mint(signer.address, ethers.constants.WeiPerEther.mul(10000)); + const st = await sf.loadSuperToken(gdframework.GoodDollar.address); + await st.createFlow({ receiver: pool.address, sender: signer.address, flowRate: baseFlowRate }).exec(signer); + const feeFlow = await st.getFlow({ + sender: pool.address, + receiver: await factory.feeRecipient(), + providerOrSigner: ethers.provider, + }); + expect(Number(feeFlow.flowRate)).eq((Number(baseFlowRate) * (await factory.feeBps())) / 10000); + + await time.increase(10); + await st.deleteFlow({ receiver: pool.address, sender: signer.address }).exec(signer); + const feeFlowAfter = await st.getFlow({ + sender: pool.address, + receiver: await factory.feeRecipient(), + providerOrSigner: ethers.provider, + }); + const managerFeeFlowAfter = await st.getFlow({ + sender: pool.address, + receiver: poolSettings.manager as any, + providerOrSigner: ethers.provider, + }); + expect(Number(feeFlowAfter.flowRate)).eq(0); + expect(Number(managerFeeFlowAfter.flowRate)).eq(0); + + const stats = await pool.stats(); + expect(stats.totalFees).gte((expectedManagerFee + expectedProtocolFee) * 10); + expect(stats.protocolFees).gte(expectedProtocolFee * 10) + expect(stats.managerFees).gte(expectedManagerFee * 10) + expect(stats.netIncome).gte((Number(baseFlowRate) - expectedManagerFee - expectedProtocolFee) * 10); + }); + + it('should update feeflow', async () => { + + const expectedProtocolFee = + (Number(baseFlowRate) * (await factory.feeBps())) / 10000; + const expectedManagerFee = (Number(baseFlowRate) * Number(poolSettings.managerFeeBps)) / 10000; + + const updatedFlowRate = ethers.utils.parseEther("0.000001") + + await gdframework.GoodDollar.mint(signer.address, ethers.constants.WeiPerEther.mul(100000)); + const st = await sf.loadSuperToken(gdframework.GoodDollar.address); + await st + .createFlow({ receiver: pool.address, sender: signer.address, flowRate: ethers.constants.WeiPerEther.toString() }) + .exec(signer); + const feeFlow = await st.getFlow({ + sender: pool.address, + receiver: await factory.feeRecipient(), + providerOrSigner: ethers.provider, + }); + expect(feeFlow.flowRate).eq( + ethers.constants.WeiPerEther.mul(await factory.feeBps()) + .div(10000) + .toString() + ).not.eq("0") + + await time.increase(10); + await st + .updateFlow({ + receiver: pool.address, + sender: signer.address, + flowRate: updatedFlowRate.toString(), + }) + .exec(signer); + const afterFeeFlow = await st.getFlow({ + sender: pool.address, + receiver: await factory.feeRecipient(), + providerOrSigner: ethers.provider, + }); + expect(afterFeeFlow.flowRate).eq( + updatedFlowRate + .mul(await factory.feeBps()) + .div(10000) + ); + + const manaerAfterFeeFlow = await st.getFlow({ + sender: pool.address, + receiver: poolSettings.manager as any, + providerOrSigner: ethers.provider, + }); + expect(manaerAfterFeeFlow.flowRate).eq( + updatedFlowRate + .mul(Number(poolSettings.managerFeeBps)) + .div(10000) + ); + + await time.increase(10); + + const stats = await pool.stats(); + + await time.increase(10) + const realTimeStats = await pool.getRealtimeStats(); + expect(stats.netIncome).gt(0); + expect(stats.totalFees).gt(0); + expect(realTimeStats.totalFees).gt(stats.totalFees); + expect(realTimeStats.netIncome).gt(stats.netIncome); + + + const expectedUpdatedProtocolFee = + (Number(updatedFlowRate) * (await factory.feeBps())) / 10000; + const expectedUpdatedManagerFee = (Number(updatedFlowRate) * Number(poolSettings.managerFeeBps)) / 10000; + expect(stats.totalFees).gte((expectedManagerFee + expectedProtocolFee) * 10 + (expectedUpdatedManagerFee + expectedUpdatedProtocolFee) * 10); + expect(stats.protocolFees).gte(expectedProtocolFee * 10 + expectedUpdatedProtocolFee * 10) + expect(stats.managerFees).gte(expectedManagerFee * 10 + expectedUpdatedManagerFee * 10) + expect(stats.netIncome).gte((Number(baseFlowRate) - expectedManagerFee - expectedProtocolFee) * 10 + (Number(updatedFlowRate) - expectedUpdatedProtocolFee - expectedUpdatedManagerFee) * 10); + }); + + it('should take fee from single contribution using transferAndCall', async () => { + //mint to the swaprouter so it has G$s to send in exchange + await gdframework.GoodDollar.mint(signer.address, ethers.constants.WeiPerEther); + const st = gdframework.GoodDollar; + await st.transferAndCall(pool.address, 10000000, '0x'); + expect(await gdframework.GoodDollar.balanceOf(pool.address)).eq(10000000 * (10000 - await factory.feeBps() - Number(poolSettings.managerFeeBps)) / 10000); + expect(await gdframework.GoodDollar.balanceOf(await factory.feeRecipient())).eq( + (10000000 * (await factory.feeBps())) / 10000 + ); + expect(await gdframework.GoodDollar.balanceOf(poolSettings.manager)).eq( + (10000000 * Number(poolSettings.managerFeeBps)) / 10000 + ); + const stats = await pool.stats(); + expect(stats.netIncome).eq(10000000 * (10000 - await factory.feeBps() - Number(poolSettings.managerFeeBps)) / 10000); + expect(stats.totalFees).eq(((10000000 * (await factory.feeBps())) / 10000) + (10000000 * Number(poolSettings.managerFeeBps)) / 10000); + expect(stats.managerFees).eq((10000000 * Number(poolSettings.managerFeeBps)) / 10000); + expect(stats.protocolFees).eq((10000000 * (await factory.feeBps())) / 10000); + + await mine(2, { interval: 5 }); + + const realTimeStats = await pool.getRealtimeStats(); + expect(realTimeStats.totalFees).eq(stats.totalFees); + expect(realTimeStats.netIncome).eq(stats.netIncome); + expect(realTimeStats.managerFees).eq(stats.managerFees); + expect(realTimeStats.protocolFees).eq(stats.protocolFees); + }); + + it('should be take fee from single contribution by calling support', async () => { + //mint to the swaprouter so it has G$s to send in exchange + await gdframework.GoodDollar.mint(signer.address, ethers.constants.WeiPerEther); + const st = gdframework.GoodDollar; + const transferAction = await st.approve(pool.address, 10000000); + const supportAction = await pool.support(signer.address, 10000000, '0x'); + expect(await gdframework.GoodDollar.balanceOf(pool.address)).eq(10000000 * (10000 - await factory.feeBps() - Number(poolSettings.managerFeeBps)) / 10000); + expect(await gdframework.GoodDollar.balanceOf(await factory.feeRecipient())).eq( + (10000000 * (await factory.feeBps())) / 10000 + ); + expect(await gdframework.GoodDollar.balanceOf(poolSettings.manager)).eq( + (10000000 * Number(poolSettings.managerFeeBps)) / 10000 + ); + const stats = await pool.stats(); + expect(stats.netIncome).eq(10000000 * (10000 - await factory.feeBps() - Number(poolSettings.managerFeeBps)) / 10000); + expect(stats.totalFees).eq(((10000000 * (await factory.feeBps())) / 10000) + (10000000 * Number(poolSettings.managerFeeBps)) / 10000); + expect(stats.managerFees).eq((10000000 * Number(poolSettings.managerFeeBps)) / 10000); + expect(stats.protocolFees).eq((10000000 * (await factory.feeBps())) / 10000); + + await mine(2, { interval: 5 }); + + const realTimeStats = await pool.getRealtimeStats(); + expect(realTimeStats.totalFees).eq(stats.totalFees); + expect(realTimeStats.netIncome).eq(stats.netIncome); + expect(realTimeStats.managerFees).eq(stats.managerFees); + expect(realTimeStats.protocolFees).eq(stats.protocolFees); + }); +}); diff --git a/packages/contracts/test/DirectPayments/DirectPaymentsFactory.test.ts b/packages/contracts/test/DirectPayments/DirectPaymentsFactory.test.ts index ae2ab533..d5f1ba88 100644 --- a/packages/contracts/test/DirectPayments/DirectPaymentsFactory.test.ts +++ b/packages/contracts/test/DirectPayments/DirectPaymentsFactory.test.ts @@ -76,6 +76,7 @@ describe('DirectPaymentsFactory', () => { membersValidator: ethers.constants.AddressZero, rewardToken: '0x03d3daB843e6c03b3d271eff9178e6A96c28D25f', allowRewardOverride: false, + managerFeeBps: 0 }; poolLimits = { diff --git a/packages/contracts/test/UBIPool/UBIPoolFactory.test.ts b/packages/contracts/test/UBIPool/UBIPoolFactory.test.ts index 416fbce8..60586ab7 100644 --- a/packages/contracts/test/UBIPool/UBIPoolFactory.test.ts +++ b/packages/contracts/test/UBIPool/UBIPoolFactory.test.ts @@ -53,7 +53,8 @@ describe('UBIPoolFactory', () => { uniquenessValidator: '0xF25fA0D4896271228193E782831F6f3CFCcF169C', manager: signers[1].address, membersValidator: ethers.constants.AddressZero, - rewardToken: '0x03d3daB843e6c03b3d271eff9178e6A96c28D25f' + rewardToken: '0x03d3daB843e6c03b3d271eff9178e6A96c28D25f', + managerFeeBps: 0, }; poolLimits = { @@ -62,8 +63,10 @@ describe('UBIPoolFactory', () => { minActiveUsers: ethers.BigNumber.from(100), claimForEnabled: true, maxClaimAmount: ethers.utils.parseEther('100'), - maxClaimers: 500, + maxMembers: 500, onlyMembers: true, + maxPeriodClaimers: 500, + minClaimAmount: ethers.utils.parseEther('1'), }; }); From 545caf650b754bed915bcec545ed45873f8eaf5f Mon Sep 17 00:00:00 2001 From: sirpy Date: Thu, 7 Nov 2024 16:02:22 +0200 Subject: [PATCH 2/5] add: updated deployments --- packages/contracts/hardhat.config.ts | 6 +- packages/contracts/releases/deployment.json | 398 ++++++++++++++++++-- 2 files changed, 373 insertions(+), 31 deletions(-) diff --git a/packages/contracts/hardhat.config.ts b/packages/contracts/hardhat.config.ts index d04c0630..51ee23bc 100644 --- a/packages/contracts/hardhat.config.ts +++ b/packages/contracts/hardhat.config.ts @@ -10,6 +10,8 @@ import { HardhatUserConfig } from 'hardhat/config'; dotenv.config(); const mnemonic = process.env.MNEMONIC || ''; +const privateKey = process.env.PRIVATE_KEY || ''; + const config: HardhatUserConfig = { contractSizer: { alphaSort: true, @@ -81,9 +83,7 @@ const config: HardhatUserConfig = { chainId: 42220, url: `https://forno.celo.org`, gasPrice: 5000000000, - accounts: { - mnemonic, - }, + accounts: [privateKey], verify: { etherscan: { apiKey: process.env.CELOSCAN_KEY, diff --git a/packages/contracts/releases/deployment.json b/packages/contracts/releases/deployment.json index 3fbe8a18..420cb34c 100644 --- a/packages/contracts/releases/deployment.json +++ b/packages/contracts/releases/deployment.json @@ -27707,6 +27707,11 @@ "internalType": "bool", "name": "allowRewardOverride", "type": "bool" + }, + { + "internalType": "uint32", + "name": "managerFeeBps", + "type": "uint32" } ], "indexed": false, @@ -27964,6 +27969,11 @@ "internalType": "bool", "name": "allowRewardOverride", "type": "bool" + }, + { + "internalType": "uint32", + "name": "managerFeeBps", + "type": "uint32" } ], "internalType": "struct DirectPaymentsPool.PoolSettings", @@ -28057,6 +28067,11 @@ "internalType": "bool", "name": "allowRewardOverride", "type": "bool" + }, + { + "internalType": "uint32", + "name": "managerFeeBps", + "type": "uint32" } ], "internalType": "struct DirectPaymentsPool.PoolSettings", @@ -28527,7 +28542,7 @@ ] }, "DirectPaymentsFactory_Implementation": { - "address": "0x1eFE82eFcf82F67d94e0deD879ba0568aF8b5B6D", + "address": "0xB25801A0E679c987ea49A1C201979081407139C6", "abi": [ { "inputs": [], @@ -28652,6 +28667,11 @@ "internalType": "bool", "name": "allowRewardOverride", "type": "bool" + }, + { + "internalType": "uint32", + "name": "managerFeeBps", + "type": "uint32" } ], "indexed": false, @@ -28922,6 +28942,11 @@ "internalType": "bool", "name": "allowRewardOverride", "type": "bool" + }, + { + "internalType": "uint32", + "name": "managerFeeBps", + "type": "uint32" } ], "internalType": "struct DirectPaymentsPool.PoolSettings", @@ -29015,6 +29040,11 @@ "internalType": "bool", "name": "allowRewardOverride", "type": "bool" + }, + { + "internalType": "uint32", + "name": "managerFeeBps", + "type": "uint32" } ], "internalType": "struct DirectPaymentsPool.PoolSettings", @@ -29543,7 +29573,7 @@ ] }, "DirectPaymentsPool": { - "address": "0xAd78035708FB3912bEb6481583d4E7ff452b03d4", + "address": "0xF7f70A3CfCfE5884a617b9CA11E28Bc08eb88C7f", "abi": [ { "inputs": [ @@ -29886,6 +29916,11 @@ "internalType": "bool", "name": "allowRewardOverride", "type": "bool" + }, + { + "internalType": "uint32", + "name": "managerFeeBps", + "type": "uint32" } ], "indexed": false, @@ -29994,6 +30029,11 @@ "internalType": "bool", "name": "allowRewardOverride", "type": "bool" + }, + { + "internalType": "uint32", + "name": "managerFeeBps", + "type": "uint32" } ], "indexed": false, @@ -30585,6 +30625,24 @@ "stateMutability": "view", "type": "function" }, + { + "inputs": [], + "name": "getManagerFee", + "outputs": [ + { + "internalType": "address", + "name": "feeRecipient", + "type": "address" + }, + { + "internalType": "uint32", + "name": "feeBps", + "type": "uint32" + } + ], + "stateMutability": "view", + "type": "function" + }, { "inputs": [ { @@ -30618,6 +30676,16 @@ "name": "totalFees", "type": "uint256" }, + { + "internalType": "uint256", + "name": "protocolFees", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "managerFees", + "type": "uint256" + }, { "internalType": "int96", "name": "incomeFlowRate", @@ -30627,6 +30695,11 @@ "internalType": "int96", "name": "feeRate", "type": "int96" + }, + { + "internalType": "int96", + "name": "managerFeeRate", + "type": "int96" } ], "stateMutability": "view", @@ -30856,6 +30929,11 @@ "internalType": "bool", "name": "allowRewardOverride", "type": "bool" + }, + { + "internalType": "uint32", + "name": "managerFeeBps", + "type": "uint32" } ], "internalType": "struct DirectPaymentsPool.PoolSettings", @@ -31264,6 +31342,11 @@ "internalType": "bool", "name": "allowRewardOverride", "type": "bool" + }, + { + "internalType": "uint32", + "name": "managerFeeBps", + "type": "uint32" } ], "internalType": "struct DirectPaymentsPool.PoolSettings", @@ -31309,6 +31392,11 @@ "internalType": "bool", "name": "allowRewardOverride", "type": "bool" + }, + { + "internalType": "uint32", + "name": "managerFeeBps", + "type": "uint32" } ], "stateMutability": "view", @@ -31342,6 +31430,21 @@ "internalType": "int96", "name": "lastIncomeRate", "type": "int96" + }, + { + "internalType": "address", + "name": "lastManagerFeeRecipient", + "type": "address" + }, + { + "internalType": "uint256", + "name": "protocolFees", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "managerFees", + "type": "uint256" } ], "stateMutability": "view", @@ -31553,7 +31656,7 @@ ] }, "HelperLibrary": { - "address": "0xbc67CA6E216b37C3496Aa9Aa091a648b575755a4", + "address": "0xA9E671fB33a0Bb36D50607ddd72E7e1519A2B02E", "abi": [ { "inputs": [ @@ -31583,6 +31686,21 @@ "internalType": "int96", "name": "lastIncomeRate", "type": "int96" + }, + { + "internalType": "address", + "name": "lastManagerFeeRecipient", + "type": "address" + }, + { + "internalType": "uint256", + "name": "protocolFees", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "managerFees", + "type": "uint256" } ], "internalType": "struct IGoodCollectiveSuperApp.Stats", @@ -31607,6 +31725,16 @@ "name": "totalFees", "type": "uint256" }, + { + "internalType": "uint256", + "name": "protocolFees", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "managerFees", + "type": "uint256" + }, { "internalType": "int96", "name": "incomeFlowRate", @@ -31616,6 +31744,11 @@ "internalType": "int96", "name": "feeRate", "type": "int96" + }, + { + "internalType": "int96", + "name": "managerFeeRate", + "type": "int96" } ], "stateMutability": "view", @@ -34176,7 +34309,7 @@ ] }, "UBIPool": { - "address": "0x02B23d4c563a29A4D9C87C8bb1fCc6CeC25e41Ed", + "address": "0x8142417D8200757BF29686511F7E0861f2b5c929", "abi": [ { "inputs": [ @@ -34238,7 +34371,18 @@ }, { "inputs": [], - "name": "MAX_CLAIMERS_REACHED", + "name": "MAX_MEMBERS_REACHED", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "claimers", + "type": "uint256" + } + ], + "name": "MAX_PERIOD_CLAIMERS_REACHED", "type": "error" }, { @@ -34395,10 +34539,15 @@ "internalType": "contract IERC20Upgradeable", "name": "rewardToken", "type": "address" + }, + { + "internalType": "uint32", + "name": "managerFeeBps", + "type": "uint32" } ], "indexed": false, - "internalType": "struct UBIPool.PoolSettings", + "internalType": "struct PoolSettings", "name": "settings", "type": "tuple" } @@ -34649,13 +34798,23 @@ }, { "internalType": "uint32", - "name": "maxClaimers", + "name": "maxMembers", "type": "uint32" }, { "internalType": "bool", "name": "onlyMembers", "type": "bool" + }, + { + "internalType": "uint32", + "name": "maxPeriodClaimers", + "type": "uint32" + }, + { + "internalType": "uint256", + "name": "minClaimAmount", + "type": "uint256" } ], "indexed": false, @@ -35112,7 +35271,7 @@ "outputs": [ { "internalType": "uint256", - "name": "", + "name": "nextDailyUbi", "type": "uint256" } ], @@ -35132,6 +35291,24 @@ "stateMutability": "view", "type": "function" }, + { + "inputs": [], + "name": "getManagerFee", + "outputs": [ + { + "internalType": "address", + "name": "feeRecipient", + "type": "address" + }, + { + "internalType": "uint32", + "name": "feeBps", + "type": "uint32" + } + ], + "stateMutability": "view", + "type": "function" + }, { "inputs": [ { @@ -35165,6 +35342,16 @@ "name": "totalFees", "type": "uint256" }, + { + "internalType": "uint256", + "name": "protocolFees", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "managerFees", + "type": "uint256" + }, { "internalType": "int96", "name": "incomeFlowRate", @@ -35174,6 +35361,11 @@ "internalType": "int96", "name": "feeRate", "type": "int96" + }, + { + "internalType": "int96", + "name": "managerFeeRate", + "type": "int96" } ], "stateMutability": "view", @@ -35364,9 +35556,14 @@ "internalType": "contract IERC20Upgradeable", "name": "rewardToken", "type": "address" + }, + { + "internalType": "uint32", + "name": "managerFeeBps", + "type": "uint32" } ], - "internalType": "struct UBIPool.PoolSettings", + "internalType": "struct PoolSettings", "name": "_settings", "type": "tuple" }, @@ -35399,13 +35596,23 @@ }, { "internalType": "uint32", - "name": "maxClaimers", + "name": "maxMembers", "type": "uint32" }, { "internalType": "bool", "name": "onlyMembers", "type": "bool" + }, + { + "internalType": "uint32", + "name": "maxPeriodClaimers", + "type": "uint32" + }, + { + "internalType": "uint256", + "name": "minClaimAmount", + "type": "uint256" } ], "internalType": "struct UBIPool.UBISettings", @@ -35582,9 +35789,14 @@ "internalType": "contract IERC20Upgradeable", "name": "rewardToken", "type": "address" + }, + { + "internalType": "uint32", + "name": "managerFeeBps", + "type": "uint32" } ], - "internalType": "struct UBIPool.PoolSettings", + "internalType": "struct PoolSettings", "name": "_settings", "type": "tuple" } @@ -35625,13 +35837,23 @@ }, { "internalType": "uint32", - "name": "maxClaimers", + "name": "maxMembers", "type": "uint32" }, { "internalType": "bool", "name": "onlyMembers", "type": "bool" + }, + { + "internalType": "uint32", + "name": "maxPeriodClaimers", + "type": "uint32" + }, + { + "internalType": "uint256", + "name": "minClaimAmount", + "type": "uint256" } ], "internalType": "struct UBIPool.UBISettings", @@ -35667,6 +35889,11 @@ "internalType": "contract IERC20Upgradeable", "name": "rewardToken", "type": "address" + }, + { + "internalType": "uint32", + "name": "managerFeeBps", + "type": "uint32" } ], "stateMutability": "view", @@ -35700,6 +35927,21 @@ "internalType": "int96", "name": "lastIncomeRate", "type": "int96" + }, + { + "internalType": "address", + "name": "lastManagerFeeRecipient", + "type": "address" + }, + { + "internalType": "uint256", + "name": "protocolFees", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "managerFees", + "type": "uint256" } ], "stateMutability": "view", @@ -35746,7 +35988,7 @@ }, { "internalType": "uint32", - "name": "claimersCount", + "name": "membersCount", "type": "uint32" } ], @@ -35943,13 +36185,23 @@ }, { "internalType": "uint32", - "name": "maxClaimers", + "name": "maxMembers", "type": "uint32" }, { "internalType": "bool", "name": "onlyMembers", "type": "bool" + }, + { + "internalType": "uint32", + "name": "maxPeriodClaimers", + "type": "uint32" + }, + { + "internalType": "uint256", + "name": "minClaimAmount", + "type": "uint256" } ], "stateMutability": "view", @@ -36122,10 +36374,15 @@ "internalType": "contract IERC20Upgradeable", "name": "rewardToken", "type": "address" + }, + { + "internalType": "uint32", + "name": "managerFeeBps", + "type": "uint32" } ], "indexed": false, - "internalType": "struct UBIPool.PoolSettings", + "internalType": "struct PoolSettings", "name": "poolSettings", "type": "tuple" }, @@ -36158,13 +36415,23 @@ }, { "internalType": "uint32", - "name": "maxClaimers", + "name": "maxMembers", "type": "uint32" }, { "internalType": "bool", "name": "onlyMembers", "type": "bool" + }, + { + "internalType": "uint32", + "name": "maxPeriodClaimers", + "type": "uint32" + }, + { + "internalType": "uint256", + "name": "minClaimAmount", + "type": "uint256" } ], "indexed": false, @@ -36379,9 +36646,14 @@ "internalType": "contract IERC20Upgradeable", "name": "rewardToken", "type": "address" + }, + { + "internalType": "uint32", + "name": "managerFeeBps", + "type": "uint32" } ], - "internalType": "struct UBIPool.PoolSettings", + "internalType": "struct PoolSettings", "name": "_settings", "type": "tuple" }, @@ -36414,13 +36686,23 @@ }, { "internalType": "uint32", - "name": "maxClaimers", + "name": "maxMembers", "type": "uint32" }, { "internalType": "bool", "name": "onlyMembers", "type": "bool" + }, + { + "internalType": "uint32", + "name": "maxPeriodClaimers", + "type": "uint32" + }, + { + "internalType": "uint256", + "name": "minClaimAmount", + "type": "uint256" } ], "internalType": "struct UBIPool.UBISettings", @@ -36472,9 +36754,14 @@ "internalType": "contract IERC20Upgradeable", "name": "rewardToken", "type": "address" + }, + { + "internalType": "uint32", + "name": "managerFeeBps", + "type": "uint32" } ], - "internalType": "struct UBIPool.PoolSettings", + "internalType": "struct PoolSettings", "name": "_settings", "type": "tuple" }, @@ -36507,13 +36794,23 @@ }, { "internalType": "uint32", - "name": "maxClaimers", + "name": "maxMembers", "type": "uint32" }, { "internalType": "bool", "name": "onlyMembers", "type": "bool" + }, + { + "internalType": "uint32", + "name": "maxPeriodClaimers", + "type": "uint32" + }, + { + "internalType": "uint256", + "name": "minClaimAmount", + "type": "uint256" } ], "internalType": "struct UBIPool.UBISettings", @@ -36950,7 +37247,7 @@ ] }, "UBIPoolFactory_Implementation": { - "address": "0x745AE2e7eFD6Cb2b79F16D5dCFD51FF1105708C0", + "address": "0x05e9bE6B03A64c6A0fF48b5bBC1D36fa058A7f5e", "abi": [ { "inputs": [], @@ -37049,10 +37346,15 @@ "internalType": "contract IERC20Upgradeable", "name": "rewardToken", "type": "address" + }, + { + "internalType": "uint32", + "name": "managerFeeBps", + "type": "uint32" } ], "indexed": false, - "internalType": "struct UBIPool.PoolSettings", + "internalType": "struct PoolSettings", "name": "poolSettings", "type": "tuple" }, @@ -37085,13 +37387,23 @@ }, { "internalType": "uint32", - "name": "maxClaimers", + "name": "maxMembers", "type": "uint32" }, { "internalType": "bool", "name": "onlyMembers", "type": "bool" + }, + { + "internalType": "uint32", + "name": "maxPeriodClaimers", + "type": "uint32" + }, + { + "internalType": "uint256", + "name": "minClaimAmount", + "type": "uint256" } ], "indexed": false, @@ -37319,9 +37631,14 @@ "internalType": "contract IERC20Upgradeable", "name": "rewardToken", "type": "address" + }, + { + "internalType": "uint32", + "name": "managerFeeBps", + "type": "uint32" } ], - "internalType": "struct UBIPool.PoolSettings", + "internalType": "struct PoolSettings", "name": "_settings", "type": "tuple" }, @@ -37354,13 +37671,23 @@ }, { "internalType": "uint32", - "name": "maxClaimers", + "name": "maxMembers", "type": "uint32" }, { "internalType": "bool", "name": "onlyMembers", "type": "bool" + }, + { + "internalType": "uint32", + "name": "maxPeriodClaimers", + "type": "uint32" + }, + { + "internalType": "uint256", + "name": "minClaimAmount", + "type": "uint256" } ], "internalType": "struct UBIPool.UBISettings", @@ -37412,9 +37739,14 @@ "internalType": "contract IERC20Upgradeable", "name": "rewardToken", "type": "address" + }, + { + "internalType": "uint32", + "name": "managerFeeBps", + "type": "uint32" } ], - "internalType": "struct UBIPool.PoolSettings", + "internalType": "struct PoolSettings", "name": "_settings", "type": "tuple" }, @@ -37447,13 +37779,23 @@ }, { "internalType": "uint32", - "name": "maxClaimers", + "name": "maxMembers", "type": "uint32" }, { "internalType": "bool", "name": "onlyMembers", "type": "bool" + }, + { + "internalType": "uint32", + "name": "maxPeriodClaimers", + "type": "uint32" + }, + { + "internalType": "uint256", + "name": "minClaimAmount", + "type": "uint256" } ], "internalType": "struct UBIPool.UBISettings", @@ -44081,4 +44423,4 @@ } } ] -} \ No newline at end of file +} From 766b959279f594fbae0a219dfea8eeb70d19e05b Mon Sep 17 00:00:00 2001 From: sirpy Date: Thu, 21 Nov 2024 19:15:20 +0200 Subject: [PATCH 3/5] fix: correct manager fee and ubi settings upgrade --- .../DirectPayments/DirectPaymentsFactory.sol | 16 +- .../DirectPayments/DirectPaymentsPool.sol | 10 +- .../GoodCollective/GoodCollectiveSuperApp.sol | 2 +- .../IGoodCollectiveSuperApp.sol | 1 + packages/contracts/contracts/Interfaces.sol | 1 - packages/contracts/contracts/UBI/UBIPool.sol | 35 +- .../contracts/UBI/UBIPoolFactory.sol | 13 +- packages/contracts/releases/deployment.json | 1156 +++++++++++++---- .../DirectPayments.claim.test.ts | 10 +- .../DirectPayments.superapp.test.ts | 3 +- .../DirectPayments.superappfees.test.ts | 5 +- ...DirectPayments.superappmanagerfees.test.ts | 37 +- .../DirectPaymentsFactory.test.ts | 11 +- packages/contracts/test/Swap.e2e.test.ts | 2 - .../test/UBIPool/UBIPoolFactory.test.ts | 22 +- packages/contracts/tsconfig.json | 4 +- .../__tests__/goodcollective.test.ts | 4 +- .../src/goodcollective/goodcollective.ts | 28 +- 18 files changed, 1045 insertions(+), 315 deletions(-) diff --git a/packages/contracts/contracts/DirectPayments/DirectPaymentsFactory.sol b/packages/contracts/contracts/DirectPayments/DirectPaymentsFactory.sol index ea91de72..36228f69 100644 --- a/packages/contracts/contracts/DirectPayments/DirectPaymentsFactory.sol +++ b/packages/contracts/contracts/DirectPayments/DirectPaymentsFactory.sol @@ -98,18 +98,20 @@ contract DirectPaymentsFactory is AccessControlUpgradeable, UUPSUpgradeable { string memory _projectId, string memory _ipfs, DirectPaymentsPool.PoolSettings memory _settings, - DirectPaymentsPool.SafetyLimits memory _limits + DirectPaymentsPool.SafetyLimits memory _limits, + uint32 _managerFeeBps ) external onlyProjectOwnerOrNon(_projectId) returns (DirectPaymentsPool pool) { - return _createPool(_projectId, _ipfs, _settings, _limits, true); + return _createPool(_projectId, _ipfs, _settings, _limits, _managerFeeBps, true); } function createPool( string memory _projectId, string memory _ipfs, DirectPaymentsPool.PoolSettings memory _settings, - DirectPaymentsPool.SafetyLimits memory _limits + DirectPaymentsPool.SafetyLimits memory _limits, + uint32 _managerFeeBps ) external onlyProjectOwnerOrNon(_projectId) returns (DirectPaymentsPool pool) { - return _createPool(_projectId, _ipfs, _settings, _limits, false); + return _createPool(_projectId, _ipfs, _settings, _limits, _managerFeeBps, false); } function _createPool( @@ -117,12 +119,16 @@ contract DirectPaymentsFactory is AccessControlUpgradeable, UUPSUpgradeable { string memory _ipfs, DirectPaymentsPool.PoolSettings memory _settings, DirectPaymentsPool.SafetyLimits memory _limits, + uint32 _managerFeeBps, bool useBeacon ) internal returns (DirectPaymentsPool pool) { //TODO: add check if msg.sender is whitelisted _settings.nftType = nextNftType; - bytes memory initCall = abi.encodeCall(DirectPaymentsPool.initialize, (nft, _settings, _limits, this)); + bytes memory initCall = abi.encodeCall( + DirectPaymentsPool.initialize, + (nft, _settings, _limits, _managerFeeBps, this) + ); if (useBeacon) { pool = DirectPaymentsPool(address(new BeaconProxy(address(impl), initCall))); diff --git a/packages/contracts/contracts/DirectPayments/DirectPaymentsPool.sol b/packages/contracts/contracts/DirectPayments/DirectPaymentsPool.sol index 4599cbc7..68802241 100644 --- a/packages/contracts/contracts/DirectPayments/DirectPaymentsPool.sol +++ b/packages/contracts/contracts/DirectPayments/DirectPaymentsPool.sol @@ -86,7 +86,6 @@ contract DirectPaymentsPool is IIdentityV2 uniquenessValidator; IERC20Upgradeable rewardToken; bool allowRewardOverride; - uint32 managerFeeBps; } struct SafetyLimits { @@ -113,6 +112,8 @@ contract DirectPaymentsPool is LimitsData public globalLimits; DirectPaymentsFactory public registry; + uint32 public managerFeeBps; + /// @custom:oz-upgrades-unsafe-allow constructor constructor(ISuperfluid _host, IV3SwapRouter _swapRouter) GoodCollectiveSuperApp(_host, _swapRouter) {} @@ -127,7 +128,7 @@ contract DirectPaymentsPool is } function getManagerFee() public view override returns (address feeRecipient, uint32 feeBps) { - return (settings.manager, settings.managerFeeBps); + return (settings.manager, managerFeeBps); } /** @@ -140,12 +141,14 @@ contract DirectPaymentsPool is ProvableNFT _nft, PoolSettings memory _settings, SafetyLimits memory _limits, + uint32 _managerFeeBps, DirectPaymentsFactory _registry ) external initializer { registry = _registry; settings = _settings; limits = _limits; nft = _nft; + managerFeeBps = _managerFeeBps; _setupRole(DEFAULT_ADMIN_ROLE, msg.sender); // when using factory this gives factory role which then set role to the real msg.sender _setupRole(MANAGER_ROLE, _settings.manager); _setupRole(MINTER_ROLE, _settings.manager); @@ -380,7 +383,8 @@ contract DirectPaymentsPool is * @dev Sets the settings for the pool. * @param _settings The new pool settings. */ - function setPoolSettings(PoolSettings memory _settings) public onlyRole(MANAGER_ROLE) { + function setPoolSettings(PoolSettings memory _settings, uint32 _managerFeeBps) public onlyRole(MANAGER_ROLE) { + managerFeeBps = _managerFeeBps; if (_settings.nftType != settings.nftType) revert NFTTYPE_CHANGED(); if (_settings.manager == address(0)) revert EMPTY_MANAGER(); diff --git a/packages/contracts/contracts/GoodCollective/GoodCollectiveSuperApp.sol b/packages/contracts/contracts/GoodCollective/GoodCollectiveSuperApp.sol index bcd515d4..ec2f46e6 100644 --- a/packages/contracts/contracts/GoodCollective/GoodCollectiveSuperApp.sol +++ b/packages/contracts/contracts/GoodCollective/GoodCollectiveSuperApp.sol @@ -68,7 +68,7 @@ abstract contract GoodCollectiveSuperApp is SuperAppBaseFlow { IGoodCollectiveSuperApp.Stats public stats; - uint256[48] private _reserved; + uint256[45] private _reserved; /// @custom:oz-upgrades-unsafe-allow constructor constructor(ISuperfluid _host, IV3SwapRouter _swapRouter) SuperAppBaseFlow(_host) { diff --git a/packages/contracts/contracts/GoodCollective/IGoodCollectiveSuperApp.sol b/packages/contracts/contracts/GoodCollective/IGoodCollectiveSuperApp.sol index 6f3fc5d1..b4211c66 100644 --- a/packages/contracts/contracts/GoodCollective/IGoodCollectiveSuperApp.sol +++ b/packages/contracts/contracts/GoodCollective/IGoodCollectiveSuperApp.sol @@ -17,6 +17,7 @@ interface IGoodCollectiveSuperApp { address lastManagerFeeRecipient; uint256 protocolFees; uint256 managerFees; + // adding fields MUST update GoodCollectiveSuperApp storage layout } function getAdminFee() external view returns (address admin, uint32 feeBps); diff --git a/packages/contracts/contracts/Interfaces.sol b/packages/contracts/contracts/Interfaces.sol index c01d1c3b..9df9f664 100644 --- a/packages/contracts/contracts/Interfaces.sol +++ b/packages/contracts/contracts/Interfaces.sol @@ -22,5 +22,4 @@ struct PoolSettings { IMembersValidator membersValidator; IIdentityV2 uniquenessValidator; IERC20Upgradeable rewardToken; - uint32 managerFeeBps; } diff --git a/packages/contracts/contracts/UBI/UBIPool.sol b/packages/contracts/contracts/UBI/UBIPool.sol index 50db4416..78cdb86b 100644 --- a/packages/contracts/contracts/UBI/UBIPool.sol +++ b/packages/contracts/contracts/UBI/UBIPool.sol @@ -57,10 +57,6 @@ contract UBIPool is AccessControlUpgradeable, GoodCollectiveSuperApp, UUPSUpgrad // max number of members in a pool uint32 maxMembers; bool onlyMembers; - // max number of members that can claim in a day maxPeriodClaimers <= maxMembers - uint32 maxPeriodClaimers; - // min daily claim amount, daily amount will be 0 if = status.currentDay + ubiSettings.claimPeriodDays) { @@ -155,7 +163,7 @@ contract UBIPool is AccessControlUpgradeable, GoodCollectiveSuperApp, UUPSUpgrad uint256 prevPeriodClaimers = status.periodClaimers; status.dailyUbi = nextDailyUbi; - if (status.dailyUbi <= ubiSettings.minClaimAmount) status.dailyUbi = 0; + if (status.dailyUbi <= extendedSettings.minClaimAmount) status.dailyUbi = 0; emit UBICalculated( currentDay, @@ -194,7 +202,7 @@ contract UBIPool is AccessControlUpgradeable, GoodCollectiveSuperApp, UUPSUpgrad nextPeriodPool / max((status.periodClaimers * 10500) / 10000, ubiSettings.minActiveUsers) ); - if (nextDailyUbi < ubiSettings.minClaimAmount) nextDailyUbi = 0; + if (nextDailyUbi < extendedSettings.minClaimAmount) nextDailyUbi = 0; } /** @@ -241,7 +249,7 @@ contract UBIPool is AccessControlUpgradeable, GoodCollectiveSuperApp, UUPSUpgrad if ((ubiSettings.maxMembers > 0 || ubiSettings.onlyMembers) && hasRole(MEMBER_ROLE, claimer) == false) revert NOT_MEMBER(claimer); - if (ubiSettings.maxPeriodClaimers > 0 && status.periodClaimers >= ubiSettings.maxPeriodClaimers) + if (extendedSettings.maxPeriodClaimers > 0 && status.periodClaimers >= extendedSettings.maxPeriodClaimers) revert MAX_PERIOD_CLAIMERS_REACHED(status.periodClaimers); // calculats the formula up today ie on day 0 there are no active users, on day 1 any user @@ -314,9 +322,13 @@ contract UBIPool is AccessControlUpgradeable, GoodCollectiveSuperApp, UUPSUpgrad * @dev Sets the safety limits for the pool. * @param _ubiSettings The new safety limits. */ - function setUBISettings(UBISettings memory _ubiSettings) public onlyRole(MANAGER_ROLE) { + function setUBISettings( + UBISettings memory _ubiSettings, + ExtendedSettings memory _extendedSettings + ) public onlyRole(MANAGER_ROLE) { _verifyUBISettings(_ubiSettings); ubiSettings = _ubiSettings; + extendedSettings = _extendedSettings; emit UBISettingsChanged(_ubiSettings); } @@ -371,7 +383,8 @@ contract UBIPool is AccessControlUpgradeable, GoodCollectiveSuperApp, UUPSUpgrad // current day has already been updated which means // that the dailyUbi has been updated if (status.currentDay == getCurrentDay() && status.dailyUbi > 0) { - if (ubiSettings.maxPeriodClaimers > 0 && status.periodClaimers >= ubiSettings.maxPeriodClaimers) return 0; + if (extendedSettings.maxPeriodClaimers > 0 && status.periodClaimers >= extendedSettings.maxPeriodClaimers) + return 0; return hasClaimed(_member) ? 0 : status.dailyUbi; } return estimateNextDailyUBI(); diff --git a/packages/contracts/contracts/UBI/UBIPoolFactory.sol b/packages/contracts/contracts/UBI/UBIPoolFactory.sol index 1930896b..f44910fb 100644 --- a/packages/contracts/contracts/UBI/UBIPoolFactory.sol +++ b/packages/contracts/contracts/UBI/UBIPoolFactory.sol @@ -87,18 +87,20 @@ contract UBIPoolFactory is AccessControlUpgradeable, UUPSUpgradeable { string memory _projectId, string memory _ipfs, PoolSettings memory _settings, - UBIPool.UBISettings memory _limits + UBIPool.UBISettings memory _limits, + UBIPool.ExtendedSettings memory _extendedSettings ) external onlyProjectOwnerOrNon(_projectId) returns (UBIPool pool) { - return _createPool(_projectId, _ipfs, _settings, _limits, true); + return _createPool(_projectId, _ipfs, _settings, _limits, _extendedSettings, true); } function createPool( string memory _projectId, string memory _ipfs, PoolSettings memory _settings, - UBIPool.UBISettings memory _limits + UBIPool.UBISettings memory _limits, + UBIPool.ExtendedSettings memory _extendedSettings ) external onlyProjectOwnerOrNon(_projectId) returns (UBIPool pool) { - return _createPool(_projectId, _ipfs, _settings, _limits, false); + return _createPool(_projectId, _ipfs, _settings, _limits, _extendedSettings, false); } function _createPool( @@ -106,11 +108,12 @@ contract UBIPoolFactory is AccessControlUpgradeable, UUPSUpgradeable { string memory _ipfs, PoolSettings memory _settings, UBIPool.UBISettings memory _limits, + UBIPool.ExtendedSettings memory _extendedSettings, bool useBeacon ) internal returns (UBIPool pool) { //TODO: add check if msg.sender is whitelisted - bytes memory initCall = abi.encodeCall(UBIPool.initialize, (_settings, _limits, this)); + bytes memory initCall = abi.encodeCall(UBIPool.initialize, (_settings, _limits, _extendedSettings, this)); if (useBeacon) { pool = UBIPool(address(new BeaconProxy(address(impl), initCall))); diff --git a/packages/contracts/releases/deployment.json b/packages/contracts/releases/deployment.json index 420cb34c..c684aa7a 100644 --- a/packages/contracts/releases/deployment.json +++ b/packages/contracts/releases/deployment.json @@ -6952,6 +6952,11 @@ "internalType": "struct DirectPaymentsPool.SafetyLimits", "name": "_limits", "type": "tuple" + }, + { + "internalType": "uint32", + "name": "_managerFeeBps", + "type": "uint32" } ], "name": "createBeaconPool", @@ -7045,6 +7050,11 @@ "internalType": "struct DirectPaymentsPool.SafetyLimits", "name": "_limits", "type": "tuple" + }, + { + "internalType": "uint32", + "name": "_managerFeeBps", + "type": "uint32" } ], "name": "createPool", @@ -7488,7 +7498,7 @@ ] }, "DirectPaymentsFactory_Implementation": { - "address": "0xff13c3E9E462a73433505E7230F72F4565b81296", + "address": "0xC64ED8278B73cC3A623ffFa50Ad5F622c3775a5D", "abi": [ { "inputs": [], @@ -7910,6 +7920,11 @@ "internalType": "struct DirectPaymentsPool.SafetyLimits", "name": "_limits", "type": "tuple" + }, + { + "internalType": "uint32", + "name": "_managerFeeBps", + "type": "uint32" } ], "name": "createBeaconPool", @@ -8003,6 +8018,11 @@ "internalType": "struct DirectPaymentsPool.SafetyLimits", "name": "_limits", "type": "tuple" + }, + { + "internalType": "uint32", + "name": "_managerFeeBps", + "type": "uint32" } ], "name": "createPool", @@ -8504,7 +8524,7 @@ ] }, "DirectPaymentsPool": { - "address": "0x3a522bc2746d2411663317365C66ef4b248135E7", + "address": "0x260d41018b110b2C720a9d55F6Ba1b0C17152D36", "abi": [ { "inputs": [ @@ -9546,6 +9566,24 @@ "stateMutability": "view", "type": "function" }, + { + "inputs": [], + "name": "getManagerFee", + "outputs": [ + { + "internalType": "address", + "name": "feeRecipient", + "type": "address" + }, + { + "internalType": "uint32", + "name": "feeBps", + "type": "uint32" + } + ], + "stateMutability": "view", + "type": "function" + }, { "inputs": [ { @@ -9579,6 +9617,16 @@ "name": "totalFees", "type": "uint256" }, + { + "internalType": "uint256", + "name": "protocolFees", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "managerFees", + "type": "uint256" + }, { "internalType": "int96", "name": "incomeFlowRate", @@ -9588,6 +9636,11 @@ "internalType": "int96", "name": "feeRate", "type": "int96" + }, + { + "internalType": "int96", + "name": "managerFeeRate", + "type": "int96" } ], "stateMutability": "view", @@ -9845,6 +9898,11 @@ "name": "_limits", "type": "tuple" }, + { + "internalType": "uint32", + "name": "_managerFeeBps", + "type": "uint32" + }, { "internalType": "contract DirectPaymentsFactory", "name": "_registry", @@ -9898,6 +9956,19 @@ "stateMutability": "view", "type": "function" }, + { + "inputs": [], + "name": "managerFeeBps", + "outputs": [ + { + "internalType": "uint32", + "name": "", + "type": "uint32" + } + ], + "stateMutability": "view", + "type": "function" + }, { "inputs": [ { @@ -10230,6 +10301,11 @@ "internalType": "struct DirectPaymentsPool.PoolSettings", "name": "_settings", "type": "tuple" + }, + { + "internalType": "uint32", + "name": "_managerFeeBps", + "type": "uint32" } ], "name": "setPoolSettings", @@ -10303,6 +10379,21 @@ "internalType": "int96", "name": "lastIncomeRate", "type": "int96" + }, + { + "internalType": "address", + "name": "lastManagerFeeRecipient", + "type": "address" + }, + { + "internalType": "uint256", + "name": "protocolFees", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "managerFees", + "type": "uint256" } ], "stateMutability": "view", @@ -10514,7 +10605,7 @@ ] }, "HelperLibrary": { - "address": "0xC25Da2F6d074230F0870056659f8cEC6eD6f44cb", + "address": "0x85608a0f804D0a9A72C022c812125AA25fA5B9bf", "abi": [ { "inputs": [ @@ -10544,6 +10635,21 @@ "internalType": "int96", "name": "lastIncomeRate", "type": "int96" + }, + { + "internalType": "address", + "name": "lastManagerFeeRecipient", + "type": "address" + }, + { + "internalType": "uint256", + "name": "protocolFees", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "managerFees", + "type": "uint256" } ], "internalType": "struct IGoodCollectiveSuperApp.Stats", @@ -10568,6 +10674,16 @@ "name": "totalFees", "type": "uint256" }, + { + "internalType": "uint256", + "name": "protocolFees", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "managerFees", + "type": "uint256" + }, { "internalType": "int96", "name": "incomeFlowRate", @@ -10577,6 +10693,11 @@ "internalType": "int96", "name": "feeRate", "type": "int96" + }, + { + "internalType": "int96", + "name": "managerFeeRate", + "type": "int96" } ], "stateMutability": "view", @@ -13137,7 +13258,7 @@ ] }, "UBIPool": { - "address": "0x5104a0843F45856b1529Faac34d2003990F87286", + "address": "0x4a67793B2854E791b32647d9815B3700c797F139", "abi": [ { "inputs": [ @@ -13199,7 +13320,18 @@ }, { "inputs": [], - "name": "MAX_CLAIMERS_REACHED", + "name": "MAX_MEMBERS_REACHED", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "claimers", + "type": "uint256" + } + ], + "name": "MAX_PERIOD_CLAIMERS_REACHED", "type": "error" }, { @@ -13213,6 +13345,17 @@ "name": "MIN_FLOWRATE", "type": "error" }, + { + "inputs": [ + { + "internalType": "address", + "name": "manager", + "type": "address" + } + ], + "name": "NOT_MANAGER", + "type": "error" + }, { "inputs": [ { @@ -13348,7 +13491,7 @@ } ], "indexed": false, - "internalType": "struct UBIPool.PoolSettings", + "internalType": "struct PoolSettings", "name": "settings", "type": "tuple" } @@ -13599,7 +13742,7 @@ }, { "internalType": "uint32", - "name": "maxClaimers", + "name": "maxMembers", "type": "uint32" }, { @@ -14058,7 +14201,7 @@ }, { "inputs": [], - "name": "estimateNextDailyUBI", + "name": "distributionFormula", "outputs": [ { "internalType": "uint256", @@ -14066,6 +14209,42 @@ "type": "uint256" } ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "estimateNextDailyUBI", + "outputs": [ + { + "internalType": "uint256", + "name": "nextDailyUbi", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "extendedSettings", + "outputs": [ + { + "internalType": "uint32", + "name": "maxPeriodClaimers", + "type": "uint32" + }, + { + "internalType": "uint256", + "name": "minClaimAmount", + "type": "uint256" + }, + { + "internalType": "uint32", + "name": "managerFeeBps", + "type": "uint32" + } + ], "stateMutability": "view", "type": "function" }, @@ -14082,6 +14261,24 @@ "stateMutability": "view", "type": "function" }, + { + "inputs": [], + "name": "getManagerFee", + "outputs": [ + { + "internalType": "address", + "name": "feeRecipient", + "type": "address" + }, + { + "internalType": "uint32", + "name": "feeBps", + "type": "uint32" + } + ], + "stateMutability": "view", + "type": "function" + }, { "inputs": [ { @@ -14115,6 +14312,16 @@ "name": "totalFees", "type": "uint256" }, + { + "internalType": "uint256", + "name": "protocolFees", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "managerFees", + "type": "uint256" + }, { "internalType": "int96", "name": "incomeFlowRate", @@ -14124,6 +14331,11 @@ "internalType": "int96", "name": "feeRate", "type": "int96" + }, + { + "internalType": "int96", + "name": "managerFeeRate", + "type": "int96" } ], "stateMutability": "view", @@ -14316,7 +14528,7 @@ "type": "address" } ], - "internalType": "struct UBIPool.PoolSettings", + "internalType": "struct PoolSettings", "name": "_settings", "type": "tuple" }, @@ -14349,7 +14561,7 @@ }, { "internalType": "uint32", - "name": "maxClaimers", + "name": "maxMembers", "type": "uint32" }, { @@ -14362,6 +14574,28 @@ "name": "_ubiSettings", "type": "tuple" }, + { + "components": [ + { + "internalType": "uint32", + "name": "maxPeriodClaimers", + "type": "uint32" + }, + { + "internalType": "uint256", + "name": "minClaimAmount", + "type": "uint256" + }, + { + "internalType": "uint32", + "name": "managerFeeBps", + "type": "uint32" + } + ], + "internalType": "struct UBIPool.ExtendedSettings", + "name": "_extendedSettings", + "type": "tuple" + }, { "internalType": "contract UBIPoolFactory", "name": "_registry", @@ -14534,7 +14768,7 @@ "type": "address" } ], - "internalType": "struct UBIPool.PoolSettings", + "internalType": "struct PoolSettings", "name": "_settings", "type": "tuple" } @@ -14575,7 +14809,7 @@ }, { "internalType": "uint32", - "name": "maxClaimers", + "name": "maxMembers", "type": "uint32" }, { @@ -14587,6 +14821,28 @@ "internalType": "struct UBIPool.UBISettings", "name": "_ubiSettings", "type": "tuple" + }, + { + "components": [ + { + "internalType": "uint32", + "name": "maxPeriodClaimers", + "type": "uint32" + }, + { + "internalType": "uint256", + "name": "minClaimAmount", + "type": "uint256" + }, + { + "internalType": "uint32", + "name": "managerFeeBps", + "type": "uint32" + } + ], + "internalType": "struct UBIPool.ExtendedSettings", + "name": "_extendedSettings", + "type": "tuple" } ], "name": "setUBISettings", @@ -14650,6 +14906,21 @@ "internalType": "int96", "name": "lastIncomeRate", "type": "int96" + }, + { + "internalType": "address", + "name": "lastManagerFeeRecipient", + "type": "address" + }, + { + "internalType": "uint256", + "name": "protocolFees", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "managerFees", + "type": "uint256" } ], "stateMutability": "view", @@ -14696,7 +14967,7 @@ }, { "internalType": "uint32", - "name": "claimersCount", + "name": "membersCount", "type": "uint32" } ], @@ -14893,7 +15164,7 @@ }, { "internalType": "uint32", - "name": "maxClaimers", + "name": "maxMembers", "type": "uint32" }, { @@ -15075,7 +15346,7 @@ } ], "indexed": false, - "internalType": "struct UBIPool.PoolSettings", + "internalType": "struct PoolSettings", "name": "poolSettings", "type": "tuple" }, @@ -15108,7 +15379,7 @@ }, { "internalType": "uint32", - "name": "maxClaimers", + "name": "maxMembers", "type": "uint32" }, { @@ -15331,7 +15602,7 @@ "type": "address" } ], - "internalType": "struct UBIPool.PoolSettings", + "internalType": "struct PoolSettings", "name": "_settings", "type": "tuple" }, @@ -15364,7 +15635,7 @@ }, { "internalType": "uint32", - "name": "maxClaimers", + "name": "maxMembers", "type": "uint32" }, { @@ -15376,6 +15647,28 @@ "internalType": "struct UBIPool.UBISettings", "name": "_limits", "type": "tuple" + }, + { + "components": [ + { + "internalType": "uint32", + "name": "maxPeriodClaimers", + "type": "uint32" + }, + { + "internalType": "uint256", + "name": "minClaimAmount", + "type": "uint256" + }, + { + "internalType": "uint32", + "name": "managerFeeBps", + "type": "uint32" + } + ], + "internalType": "struct UBIPool.ExtendedSettings", + "name": "_extendedSettings", + "type": "tuple" } ], "name": "createManagedPool", @@ -15424,7 +15717,7 @@ "type": "address" } ], - "internalType": "struct UBIPool.PoolSettings", + "internalType": "struct PoolSettings", "name": "_settings", "type": "tuple" }, @@ -15457,7 +15750,7 @@ }, { "internalType": "uint32", - "name": "maxClaimers", + "name": "maxMembers", "type": "uint32" }, { @@ -15469,6 +15762,28 @@ "internalType": "struct UBIPool.UBISettings", "name": "_limits", "type": "tuple" + }, + { + "components": [ + { + "internalType": "uint32", + "name": "maxPeriodClaimers", + "type": "uint32" + }, + { + "internalType": "uint256", + "name": "minClaimAmount", + "type": "uint256" + }, + { + "internalType": "uint32", + "name": "managerFeeBps", + "type": "uint32" + } + ], + "internalType": "struct UBIPool.ExtendedSettings", + "name": "_extendedSettings", + "type": "tuple" } ], "name": "createPool", @@ -15900,7 +16215,7 @@ ] }, "UBIPoolFactory_Implementation": { - "address": "0x1fd21Fe7e0ad98e311b507c71B196bAc3939B404", + "address": "0x87a71a7dCF13161B18D871253feF3D1d1f7b2881", "abi": [ { "inputs": [], @@ -16002,7 +16317,7 @@ } ], "indexed": false, - "internalType": "struct UBIPool.PoolSettings", + "internalType": "struct PoolSettings", "name": "poolSettings", "type": "tuple" }, @@ -16035,7 +16350,7 @@ }, { "internalType": "uint32", - "name": "maxClaimers", + "name": "maxMembers", "type": "uint32" }, { @@ -16271,7 +16586,7 @@ "type": "address" } ], - "internalType": "struct UBIPool.PoolSettings", + "internalType": "struct PoolSettings", "name": "_settings", "type": "tuple" }, @@ -16304,7 +16619,7 @@ }, { "internalType": "uint32", - "name": "maxClaimers", + "name": "maxMembers", "type": "uint32" }, { @@ -16316,6 +16631,28 @@ "internalType": "struct UBIPool.UBISettings", "name": "_limits", "type": "tuple" + }, + { + "components": [ + { + "internalType": "uint32", + "name": "maxPeriodClaimers", + "type": "uint32" + }, + { + "internalType": "uint256", + "name": "minClaimAmount", + "type": "uint256" + }, + { + "internalType": "uint32", + "name": "managerFeeBps", + "type": "uint32" + } + ], + "internalType": "struct UBIPool.ExtendedSettings", + "name": "_extendedSettings", + "type": "tuple" } ], "name": "createManagedPool", @@ -16364,7 +16701,7 @@ "type": "address" } ], - "internalType": "struct UBIPool.PoolSettings", + "internalType": "struct PoolSettings", "name": "_settings", "type": "tuple" }, @@ -16397,7 +16734,7 @@ }, { "internalType": "uint32", - "name": "maxClaimers", + "name": "maxMembers", "type": "uint32" }, { @@ -16409,6 +16746,28 @@ "internalType": "struct UBIPool.UBISettings", "name": "_limits", "type": "tuple" + }, + { + "components": [ + { + "internalType": "uint32", + "name": "maxPeriodClaimers", + "type": "uint32" + }, + { + "internalType": "uint256", + "name": "minClaimAmount", + "type": "uint256" + }, + { + "internalType": "uint32", + "name": "managerFeeBps", + "type": "uint32" + } + ], + "internalType": "struct UBIPool.ExtendedSettings", + "name": "_extendedSettings", + "type": "tuple" } ], "name": "createPool", @@ -16904,7 +17263,7 @@ "chainId": "42220", "contracts": { "DirectPaymentsFactory": { - "address": "0xD5ac451B0c50B9476107823Af206eD814a2e2580", + "address": "0x998abeb3E57409262aE5b751f60747921B33613E", "abi": [ { "anonymous": false, @@ -17334,6 +17693,11 @@ "internalType": "struct DirectPaymentsPool.SafetyLimits", "name": "_limits", "type": "tuple" + }, + { + "internalType": "uint32", + "name": "_managerFeeBps", + "type": "uint32" } ], "name": "createBeaconPool", @@ -17427,6 +17791,11 @@ "internalType": "struct DirectPaymentsPool.SafetyLimits", "name": "_limits", "type": "tuple" + }, + { + "internalType": "uint32", + "name": "_managerFeeBps", + "type": "uint32" } ], "name": "createPool", @@ -17870,7 +18239,7 @@ ] }, "DirectPaymentsFactory_Implementation": { - "address": "0xCace1b78160AE76398F486c8a18044da0d66d86D", + "address": "0x95401dc811bb5740090279Ba06cfA8fcF6113778", "abi": [ { "inputs": [], @@ -18292,6 +18661,11 @@ "internalType": "struct DirectPaymentsPool.SafetyLimits", "name": "_limits", "type": "tuple" + }, + { + "internalType": "uint32", + "name": "_managerFeeBps", + "type": "uint32" } ], "name": "createBeaconPool", @@ -18385,6 +18759,11 @@ "internalType": "struct DirectPaymentsPool.SafetyLimits", "name": "_limits", "type": "tuple" + }, + { + "internalType": "uint32", + "name": "_managerFeeBps", + "type": "uint32" } ], "name": "createPool", @@ -18812,7 +19191,7 @@ ] }, "DirectPaymentsFactory_Proxy": { - "address": "0xD5ac451B0c50B9476107823Af206eD814a2e2580", + "address": "0x998abeb3E57409262aE5b751f60747921B33613E", "abi": [ { "inputs": [ @@ -18886,7 +19265,7 @@ ] }, "DirectPaymentsPool": { - "address": "0x5067457698Fd6Fa1C6964e416b3f42713513B3dD", + "address": "0x1613beB3B2C4f22Ee086B2b38C1476A3cE7f78E8", "abi": [ { "inputs": [ @@ -19928,6 +20307,24 @@ "stateMutability": "view", "type": "function" }, + { + "inputs": [], + "name": "getManagerFee", + "outputs": [ + { + "internalType": "address", + "name": "feeRecipient", + "type": "address" + }, + { + "internalType": "uint32", + "name": "feeBps", + "type": "uint32" + } + ], + "stateMutability": "view", + "type": "function" + }, { "inputs": [ { @@ -19961,6 +20358,16 @@ "name": "totalFees", "type": "uint256" }, + { + "internalType": "uint256", + "name": "protocolFees", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "managerFees", + "type": "uint256" + }, { "internalType": "int96", "name": "incomeFlowRate", @@ -19970,6 +20377,11 @@ "internalType": "int96", "name": "feeRate", "type": "int96" + }, + { + "internalType": "int96", + "name": "managerFeeRate", + "type": "int96" } ], "stateMutability": "view", @@ -20227,6 +20639,11 @@ "name": "_limits", "type": "tuple" }, + { + "internalType": "uint32", + "name": "_managerFeeBps", + "type": "uint32" + }, { "internalType": "contract DirectPaymentsFactory", "name": "_registry", @@ -20280,6 +20697,19 @@ "stateMutability": "view", "type": "function" }, + { + "inputs": [], + "name": "managerFeeBps", + "outputs": [ + { + "internalType": "uint32", + "name": "", + "type": "uint32" + } + ], + "stateMutability": "view", + "type": "function" + }, { "inputs": [ { @@ -20612,6 +21042,11 @@ "internalType": "struct DirectPaymentsPool.PoolSettings", "name": "_settings", "type": "tuple" + }, + { + "internalType": "uint32", + "name": "_managerFeeBps", + "type": "uint32" } ], "name": "setPoolSettings", @@ -20685,6 +21120,21 @@ "internalType": "int96", "name": "lastIncomeRate", "type": "int96" + }, + { + "internalType": "address", + "name": "lastManagerFeeRecipient", + "type": "address" + }, + { + "internalType": "uint256", + "name": "protocolFees", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "managerFees", + "type": "uint256" } ], "stateMutability": "view", @@ -20896,7 +21346,7 @@ ] }, "GoodDollar": { - "address": "0x86A2EE8FAf9A840F7a2c64CA3d51209F9A02081D", + "address": "0xc5a5C42992dECbae36851359345FE25997F5C42d", "abi": [ "constructor(address _host)", "error SF_TOKEN_AGREEMENT_ALREADY_EXISTS()", @@ -21043,7 +21493,7 @@ ] }, "HelperLibrary": { - "address": "0xe8D2A1E88c91DCd5433208d4152Cc4F399a7e91d", + "address": "0xa82fF9aFd8f496c3d6ac40E2a0F282E47488CFc9", "abi": [ { "inputs": [ @@ -21073,6 +21523,21 @@ "internalType": "int96", "name": "lastIncomeRate", "type": "int96" + }, + { + "internalType": "address", + "name": "lastManagerFeeRecipient", + "type": "address" + }, + { + "internalType": "uint256", + "name": "protocolFees", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "managerFees", + "type": "uint256" } ], "internalType": "struct IGoodCollectiveSuperApp.Stats", @@ -21097,6 +21562,16 @@ "name": "totalFees", "type": "uint256" }, + { + "internalType": "uint256", + "name": "protocolFees", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "managerFees", + "type": "uint256" + }, { "internalType": "int96", "name": "incomeFlowRate", @@ -21106,6 +21581,11 @@ "internalType": "int96", "name": "feeRate", "type": "int96" + }, + { + "internalType": "int96", + "name": "managerFeeRate", + "type": "int96" } ], "stateMutability": "view", @@ -21206,7 +21686,7 @@ ] }, "ProvableNFT": { - "address": "0x4b6aB5F819A515382B0dEB6935D793817bB4af28", + "address": "0xf5059a5D33d5853360D16C683c16e67980206f36", "abi": [ { "anonymous": false, @@ -22411,7 +22891,7 @@ ] }, "ProvableNFT_Implementation": { - "address": "0x18E317A7D70d8fBf8e6E893616b52390EbBdb629", + "address": "0x851356ae760d987E095750cCeb3bC6014560891C", "abi": [ { "inputs": [ @@ -23592,7 +24072,7 @@ ] }, "ProvableNFT_Proxy": { - "address": "0x4b6aB5F819A515382B0dEB6935D793817bB4af28", + "address": "0xf5059a5D33d5853360D16C683c16e67980206f36", "abi": [ { "inputs": [ @@ -23666,11 +24146,11 @@ ] }, "SuperFluidResolver": { - "address": "0x2A081C6Ed79a1fee20807013c9D43DDB7Ae308Df", + "address": "0x02330b5Be8EBD0D4d354813a7BB535140A77C881", "abi": [] }, "SwapRouterMock": { - "address": "0xAA292E8611aDF267e563f334Ee42320aC96D0463", + "address": "0xc3e53F4d16Ae77Db1c982e75a937B9f60FE63690", "abi": [ { "inputs": [ @@ -23783,7 +24263,7 @@ ] }, "UBIPool": { - "address": "0xc0F115A19107322cFBf1cDBC7ea011C19EbDB4F8", + "address": "0x4826533B4897376654Bb4d4AD88B7faFD0C98528", "abi": [ { "inputs": [ @@ -23845,7 +24325,18 @@ }, { "inputs": [], - "name": "MAX_CLAIMERS_REACHED", + "name": "MAX_MEMBERS_REACHED", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "claimers", + "type": "uint256" + } + ], + "name": "MAX_PERIOD_CLAIMERS_REACHED", "type": "error" }, { @@ -24005,7 +24496,7 @@ } ], "indexed": false, - "internalType": "struct UBIPool.PoolSettings", + "internalType": "struct PoolSettings", "name": "settings", "type": "tuple" } @@ -24256,7 +24747,7 @@ }, { "internalType": "uint32", - "name": "maxClaimers", + "name": "maxMembers", "type": "uint32" }, { @@ -24715,7 +25206,7 @@ }, { "inputs": [], - "name": "estimateNextDailyUBI", + "name": "distributionFormula", "outputs": [ { "internalType": "uint256", @@ -24723,6 +25214,19 @@ "type": "uint256" } ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "estimateNextDailyUBI", + "outputs": [ + { + "internalType": "uint256", + "name": "nextDailyUbi", + "type": "uint256" + } + ], "stateMutability": "view", "type": "function" }, @@ -24739,6 +25243,24 @@ "stateMutability": "view", "type": "function" }, + { + "inputs": [], + "name": "getManagerFee", + "outputs": [ + { + "internalType": "address", + "name": "feeRecipient", + "type": "address" + }, + { + "internalType": "uint32", + "name": "feeBps", + "type": "uint32" + } + ], + "stateMutability": "view", + "type": "function" + }, { "inputs": [ { @@ -24772,6 +25294,16 @@ "name": "totalFees", "type": "uint256" }, + { + "internalType": "uint256", + "name": "protocolFees", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "managerFees", + "type": "uint256" + }, { "internalType": "int96", "name": "incomeFlowRate", @@ -24781,6 +25313,11 @@ "internalType": "int96", "name": "feeRate", "type": "int96" + }, + { + "internalType": "int96", + "name": "managerFeeRate", + "type": "int96" } ], "stateMutability": "view", @@ -24973,7 +25510,7 @@ "type": "address" } ], - "internalType": "struct UBIPool.PoolSettings", + "internalType": "struct PoolSettings", "name": "_settings", "type": "tuple" }, @@ -25006,7 +25543,7 @@ }, { "internalType": "uint32", - "name": "maxClaimers", + "name": "maxMembers", "type": "uint32" }, { @@ -25019,6 +25556,28 @@ "name": "_ubiSettings", "type": "tuple" }, + { + "components": [ + { + "internalType": "uint32", + "name": "maxPeriodClaimers", + "type": "uint32" + }, + { + "internalType": "uint256", + "name": "minClaimAmount", + "type": "uint256" + }, + { + "internalType": "uint32", + "name": "managerFeeBps", + "type": "uint32" + } + ], + "internalType": "struct UBIPool.ExtendedSettings", + "name": "_extendedSettings", + "type": "tuple" + }, { "internalType": "contract UBIPoolFactory", "name": "_registry", @@ -25191,7 +25750,7 @@ "type": "address" } ], - "internalType": "struct UBIPool.PoolSettings", + "internalType": "struct PoolSettings", "name": "_settings", "type": "tuple" } @@ -25232,7 +25791,7 @@ }, { "internalType": "uint32", - "name": "maxClaimers", + "name": "maxMembers", "type": "uint32" }, { @@ -25244,6 +25803,28 @@ "internalType": "struct UBIPool.UBISettings", "name": "_ubiSettings", "type": "tuple" + }, + { + "components": [ + { + "internalType": "uint32", + "name": "maxPeriodClaimers", + "type": "uint32" + }, + { + "internalType": "uint256", + "name": "minClaimAmount", + "type": "uint256" + }, + { + "internalType": "uint32", + "name": "managerFeeBps", + "type": "uint32" + } + ], + "internalType": "struct UBIPool.ExtendedSettings", + "name": "_extendedSettings", + "type": "tuple" } ], "name": "setUBISettings", @@ -25307,6 +25888,21 @@ "internalType": "int96", "name": "lastIncomeRate", "type": "int96" + }, + { + "internalType": "address", + "name": "lastManagerFeeRecipient", + "type": "address" + }, + { + "internalType": "uint256", + "name": "protocolFees", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "managerFees", + "type": "uint256" } ], "stateMutability": "view", @@ -25353,7 +25949,7 @@ }, { "internalType": "uint32", - "name": "claimersCount", + "name": "membersCount", "type": "uint32" } ], @@ -25550,7 +26146,7 @@ }, { "internalType": "uint32", - "name": "maxClaimers", + "name": "maxMembers", "type": "uint32" }, { @@ -25609,7 +26205,7 @@ ] }, "UBIPoolFactory": { - "address": "0x34B40BA116d5Dec75548a9e9A8f15411461E8c70", + "address": "0x0E801D84Fa97b50751Dbf25036d067dCf18858bF", "abi": [ { "anonymous": false, @@ -25732,7 +26328,7 @@ } ], "indexed": false, - "internalType": "struct UBIPool.PoolSettings", + "internalType": "struct PoolSettings", "name": "poolSettings", "type": "tuple" }, @@ -25765,7 +26361,7 @@ }, { "internalType": "uint32", - "name": "maxClaimers", + "name": "maxMembers", "type": "uint32" }, { @@ -25988,7 +26584,7 @@ "type": "address" } ], - "internalType": "struct UBIPool.PoolSettings", + "internalType": "struct PoolSettings", "name": "_settings", "type": "tuple" }, @@ -26021,7 +26617,7 @@ }, { "internalType": "uint32", - "name": "maxClaimers", + "name": "maxMembers", "type": "uint32" }, { @@ -26033,6 +26629,28 @@ "internalType": "struct UBIPool.UBISettings", "name": "_limits", "type": "tuple" + }, + { + "components": [ + { + "internalType": "uint32", + "name": "maxPeriodClaimers", + "type": "uint32" + }, + { + "internalType": "uint256", + "name": "minClaimAmount", + "type": "uint256" + }, + { + "internalType": "uint32", + "name": "managerFeeBps", + "type": "uint32" + } + ], + "internalType": "struct UBIPool.ExtendedSettings", + "name": "_extendedSettings", + "type": "tuple" } ], "name": "createManagedPool", @@ -26081,7 +26699,7 @@ "type": "address" } ], - "internalType": "struct UBIPool.PoolSettings", + "internalType": "struct PoolSettings", "name": "_settings", "type": "tuple" }, @@ -26114,7 +26732,7 @@ }, { "internalType": "uint32", - "name": "maxClaimers", + "name": "maxMembers", "type": "uint32" }, { @@ -26126,6 +26744,28 @@ "internalType": "struct UBIPool.UBISettings", "name": "_limits", "type": "tuple" + }, + { + "components": [ + { + "internalType": "uint32", + "name": "maxPeriodClaimers", + "type": "uint32" + }, + { + "internalType": "uint256", + "name": "minClaimAmount", + "type": "uint256" + }, + { + "internalType": "uint32", + "name": "managerFeeBps", + "type": "uint32" + } + ], + "internalType": "struct UBIPool.ExtendedSettings", + "name": "_extendedSettings", + "type": "tuple" } ], "name": "createPool", @@ -26557,7 +27197,7 @@ ] }, "UBIPoolFactory_Implementation": { - "address": "0xc96304e3c037f81dA488ed9dEa1D8F2a48278a75", + "address": "0x99bbA657f2BbC93c02D617f8bA121cB8Fc104Acf", "abi": [ { "inputs": [], @@ -26659,7 +27299,7 @@ } ], "indexed": false, - "internalType": "struct UBIPool.PoolSettings", + "internalType": "struct PoolSettings", "name": "poolSettings", "type": "tuple" }, @@ -26692,7 +27332,7 @@ }, { "internalType": "uint32", - "name": "maxClaimers", + "name": "maxMembers", "type": "uint32" }, { @@ -26928,7 +27568,7 @@ "type": "address" } ], - "internalType": "struct UBIPool.PoolSettings", + "internalType": "struct PoolSettings", "name": "_settings", "type": "tuple" }, @@ -26961,7 +27601,7 @@ }, { "internalType": "uint32", - "name": "maxClaimers", + "name": "maxMembers", "type": "uint32" }, { @@ -26973,6 +27613,28 @@ "internalType": "struct UBIPool.UBISettings", "name": "_limits", "type": "tuple" + }, + { + "components": [ + { + "internalType": "uint32", + "name": "maxPeriodClaimers", + "type": "uint32" + }, + { + "internalType": "uint256", + "name": "minClaimAmount", + "type": "uint256" + }, + { + "internalType": "uint32", + "name": "managerFeeBps", + "type": "uint32" + } + ], + "internalType": "struct UBIPool.ExtendedSettings", + "name": "_extendedSettings", + "type": "tuple" } ], "name": "createManagedPool", @@ -27021,7 +27683,7 @@ "type": "address" } ], - "internalType": "struct UBIPool.PoolSettings", + "internalType": "struct PoolSettings", "name": "_settings", "type": "tuple" }, @@ -27054,7 +27716,7 @@ }, { "internalType": "uint32", - "name": "maxClaimers", + "name": "maxMembers", "type": "uint32" }, { @@ -27066,6 +27728,28 @@ "internalType": "struct UBIPool.UBISettings", "name": "_limits", "type": "tuple" + }, + { + "components": [ + { + "internalType": "uint32", + "name": "maxPeriodClaimers", + "type": "uint32" + }, + { + "internalType": "uint256", + "name": "minClaimAmount", + "type": "uint256" + }, + { + "internalType": "uint32", + "name": "managerFeeBps", + "type": "uint32" + } + ], + "internalType": "struct UBIPool.ExtendedSettings", + "name": "_extendedSettings", + "type": "tuple" } ], "name": "createPool", @@ -27481,7 +28165,7 @@ ] }, "UBIPoolFactory_Proxy": { - "address": "0x34B40BA116d5Dec75548a9e9A8f15411461E8c70", + "address": "0x0E801D84Fa97b50751Dbf25036d067dCf18858bF", "abi": [ { "inputs": [ @@ -27707,11 +28391,6 @@ "internalType": "bool", "name": "allowRewardOverride", "type": "bool" - }, - { - "internalType": "uint32", - "name": "managerFeeBps", - "type": "uint32" } ], "indexed": false, @@ -27969,11 +28648,6 @@ "internalType": "bool", "name": "allowRewardOverride", "type": "bool" - }, - { - "internalType": "uint32", - "name": "managerFeeBps", - "type": "uint32" } ], "internalType": "struct DirectPaymentsPool.PoolSettings", @@ -28001,6 +28675,11 @@ "internalType": "struct DirectPaymentsPool.SafetyLimits", "name": "_limits", "type": "tuple" + }, + { + "internalType": "uint32", + "name": "_managerFeeBps", + "type": "uint32" } ], "name": "createBeaconPool", @@ -28067,11 +28746,6 @@ "internalType": "bool", "name": "allowRewardOverride", "type": "bool" - }, - { - "internalType": "uint32", - "name": "managerFeeBps", - "type": "uint32" } ], "internalType": "struct DirectPaymentsPool.PoolSettings", @@ -28099,6 +28773,11 @@ "internalType": "struct DirectPaymentsPool.SafetyLimits", "name": "_limits", "type": "tuple" + }, + { + "internalType": "uint32", + "name": "_managerFeeBps", + "type": "uint32" } ], "name": "createPool", @@ -28542,7 +29221,7 @@ ] }, "DirectPaymentsFactory_Implementation": { - "address": "0xB25801A0E679c987ea49A1C201979081407139C6", + "address": "0x90AF745592c561f58C215D99698b4BC9bAc56b86", "abi": [ { "inputs": [], @@ -28667,11 +29346,6 @@ "internalType": "bool", "name": "allowRewardOverride", "type": "bool" - }, - { - "internalType": "uint32", - "name": "managerFeeBps", - "type": "uint32" } ], "indexed": false, @@ -28942,11 +29616,6 @@ "internalType": "bool", "name": "allowRewardOverride", "type": "bool" - }, - { - "internalType": "uint32", - "name": "managerFeeBps", - "type": "uint32" } ], "internalType": "struct DirectPaymentsPool.PoolSettings", @@ -28974,6 +29643,11 @@ "internalType": "struct DirectPaymentsPool.SafetyLimits", "name": "_limits", "type": "tuple" + }, + { + "internalType": "uint32", + "name": "_managerFeeBps", + "type": "uint32" } ], "name": "createBeaconPool", @@ -29040,11 +29714,6 @@ "internalType": "bool", "name": "allowRewardOverride", "type": "bool" - }, - { - "internalType": "uint32", - "name": "managerFeeBps", - "type": "uint32" } ], "internalType": "struct DirectPaymentsPool.PoolSettings", @@ -29072,6 +29741,11 @@ "internalType": "struct DirectPaymentsPool.SafetyLimits", "name": "_limits", "type": "tuple" + }, + { + "internalType": "uint32", + "name": "_managerFeeBps", + "type": "uint32" } ], "name": "createPool", @@ -29573,7 +30247,7 @@ ] }, "DirectPaymentsPool": { - "address": "0xF7f70A3CfCfE5884a617b9CA11E28Bc08eb88C7f", + "address": "0xB632F7Ba71A1695C79369B64F7f4cC761fA9bFd4", "abi": [ { "inputs": [ @@ -29916,11 +30590,6 @@ "internalType": "bool", "name": "allowRewardOverride", "type": "bool" - }, - { - "internalType": "uint32", - "name": "managerFeeBps", - "type": "uint32" } ], "indexed": false, @@ -30029,11 +30698,6 @@ "internalType": "bool", "name": "allowRewardOverride", "type": "bool" - }, - { - "internalType": "uint32", - "name": "managerFeeBps", - "type": "uint32" } ], "indexed": false, @@ -30929,11 +31593,6 @@ "internalType": "bool", "name": "allowRewardOverride", "type": "bool" - }, - { - "internalType": "uint32", - "name": "managerFeeBps", - "type": "uint32" } ], "internalType": "struct DirectPaymentsPool.PoolSettings", @@ -30962,6 +31621,11 @@ "name": "_limits", "type": "tuple" }, + { + "internalType": "uint32", + "name": "_managerFeeBps", + "type": "uint32" + }, { "internalType": "contract DirectPaymentsFactory", "name": "_registry", @@ -31015,6 +31679,19 @@ "stateMutability": "view", "type": "function" }, + { + "inputs": [], + "name": "managerFeeBps", + "outputs": [ + { + "internalType": "uint32", + "name": "", + "type": "uint32" + } + ], + "stateMutability": "view", + "type": "function" + }, { "inputs": [ { @@ -31342,16 +32019,16 @@ "internalType": "bool", "name": "allowRewardOverride", "type": "bool" - }, - { - "internalType": "uint32", - "name": "managerFeeBps", - "type": "uint32" } ], "internalType": "struct DirectPaymentsPool.PoolSettings", "name": "_settings", "type": "tuple" + }, + { + "internalType": "uint32", + "name": "_managerFeeBps", + "type": "uint32" } ], "name": "setPoolSettings", @@ -31392,11 +32069,6 @@ "internalType": "bool", "name": "allowRewardOverride", "type": "bool" - }, - { - "internalType": "uint32", - "name": "managerFeeBps", - "type": "uint32" } ], "stateMutability": "view", @@ -31656,7 +32328,7 @@ ] }, "HelperLibrary": { - "address": "0xA9E671fB33a0Bb36D50607ddd72E7e1519A2B02E", + "address": "0x218Cb1a3A23C8F20223701b19BEfcfE55909773C", "abi": [ { "inputs": [ @@ -34309,7 +34981,7 @@ ] }, "UBIPool": { - "address": "0x8142417D8200757BF29686511F7E0861f2b5c929", + "address": "0x0df27e5feA935870Ccd4B059927C93623eDEd12c", "abi": [ { "inputs": [ @@ -34539,11 +35211,6 @@ "internalType": "contract IERC20Upgradeable", "name": "rewardToken", "type": "address" - }, - { - "internalType": "uint32", - "name": "managerFeeBps", - "type": "uint32" } ], "indexed": false, @@ -34805,16 +35472,6 @@ "internalType": "bool", "name": "onlyMembers", "type": "bool" - }, - { - "internalType": "uint32", - "name": "maxPeriodClaimers", - "type": "uint32" - }, - { - "internalType": "uint256", - "name": "minClaimAmount", - "type": "uint256" } ], "indexed": false, @@ -35265,6 +35922,19 @@ "stateMutability": "view", "type": "function" }, + { + "inputs": [], + "name": "distributionFormula", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, { "inputs": [], "name": "estimateNextDailyUBI", @@ -35278,6 +35948,29 @@ "stateMutability": "view", "type": "function" }, + { + "inputs": [], + "name": "extendedSettings", + "outputs": [ + { + "internalType": "uint32", + "name": "maxPeriodClaimers", + "type": "uint32" + }, + { + "internalType": "uint256", + "name": "minClaimAmount", + "type": "uint256" + }, + { + "internalType": "uint32", + "name": "managerFeeBps", + "type": "uint32" + } + ], + "stateMutability": "view", + "type": "function" + }, { "inputs": [], "name": "getCurrentDay", @@ -35556,11 +36249,6 @@ "internalType": "contract IERC20Upgradeable", "name": "rewardToken", "type": "address" - }, - { - "internalType": "uint32", - "name": "managerFeeBps", - "type": "uint32" } ], "internalType": "struct PoolSettings", @@ -35603,7 +36291,14 @@ "internalType": "bool", "name": "onlyMembers", "type": "bool" - }, + } + ], + "internalType": "struct UBIPool.UBISettings", + "name": "_ubiSettings", + "type": "tuple" + }, + { + "components": [ { "internalType": "uint32", "name": "maxPeriodClaimers", @@ -35613,10 +36308,15 @@ "internalType": "uint256", "name": "minClaimAmount", "type": "uint256" + }, + { + "internalType": "uint32", + "name": "managerFeeBps", + "type": "uint32" } ], - "internalType": "struct UBIPool.UBISettings", - "name": "_ubiSettings", + "internalType": "struct UBIPool.ExtendedSettings", + "name": "_extendedSettings", "type": "tuple" }, { @@ -35789,11 +36489,6 @@ "internalType": "contract IERC20Upgradeable", "name": "rewardToken", "type": "address" - }, - { - "internalType": "uint32", - "name": "managerFeeBps", - "type": "uint32" } ], "internalType": "struct PoolSettings", @@ -35844,7 +36539,14 @@ "internalType": "bool", "name": "onlyMembers", "type": "bool" - }, + } + ], + "internalType": "struct UBIPool.UBISettings", + "name": "_ubiSettings", + "type": "tuple" + }, + { + "components": [ { "internalType": "uint32", "name": "maxPeriodClaimers", @@ -35854,10 +36556,15 @@ "internalType": "uint256", "name": "minClaimAmount", "type": "uint256" + }, + { + "internalType": "uint32", + "name": "managerFeeBps", + "type": "uint32" } ], - "internalType": "struct UBIPool.UBISettings", - "name": "_ubiSettings", + "internalType": "struct UBIPool.ExtendedSettings", + "name": "_extendedSettings", "type": "tuple" } ], @@ -35889,11 +36596,6 @@ "internalType": "contract IERC20Upgradeable", "name": "rewardToken", "type": "address" - }, - { - "internalType": "uint32", - "name": "managerFeeBps", - "type": "uint32" } ], "stateMutability": "view", @@ -36192,16 +36894,6 @@ "internalType": "bool", "name": "onlyMembers", "type": "bool" - }, - { - "internalType": "uint32", - "name": "maxPeriodClaimers", - "type": "uint32" - }, - { - "internalType": "uint256", - "name": "minClaimAmount", - "type": "uint256" } ], "stateMutability": "view", @@ -36374,11 +37066,6 @@ "internalType": "contract IERC20Upgradeable", "name": "rewardToken", "type": "address" - }, - { - "internalType": "uint32", - "name": "managerFeeBps", - "type": "uint32" } ], "indexed": false, @@ -36422,16 +37109,6 @@ "internalType": "bool", "name": "onlyMembers", "type": "bool" - }, - { - "internalType": "uint32", - "name": "maxPeriodClaimers", - "type": "uint32" - }, - { - "internalType": "uint256", - "name": "minClaimAmount", - "type": "uint256" } ], "indexed": false, @@ -36646,11 +37323,6 @@ "internalType": "contract IERC20Upgradeable", "name": "rewardToken", "type": "address" - }, - { - "internalType": "uint32", - "name": "managerFeeBps", - "type": "uint32" } ], "internalType": "struct PoolSettings", @@ -36693,7 +37365,14 @@ "internalType": "bool", "name": "onlyMembers", "type": "bool" - }, + } + ], + "internalType": "struct UBIPool.UBISettings", + "name": "_limits", + "type": "tuple" + }, + { + "components": [ { "internalType": "uint32", "name": "maxPeriodClaimers", @@ -36703,10 +37382,15 @@ "internalType": "uint256", "name": "minClaimAmount", "type": "uint256" + }, + { + "internalType": "uint32", + "name": "managerFeeBps", + "type": "uint32" } ], - "internalType": "struct UBIPool.UBISettings", - "name": "_limits", + "internalType": "struct UBIPool.ExtendedSettings", + "name": "_extendedSettings", "type": "tuple" } ], @@ -36754,11 +37438,6 @@ "internalType": "contract IERC20Upgradeable", "name": "rewardToken", "type": "address" - }, - { - "internalType": "uint32", - "name": "managerFeeBps", - "type": "uint32" } ], "internalType": "struct PoolSettings", @@ -36801,7 +37480,14 @@ "internalType": "bool", "name": "onlyMembers", "type": "bool" - }, + } + ], + "internalType": "struct UBIPool.UBISettings", + "name": "_limits", + "type": "tuple" + }, + { + "components": [ { "internalType": "uint32", "name": "maxPeriodClaimers", @@ -36811,10 +37497,15 @@ "internalType": "uint256", "name": "minClaimAmount", "type": "uint256" + }, + { + "internalType": "uint32", + "name": "managerFeeBps", + "type": "uint32" } ], - "internalType": "struct UBIPool.UBISettings", - "name": "_limits", + "internalType": "struct UBIPool.ExtendedSettings", + "name": "_extendedSettings", "type": "tuple" } ], @@ -37247,7 +37938,7 @@ ] }, "UBIPoolFactory_Implementation": { - "address": "0x05e9bE6B03A64c6A0fF48b5bBC1D36fa058A7f5e", + "address": "0xAfAdA9E5EeaABBc0e37b1bf84227582b2dCb091c", "abi": [ { "inputs": [], @@ -37346,11 +38037,6 @@ "internalType": "contract IERC20Upgradeable", "name": "rewardToken", "type": "address" - }, - { - "internalType": "uint32", - "name": "managerFeeBps", - "type": "uint32" } ], "indexed": false, @@ -37394,16 +38080,6 @@ "internalType": "bool", "name": "onlyMembers", "type": "bool" - }, - { - "internalType": "uint32", - "name": "maxPeriodClaimers", - "type": "uint32" - }, - { - "internalType": "uint256", - "name": "minClaimAmount", - "type": "uint256" } ], "indexed": false, @@ -37631,11 +38307,6 @@ "internalType": "contract IERC20Upgradeable", "name": "rewardToken", "type": "address" - }, - { - "internalType": "uint32", - "name": "managerFeeBps", - "type": "uint32" } ], "internalType": "struct PoolSettings", @@ -37678,7 +38349,14 @@ "internalType": "bool", "name": "onlyMembers", "type": "bool" - }, + } + ], + "internalType": "struct UBIPool.UBISettings", + "name": "_limits", + "type": "tuple" + }, + { + "components": [ { "internalType": "uint32", "name": "maxPeriodClaimers", @@ -37688,10 +38366,15 @@ "internalType": "uint256", "name": "minClaimAmount", "type": "uint256" + }, + { + "internalType": "uint32", + "name": "managerFeeBps", + "type": "uint32" } ], - "internalType": "struct UBIPool.UBISettings", - "name": "_limits", + "internalType": "struct UBIPool.ExtendedSettings", + "name": "_extendedSettings", "type": "tuple" } ], @@ -37739,11 +38422,6 @@ "internalType": "contract IERC20Upgradeable", "name": "rewardToken", "type": "address" - }, - { - "internalType": "uint32", - "name": "managerFeeBps", - "type": "uint32" } ], "internalType": "struct PoolSettings", @@ -37786,7 +38464,14 @@ "internalType": "bool", "name": "onlyMembers", "type": "bool" - }, + } + ], + "internalType": "struct UBIPool.UBISettings", + "name": "_limits", + "type": "tuple" + }, + { + "components": [ { "internalType": "uint32", "name": "maxPeriodClaimers", @@ -37796,10 +38481,15 @@ "internalType": "uint256", "name": "minClaimAmount", "type": "uint256" + }, + { + "internalType": "uint32", + "name": "managerFeeBps", + "type": "uint32" } ], - "internalType": "struct UBIPool.UBISettings", - "name": "_limits", + "internalType": "struct UBIPool.ExtendedSettings", + "name": "_extendedSettings", "type": "tuple" } ], @@ -44423,4 +45113,4 @@ } } ] -} +} \ No newline at end of file diff --git a/packages/contracts/test/DirectPayments/DirectPayments.claim.test.ts b/packages/contracts/test/DirectPayments/DirectPayments.claim.test.ts index ea252c4f..99a532ad 100644 --- a/packages/contracts/test/DirectPayments/DirectPayments.claim.test.ts +++ b/packages/contracts/test/DirectPayments/DirectPayments.claim.test.ts @@ -54,8 +54,7 @@ describe('DirectPaymentsPool Claim', () => { manager: signer.address, membersValidator: ethers.constants.AddressZero, rewardToken: gdframework.GoodDollar.address, - allowRewardOverride: false, - managerFeeBps: 0 + allowRewardOverride: false }; poolLimits = { @@ -96,13 +95,14 @@ describe('DirectPaymentsPool Claim', () => { const poolTx = await (await poolFactory.createPool("xx", "ipfs", { ...poolSettings, membersValidator: membersValidator.address }, poolLimits, + 0 )).wait() // console.log("created pool:", poolTx.events) const poolAddress = poolTx.events?.find(_ => _.event === "PoolCreated")?.args?.[0] - pool = Pool.attach(poolAddress) + pool = Pool.attach(poolAddress) as DirectPaymentsPool const tx = await nft.mintPermissioned(signers[0].address, nftSample, true, []).then((_) => _.wait()); - await gdframework.GoodDollar.mint(pool.address, ethers.constants.WeiPerEther.mul(100000)).then((_) => _.wait()); + await gdframework.GoodDollar.mint(pool.address, ethers.constants.WeiPerEther.mul(100000)).then((_: any) => _.wait()); nftSampleId = tx.events?.find((e) => e.event === 'Transfer')?.args?.tokenId; // return { pool, nft, membersValidator }; }; @@ -118,7 +118,7 @@ describe('DirectPaymentsPool Claim', () => { ]); membersValidator.mock['isMemberValid'].returns(false); - await pool.setPoolSettings({ ...poolSettings, membersValidator: membersValidator.address }); + await pool.setPoolSettings({ ...poolSettings, membersValidator: membersValidator.address }, 0); await expect(pool['claim(uint256)'](nftSampleId)).not.reverted; const contributer = nftSample.events[0].contributers[0]; const initialBalance = await gdframework.GoodDollar.balanceOf(contributer); diff --git a/packages/contracts/test/DirectPayments/DirectPayments.superapp.test.ts b/packages/contracts/test/DirectPayments/DirectPayments.superapp.test.ts index 02da9a65..28cc0606 100644 --- a/packages/contracts/test/DirectPayments/DirectPayments.superapp.test.ts +++ b/packages/contracts/test/DirectPayments/DirectPayments.superapp.test.ts @@ -64,7 +64,6 @@ describe('DirectPaymentsPool Superapp', () => { membersValidator: ethers.constants.AddressZero, rewardToken: gdframework.GoodDollar.address, allowRewardOverride: false, - managerFeeBps: 0 }; poolLimits = { @@ -83,7 +82,7 @@ describe('DirectPaymentsPool Superapp', () => { libraries: { HelperLibrary: helper.address }, }); - pool = (await upgrades.deployProxy(Pool, [nft.address, poolSettings, poolLimits, ethers.constants.AddressZero], { + pool = (await upgrades.deployProxy(Pool, [nft.address, poolSettings, poolLimits, 0, ethers.constants.AddressZero], { unsafeAllowLinkedLibraries: true, constructorArgs: [await gdframework.GoodDollar.getHost(), swaprouter.address], })) as DirectPaymentsPool; diff --git a/packages/contracts/test/DirectPayments/DirectPayments.superappfees.test.ts b/packages/contracts/test/DirectPayments/DirectPayments.superappfees.test.ts index 4e2a787b..d3d74460 100644 --- a/packages/contracts/test/DirectPayments/DirectPayments.superappfees.test.ts +++ b/packages/contracts/test/DirectPayments/DirectPayments.superappfees.test.ts @@ -64,7 +64,6 @@ describe('DirectPaymentsPool Superapp with Fees', () => { membersValidator: ethers.constants.AddressZero, rewardToken: gdframework.GoodDollar.address, allowRewardOverride: false, - managerFeeBps: 0 }; poolLimits = { @@ -95,9 +94,9 @@ describe('DirectPaymentsPool Superapp with Fees', () => { }); const fixture = async () => { - const tx = await factory.createPool('testfees', 'ipfs', poolSettings, poolLimits); + const tx = await factory.createPool('testfees', 'ipfs', poolSettings, poolLimits, 0); const poolAddr = (await tx.wait()).events?.find((_) => _.event === 'PoolCreated')?.args?.[0]; - pool = await ethers.getContractAt('DirectPaymentsPool', poolAddr); + pool = await ethers.getContractAt('DirectPaymentsPool', poolAddr) as DirectPaymentsPool; }; beforeEach(async function () { diff --git a/packages/contracts/test/DirectPayments/DirectPayments.superappmanagerfees.test.ts b/packages/contracts/test/DirectPayments/DirectPayments.superappmanagerfees.test.ts index b377f4af..71dc8b66 100644 --- a/packages/contracts/test/DirectPayments/DirectPayments.superappmanagerfees.test.ts +++ b/packages/contracts/test/DirectPayments/DirectPayments.superappmanagerfees.test.ts @@ -36,6 +36,7 @@ describe('Superapp with Manager Fees', () => { }, ], }; + const managerFeeBps = 200; before(async () => { const { frameworkDeployer } = await deployTestFramework(); @@ -64,9 +65,7 @@ describe('Superapp with Manager Fees', () => { membersValidator: ethers.constants.AddressZero, rewardToken: gdframework.GoodDollar.address, allowRewardOverride: false, - managerFeeBps: 200 }; - poolLimits = { maxMemberPerDay: 300, maxMemberPerMonth: 1000, @@ -95,9 +94,9 @@ describe('Superapp with Manager Fees', () => { }); const fixture = async () => { - const tx = await factory.createPool('testfees', 'ipfs', poolSettings, poolLimits); + const tx = await factory.createPool('testfees', 'ipfs', poolSettings, poolLimits, managerFeeBps); const poolAddr = (await tx.wait()).events?.find((_) => _.event === 'PoolCreated')?.args?.[0]; - pool = await ethers.getContractAt('DirectPaymentsPool', poolAddr); + pool = await ethers.getContractAt('DirectPaymentsPool', poolAddr) as DirectPaymentsPool; }; beforeEach(async function () { @@ -108,7 +107,7 @@ describe('Superapp with Manager Fees', () => { const expectedProtocolFee = (Number(baseFlowRate) * (await factory.feeBps())) / 10000; - const expectedManagerFee = (Number(baseFlowRate) * Number(poolSettings.managerFeeBps)) / 10000; + const expectedManagerFee = (Number(baseFlowRate) * Number(managerFeeBps)) / 10000; await gdframework.GoodDollar.mint(signer.address, ethers.constants.WeiPerEther); @@ -143,7 +142,7 @@ describe('Superapp with Manager Fees', () => { it('should decrease fee when stopped streaming', async () => { const expectedProtocolFee = (Number(baseFlowRate) * (await factory.feeBps())) / 10000; - const expectedManagerFee = (Number(baseFlowRate) * Number(poolSettings.managerFeeBps)) / 10000; + const expectedManagerFee = (Number(baseFlowRate) * Number(managerFeeBps)) / 10000; await gdframework.GoodDollar.mint(signer.address, ethers.constants.WeiPerEther.mul(10000)); const st = await sf.loadSuperToken(gdframework.GoodDollar.address); @@ -181,7 +180,7 @@ describe('Superapp with Manager Fees', () => { const expectedProtocolFee = (Number(baseFlowRate) * (await factory.feeBps())) / 10000; - const expectedManagerFee = (Number(baseFlowRate) * Number(poolSettings.managerFeeBps)) / 10000; + const expectedManagerFee = (Number(baseFlowRate) * Number(managerFeeBps)) / 10000; const updatedFlowRate = ethers.utils.parseEther("0.000001") @@ -227,7 +226,7 @@ describe('Superapp with Manager Fees', () => { }); expect(manaerAfterFeeFlow.flowRate).eq( updatedFlowRate - .mul(Number(poolSettings.managerFeeBps)) + .mul(Number(managerFeeBps)) .div(10000) ); @@ -245,7 +244,7 @@ describe('Superapp with Manager Fees', () => { const expectedUpdatedProtocolFee = (Number(updatedFlowRate) * (await factory.feeBps())) / 10000; - const expectedUpdatedManagerFee = (Number(updatedFlowRate) * Number(poolSettings.managerFeeBps)) / 10000; + const expectedUpdatedManagerFee = (Number(updatedFlowRate) * Number(managerFeeBps)) / 10000; expect(stats.totalFees).gte((expectedManagerFee + expectedProtocolFee) * 10 + (expectedUpdatedManagerFee + expectedUpdatedProtocolFee) * 10); expect(stats.protocolFees).gte(expectedProtocolFee * 10 + expectedUpdatedProtocolFee * 10) expect(stats.managerFees).gte(expectedManagerFee * 10 + expectedUpdatedManagerFee * 10) @@ -257,17 +256,17 @@ describe('Superapp with Manager Fees', () => { await gdframework.GoodDollar.mint(signer.address, ethers.constants.WeiPerEther); const st = gdframework.GoodDollar; await st.transferAndCall(pool.address, 10000000, '0x'); - expect(await gdframework.GoodDollar.balanceOf(pool.address)).eq(10000000 * (10000 - await factory.feeBps() - Number(poolSettings.managerFeeBps)) / 10000); + expect(await gdframework.GoodDollar.balanceOf(pool.address)).eq(10000000 * (10000 - await factory.feeBps() - Number(managerFeeBps)) / 10000); expect(await gdframework.GoodDollar.balanceOf(await factory.feeRecipient())).eq( (10000000 * (await factory.feeBps())) / 10000 ); expect(await gdframework.GoodDollar.balanceOf(poolSettings.manager)).eq( - (10000000 * Number(poolSettings.managerFeeBps)) / 10000 + (10000000 * Number(managerFeeBps)) / 10000 ); const stats = await pool.stats(); - expect(stats.netIncome).eq(10000000 * (10000 - await factory.feeBps() - Number(poolSettings.managerFeeBps)) / 10000); - expect(stats.totalFees).eq(((10000000 * (await factory.feeBps())) / 10000) + (10000000 * Number(poolSettings.managerFeeBps)) / 10000); - expect(stats.managerFees).eq((10000000 * Number(poolSettings.managerFeeBps)) / 10000); + expect(stats.netIncome).eq(10000000 * (10000 - await factory.feeBps() - Number(managerFeeBps)) / 10000); + expect(stats.totalFees).eq(((10000000 * (await factory.feeBps())) / 10000) + (10000000 * Number(managerFeeBps)) / 10000); + expect(stats.managerFees).eq((10000000 * Number(managerFeeBps)) / 10000); expect(stats.protocolFees).eq((10000000 * (await factory.feeBps())) / 10000); await mine(2, { interval: 5 }); @@ -285,17 +284,17 @@ describe('Superapp with Manager Fees', () => { const st = gdframework.GoodDollar; const transferAction = await st.approve(pool.address, 10000000); const supportAction = await pool.support(signer.address, 10000000, '0x'); - expect(await gdframework.GoodDollar.balanceOf(pool.address)).eq(10000000 * (10000 - await factory.feeBps() - Number(poolSettings.managerFeeBps)) / 10000); + expect(await gdframework.GoodDollar.balanceOf(pool.address)).eq(10000000 * (10000 - await factory.feeBps() - Number(managerFeeBps)) / 10000); expect(await gdframework.GoodDollar.balanceOf(await factory.feeRecipient())).eq( (10000000 * (await factory.feeBps())) / 10000 ); expect(await gdframework.GoodDollar.balanceOf(poolSettings.manager)).eq( - (10000000 * Number(poolSettings.managerFeeBps)) / 10000 + (10000000 * Number(managerFeeBps)) / 10000 ); const stats = await pool.stats(); - expect(stats.netIncome).eq(10000000 * (10000 - await factory.feeBps() - Number(poolSettings.managerFeeBps)) / 10000); - expect(stats.totalFees).eq(((10000000 * (await factory.feeBps())) / 10000) + (10000000 * Number(poolSettings.managerFeeBps)) / 10000); - expect(stats.managerFees).eq((10000000 * Number(poolSettings.managerFeeBps)) / 10000); + expect(stats.netIncome).eq(10000000 * (10000 - await factory.feeBps() - Number(managerFeeBps)) / 10000); + expect(stats.totalFees).eq(((10000000 * (await factory.feeBps())) / 10000) + (10000000 * Number(managerFeeBps)) / 10000); + expect(stats.managerFees).eq((10000000 * Number(managerFeeBps)) / 10000); expect(stats.protocolFees).eq((10000000 * (await factory.feeBps())) / 10000); await mine(2, { interval: 5 }); diff --git a/packages/contracts/test/DirectPayments/DirectPaymentsFactory.test.ts b/packages/contracts/test/DirectPayments/DirectPaymentsFactory.test.ts index d5f1ba88..5a91f204 100644 --- a/packages/contracts/test/DirectPayments/DirectPaymentsFactory.test.ts +++ b/packages/contracts/test/DirectPayments/DirectPaymentsFactory.test.ts @@ -76,7 +76,6 @@ describe('DirectPaymentsFactory', () => { membersValidator: ethers.constants.AddressZero, rewardToken: '0x03d3daB843e6c03b3d271eff9178e6A96c28D25f', allowRewardOverride: false, - managerFeeBps: 0 }; poolLimits = { @@ -87,7 +86,7 @@ describe('DirectPaymentsFactory', () => { }); it('should create pool correctly', async () => { - const tx = factory.createPool('test', 'pool1', poolSettings, poolLimits); + const tx = factory.createPool('test', 'pool1', poolSettings, poolLimits, 0); await expect(tx).not.reverted; await expect(tx).emit(factory, 'PoolCreated'); const poolAddr = (await (await tx).wait()).events?.find((_) => _.event === 'PoolCreated')?.args?.[0]; @@ -121,19 +120,19 @@ describe('DirectPaymentsFactory', () => { }); it("should not be able to create pool if not project's manager", async () => { - const tx = await factory.connect(signers[1]).createPool('test', 'pool1', poolSettings, poolLimits); + const tx = await factory.connect(signers[1]).createPool('test', 'pool1', poolSettings, poolLimits, 0); // signer 1 is the pool manager so it should not revert - await expect(factory.connect(signers[1]).createPool('test', 'pool2', poolSettings, poolLimits)).not.reverted; + await expect(factory.connect(signers[1]).createPool('test', 'pool2', poolSettings, poolLimits, 0)).not.reverted; - await expect(factory.createPool('test', 'pool3', poolSettings, poolLimits)).revertedWithCustomError( + await expect(factory.createPool('test', 'pool3', poolSettings, poolLimits, 0)).revertedWithCustomError( factory, 'NOT_PROJECT_OWNER' ); }); it("should be able to mint NFT from pool's minter", async () => { - const tx = await factory.createPool('test', 'pool1', poolSettings, poolLimits); + const tx = await factory.createPool('test', 'pool1', poolSettings, poolLimits, 0); const poolAddr = (await (await tx).wait()).events?.find((_) => _.event === 'PoolCreated')?.args?.[0]; const pool = (await ethers.getContractAt('DirectPaymentsPool', poolAddr)) as DirectPaymentsPool; await expect(pool.connect(signers[1]).mintNFT(pool.address, nftSample, false)).not.reverted; diff --git a/packages/contracts/test/Swap.e2e.test.ts b/packages/contracts/test/Swap.e2e.test.ts index a6d47b15..2b186765 100644 --- a/packages/contracts/test/Swap.e2e.test.ts +++ b/packages/contracts/test/Swap.e2e.test.ts @@ -88,7 +88,6 @@ describe('HelperLibrary Swap E2E (Celo fork)', () => { }); it('should create stream and swap', async () => { - console.log('network:', await trader.provider?.getNetwork()); const opts = { chainId: Number(42220), provider: ethers.provider, @@ -133,7 +132,6 @@ describe('HelperLibrary Swap E2E (Celo fork)', () => { const op = sf.batchCall([swapAction, flowOp]); const tx = op.exec(trader); - console.log(await (await tx).wait()); await expect(tx).not.reverted; }); diff --git a/packages/contracts/test/UBIPool/UBIPoolFactory.test.ts b/packages/contracts/test/UBIPool/UBIPoolFactory.test.ts index 60586ab7..e43d9f26 100644 --- a/packages/contracts/test/UBIPool/UBIPoolFactory.test.ts +++ b/packages/contracts/test/UBIPool/UBIPoolFactory.test.ts @@ -4,6 +4,7 @@ import { deployTestFramework } from '@superfluid-finance/ethereum-contracts/dev- import { expect } from 'chai'; import { UBIPoolFactory, UBIPool, ProvableNFT } from 'typechain-types'; import { ethers, upgrades } from 'hardhat'; +import { PoolSettingsStruct } from 'typechain-types/contracts/UBI/UBIPool'; type SignerWithAddress = Awaited>; @@ -11,7 +12,8 @@ describe('UBIPoolFactory', () => { let factory: UBIPoolFactory; let signer: SignerWithAddress; let signers: SignerWithAddress[]; - let poolSettings: UBIPool.PoolSettingsStruct; + let poolSettings: PoolSettingsStruct; + let extendedPoolSettings: UBIPool.ExtendedSettingsStruct; let poolLimits: UBIPool.UBISettingsStruct; let gdframework: Awaited>; let sfFramework: { [key: string]: string }; @@ -54,9 +56,12 @@ describe('UBIPoolFactory', () => { manager: signers[1].address, membersValidator: ethers.constants.AddressZero, rewardToken: '0x03d3daB843e6c03b3d271eff9178e6A96c28D25f', - managerFeeBps: 0, }; - + extendedPoolSettings = { + maxPeriodClaimers: 500, + minClaimAmount: ethers.utils.parseEther('1'), + managerFeeBps: 0, + } poolLimits = { cycleLengthDays: ethers.BigNumber.from(60), claimPeriodDays: ethers.BigNumber.from(1), @@ -65,13 +70,12 @@ describe('UBIPoolFactory', () => { maxClaimAmount: ethers.utils.parseEther('100'), maxMembers: 500, onlyMembers: true, - maxPeriodClaimers: 500, - minClaimAmount: ethers.utils.parseEther('1'), + }; }); it('should create pool correctly', async () => { - const tx = factory.createPool('test', 'pool1', poolSettings, poolLimits); + const tx = factory.createPool('test', 'pool1', poolSettings, poolLimits, extendedPoolSettings); await expect(tx).not.reverted; await expect(tx).emit(factory, 'PoolCreated'); const poolAddr = (await (await tx).wait()).events?.find((_) => _.event === 'PoolCreated')?.args?.[0]; @@ -93,12 +97,12 @@ describe('UBIPoolFactory', () => { }); it("should not be able to create pool if not project's manager", async () => { - const tx = await factory.createPool('test', 'pool1', poolSettings, poolLimits); + const tx = await factory.createPool('test', 'pool1', poolSettings, poolLimits, extendedPoolSettings); // signer 1 is the pool manager so it should not revert - await expect(factory.createPool('test', 'pool2', poolSettings, poolLimits)).not.reverted; + await expect(factory.createPool('test', 'pool2', poolSettings, poolLimits, extendedPoolSettings)).not.reverted; - await expect(factory.connect(signers[1]).createPool('test', 'pool3', poolSettings, poolLimits)).revertedWithCustomError( + await expect(factory.connect(signers[1]).createPool('test', 'pool3', poolSettings, poolLimits, extendedPoolSettings)).revertedWithCustomError( factory, 'NOT_PROJECT_OWNER' ); diff --git a/packages/contracts/tsconfig.json b/packages/contracts/tsconfig.json index e08da79a..748d9725 100644 --- a/packages/contracts/tsconfig.json +++ b/packages/contracts/tsconfig.json @@ -8,5 +8,7 @@ "skipLibCheck": true, "resolveJsonModule": true, "baseUrl": "." - } + }, + "exclude": ["typechain-types"], + "include": ["test"] } diff --git a/packages/sdk-js/src/goodcollective/__tests__/goodcollective.test.ts b/packages/sdk-js/src/goodcollective/__tests__/goodcollective.test.ts index 32aa3dbe..96661f89 100644 --- a/packages/sdk-js/src/goodcollective/__tests__/goodcollective.test.ts +++ b/packages/sdk-js/src/goodcollective/__tests__/goodcollective.test.ts @@ -48,6 +48,7 @@ const testPoolSettings = [ maxMemberPerMonth: 10000, maxTotalPerMonth: 100000, }, + 0, //manager fee false, ]; describe('GoodCollective SDK', () => { @@ -102,7 +103,8 @@ describe('GoodCollective SDK', () => { maxMemberPerDay: 1000, maxMemberPerMonth: 10000, maxTotalPerMonth: 100000, - } + }, + 0 ); const assignedType = (await pool.settings()).nftType; const toMint = { diff --git a/packages/sdk-js/src/goodcollective/goodcollective.ts b/packages/sdk-js/src/goodcollective/goodcollective.ts index 81342abe..ca0592cb 100644 --- a/packages/sdk-js/src/goodcollective/goodcollective.ts +++ b/packages/sdk-js/src/goodcollective/goodcollective.ts @@ -16,15 +16,16 @@ import { HelperLibrary } from '@gooddollar/goodcollective-contracts/typechain-ty // import * as Proof from '@web3-storage/w3up-client/proof'; // import { Signer } from '@web3-storage/w3up-client/principal/ed25519'; import { Multicall, ContractCallResults, ContractCallContext } from 'ethereum-multicall'; +import { PoolSettingsStruct } from '@gooddollar/goodcollective-contracts/typechain-types/contracts/UBI/UBIPool.ts'; export type NFTData = ProvableNFT.NFTDataStruct; export type EventData = ProvableNFT.EventDataStruct; export type PoolSettings = Omit & { nftType?: BigNumberish }; export type PoolLimits = DirectPaymentsPool.SafetyLimitsStruct; export type SwapData = HelperLibrary.SwapDataStruct; -export type UBIPoolSettings = UBIPool.PoolSettingsStruct; +export type UBIPoolSettings = PoolSettingsStruct; export type UBISettings = UBIPool.UBISettingsStruct; - +export type ExtendedUBISettings = UBIPool.ExtendedSettingsStruct; export type PoolAttributes = { name: string; description: string; @@ -223,10 +224,11 @@ export class GoodCollectiveSDK { poolAttributes: PoolAttributes, poolSettings: PoolSettings, poolLimits: PoolLimits, + managerFee: number, isBeacon: boolean ) { const uri = await this.savePoolToIPFS(poolAttributes); - return this.createPool(signer, projectId, uri, poolSettings, poolLimits, isBeacon); + return this.createPool(signer, projectId, uri, poolSettings, poolLimits, managerFee, isBeacon); } /** @@ -244,6 +246,7 @@ export class GoodCollectiveSDK { poolIpfs: string, poolSettings: PoolSettings, poolLimits: PoolLimits, + managerFee: number, isBeacon = true ) { poolSettings.nftType = 0; // force some type, this will be re-assigned by the factory @@ -251,7 +254,13 @@ export class GoodCollectiveSDK { ? this.factory.connect(signer).createBeaconPool : this.factory.connect(signer).createPool; const tx = await ( - await createMethod(projectId, poolIpfs, poolSettings as DirectPaymentsPool.PoolSettingsStruct, poolLimits) + await createMethod( + projectId, + poolIpfs, + poolSettings as DirectPaymentsPool.PoolSettingsStruct, + poolLimits, + managerFee + ) ).wait(); const created = tx.events?.find((_) => _.event === 'PoolCreated'); return this.pool.attach(created?.args?.[0]); @@ -267,10 +276,11 @@ export class GoodCollectiveSDK { async setPoolSettings( signer: ethers.Signer, poolAddress: string, - poolSettings: DirectPaymentsPool.PoolSettingsStruct + poolSettings: DirectPaymentsPool.PoolSettingsStruct, + managerFee: number ) { const connected = this.pool.attach(poolAddress).connect(signer); - return connected.setPoolSettings(poolSettings, { ...CHAIN_OVERRIDES[this.chainId] }); + return connected.setPoolSettings(poolSettings, managerFee, { ...CHAIN_OVERRIDES[this.chainId] }); } /** @@ -288,10 +298,11 @@ export class GoodCollectiveSDK { poolAttributes: PoolAttributes, poolSettings: UBIPoolSettings, poolLimits: UBISettings, + poolExtendedSettings: ExtendedUBISettings, isBeacon: boolean ) { const uri = await this.savePoolToIPFS(poolAttributes); - return this.createUbiPool(signer, projectId, uri, poolSettings, poolLimits, isBeacon); + return this.createUbiPool(signer, projectId, uri, poolSettings, poolLimits, poolExtendedSettings, isBeacon); } /** @@ -309,6 +320,7 @@ export class GoodCollectiveSDK { poolIpfs: string, poolSettings: UBIPoolSettings, poolLimits: UBISettings, + poolExtendedSettings: ExtendedUBISettings, isBeacon: boolean ) { if (!this.ubifactory) { @@ -317,7 +329,7 @@ export class GoodCollectiveSDK { const createMethod = isBeacon ? this.ubifactory.connect(signer).createManagedPool : this.ubifactory.connect(signer).createPool; - const tx = await (await createMethod(projectId, poolIpfs, poolSettings, poolLimits)).wait(); + const tx = await (await createMethod(projectId, poolIpfs, poolSettings, poolLimits, poolExtendedSettings)).wait(); const created = tx.events?.find((_) => _.event === 'PoolCreated'); return new ethers.Contract(created?.args?.[0], this.contracts.UBIPool?.abi as [], this.factory.provider) as UBIPool; } From 7c3cdb809bca9e72a3777d27b8e3c70517816738 Mon Sep 17 00:00:00 2001 From: sirpy Date: Sun, 24 Nov 2024 16:24:10 +0200 Subject: [PATCH 4/5] fix: GoodDollar verification shown for wallet that is not Face Verified #1911 --- .../app/src/hooks/useIsStewardVerified.ts | 31 +++++++++++++++---- 1 file changed, 25 insertions(+), 6 deletions(-) diff --git a/packages/app/src/hooks/useIsStewardVerified.ts b/packages/app/src/hooks/useIsStewardVerified.ts index fd0c5bcb..6b89477d 100644 --- a/packages/app/src/hooks/useIsStewardVerified.ts +++ b/packages/app/src/hooks/useIsStewardVerified.ts @@ -1,17 +1,36 @@ -import { G$ContractAddresses, CONTRACT_TO_ABI } from '@gooddollar/web3sdk-v2'; +import { G$ContractAddresses } from '@gooddollar/web3sdk-v2'; +import { isAddress, zeroAddress } from 'viem'; import { useContractRead, useNetwork } from 'wagmi'; export const useIsStewardVerified = (address: string): boolean => { const chain = useNetwork(); const idAddress = G$ContractAddresses('Identity', 'production-celo') as `0x{string}`; - const abi = CONTRACT_TO_ABI.Identity.abi; const result = useContractRead({ chainId: chain.chain?.id, - abi, + abi: [ + { + inputs: [ + { + internalType: 'address', + name: 'account', + type: 'address', + }, + ], + name: 'getWhitelistedRoot', + outputs: [ + { + internalType: 'address', + name: 'whitelisted', + type: 'address', + }, + ], + stateMutability: 'view', + type: 'function', + }, + ], address: idAddress, args: [address], - functionName: 'isWhitelisted', + functionName: 'getWhitelistedRoot', }); - - return result.data as any as boolean; + return result.data !== zeroAddress && isAddress(result.data as any); }; From 2de229d5f1525073983e55558c167b6408e2d47c Mon Sep 17 00:00:00 2001 From: sirpy Date: Tue, 26 Nov 2024 09:55:34 +0200 Subject: [PATCH 5/5] fix: compile issue --- packages/contracts/hardhat.config.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/contracts/hardhat.config.ts b/packages/contracts/hardhat.config.ts index 51ee23bc..96b1d12b 100644 --- a/packages/contracts/hardhat.config.ts +++ b/packages/contracts/hardhat.config.ts @@ -10,7 +10,7 @@ import { HardhatUserConfig } from 'hardhat/config'; dotenv.config(); const mnemonic = process.env.MNEMONIC || ''; -const privateKey = process.env.PRIVATE_KEY || ''; +const privateKey = process.env.PRIVATE_KEY || '0x0000000000000000000000000000000000000000000000000000000000000000'; const config: HardhatUserConfig = { contractSizer: {