From 0463c64f8c5677a0c9ab094a312cce98e97471a3 Mon Sep 17 00:00:00 2001 From: Marc Doerflinger Date: Wed, 14 Aug 2024 11:38:14 +0000 Subject: [PATCH 1/9] implement setLocked for component and instance (#590) --- .../unpermissioned/SimpleDistribution.sol | 7 ++ contracts/instance/IInstance.sol | 3 +- contracts/instance/IInstanceService.sol | 4 - contracts/instance/Instance.sol | 6 +- contracts/instance/InstanceAdmin.sol | 21 ++-- contracts/instance/InstanceService.sol | 45 ++------ contracts/registry/ServiceAuthorizationV3.sol | 1 + contracts/shared/Component.sol | 13 ++- contracts/shared/ComponentService.sol | 65 ++++++++++-- contracts/shared/IComponentService.sol | 12 ++- contracts/shared/IInstanceLinkedComponent.sol | 11 -- contracts/shared/InstanceLinkedComponent.sol | 8 -- test/Component.t.sol | 24 +++-- test/instance/Instance.t.sol | 100 ++++++++++++++++++ 14 files changed, 220 insertions(+), 100 deletions(-) create mode 100644 test/instance/Instance.t.sol diff --git a/contracts/examples/unpermissioned/SimpleDistribution.sol b/contracts/examples/unpermissioned/SimpleDistribution.sol index c312f29a5..5d3023979 100644 --- a/contracts/examples/unpermissioned/SimpleDistribution.sol +++ b/contracts/examples/unpermissioned/SimpleDistribution.sol @@ -55,4 +55,11 @@ contract SimpleDistribution is { _setWallet(newWallet); } + + function setLocked(bool locked) + external + onlyOwner() + { + _setLocked(locked); + } } \ No newline at end of file diff --git a/contracts/instance/IInstance.sol b/contracts/instance/IInstance.sol index b54a40bc2..bf3bd844d 100644 --- a/contracts/instance/IInstance.sol +++ b/contracts/instance/IInstance.sol @@ -15,7 +15,6 @@ import {NftId} from "../type/NftId.sol"; import {RoleId} from "../type/RoleId.sol"; import {Seconds} from "../type/Seconds.sol"; import {UFixed} from "../type/UFixed.sol"; -import {VersionPart} from "../type/Version.sol"; interface IInstance is @@ -53,7 +52,7 @@ interface IInstance is function createTarget(address target, string memory name) external; function setTargetFunctionRole(string memory targetName, bytes4[] calldata selectors, RoleId roleId) external; - function setTargetLocked(address target, bool locked) external; + function setLocked(address target, bool locked) external; function setStakingLockingPeriod(Seconds stakeLockingPeriod) external; function setStakingRewardRate(UFixed rewardRate) external; diff --git a/contracts/instance/IInstanceService.sol b/contracts/instance/IInstanceService.sol index 7d1fab7f4..6e1282826 100644 --- a/contracts/instance/IInstanceService.sol +++ b/contracts/instance/IInstanceService.sol @@ -3,11 +3,9 @@ pragma solidity ^0.8.20; import {Amount} from "../type/Amount.sol"; import {IInstance} from "./IInstance.sol"; -import {IInstanceLinkedComponent} from "../shared/IInstanceLinkedComponent.sol"; import {IService} from "../shared/IService.sol"; import {NftId} from "../type/NftId.sol"; import {ObjectType} from "../type/ObjectType.sol"; -import {RoleId} from "../type/RoleId.sol"; import {Seconds} from "../type/Seconds.sol"; import {UFixed} from "../type/UFixed.sol"; import {VersionPart} from "../type/Version.sol"; @@ -69,6 +67,4 @@ interface IInstanceService is IService { /// @dev Defunds the staking reward reserves for the specified target. function withdrawStakingRewardReserves(Amount dipAmount) external returns (Amount newBalance); - - function setComponentLocked(bool locked) external; } \ No newline at end of file diff --git a/contracts/instance/Instance.sol b/contracts/instance/Instance.sol index 0495b73a2..f191ba345 100644 --- a/contracts/instance/Instance.sol +++ b/contracts/instance/Instance.sol @@ -19,7 +19,6 @@ import {Registerable} from "../shared/Registerable.sol"; import {RoleId} from "../type/RoleId.sol"; import {Seconds} from "../type/Seconds.sol"; import {UFixed} from "../type/UFixed.sol"; -import {VersionPart, VersionPartLib} from "../type/Version.sol"; contract Instance is IInstance, @@ -186,12 +185,11 @@ contract Instance is // _instanceAdmin.setTargetFunctionRoleByInstance(targetName, selectors, roleId); } - function setTargetLocked(address target, bool locked) + function setLocked(address target, bool locked) external onlyOwner() { - // TODO refactor - // _instanceAdmin.setTargetLockedByInstance(target, locked); + _componentService.setLockedFromInstance(target, locked); } //--- ITransferInterceptor ----------------------------------------------// diff --git a/contracts/instance/InstanceAdmin.sol b/contracts/instance/InstanceAdmin.sol index 3a2dbbc67..b12c8288a 100644 --- a/contracts/instance/InstanceAdmin.sol +++ b/contracts/instance/InstanceAdmin.sol @@ -1,7 +1,6 @@ // SPDX-License-Identifier: UNLICENSED pragma solidity ^0.8.20; -import {AccessManager} from "@openzeppelin/contracts/access/manager/AccessManager.sol"; import {AccessAdmin} from "../authorization/AccessAdmin.sol"; import {AccessManagerCloneable} from "../authorization/AccessManagerCloneable.sol"; @@ -10,9 +9,8 @@ import {IInstanceLinkedComponent} from "../shared/IInstanceLinkedComponent.sol"; import {IAuthorization} from "../authorization/IAuthorization.sol"; import {IRegistry} from "../registry/IRegistry.sol"; import {IInstance} from "./IInstance.sol"; -import {IService} from "../shared/IService.sol"; -import {ObjectType, ObjectTypeLib, ALL, POOL, RELEASE} from "../type/ObjectType.sol"; -import {RoleId, RoleIdLib, ADMIN_ROLE, PUBLIC_ROLE} from "../type/RoleId.sol"; +import {ObjectType} from "../type/ObjectType.sol"; +import {RoleId, RoleIdLib} from "../type/RoleId.sol"; import {Str, StrLib} from "../type/String.sol"; import {TokenHandler} from "../shared/TokenHandler.sol"; import {VersionPart} from "../type/Version.sol"; @@ -34,11 +32,11 @@ contract InstanceAdmin is error ErrorInstanceAdminReleaseMismatch(); error ErrorInstanceAdminExpectedTargetMissing(string targetName); - IInstance _instance; + IInstance internal _instance; IRegistry internal _registry; - uint64 _idNext; + uint64 internal _idNext; - IAuthorization _instanceAuthorization; + IAuthorization internal _instanceAuthorization; /// @dev Only used for master instance admin. /// Contracts created via constructor come with disabled initializers. @@ -162,6 +160,15 @@ contract InstanceAdmin is _grantRoleToAccount(roleId, account); } + function setTargetLocked(address target, bool locked) + external + // FIXME: restricted() + { + _setTargetClosed(target, locked); + } + + + /// @dev Returns the instance authorization specification used to set up this instance admin. function getInstanceAuthorization() external diff --git a/contracts/instance/InstanceService.sol b/contracts/instance/InstanceService.sol index 66452554a..2c52ce28c 100644 --- a/contracts/instance/InstanceService.sol +++ b/contracts/instance/InstanceService.sol @@ -2,7 +2,6 @@ pragma solidity ^0.8.20; import {Clones} from "@openzeppelin/contracts/proxy/Clones.sol"; -import {ShortString, ShortStrings} from "@openzeppelin/contracts/utils/ShortStrings.sol"; import {AccessManagerCloneable} from "../authorization/AccessManagerCloneable.sol"; import {Amount} from "../type/Amount.sol"; @@ -11,24 +10,17 @@ import {RiskSet} from "./RiskSet.sol"; import {ChainNft} from "../registry/ChainNft.sol"; import {NftId} from "../type/NftId.sol"; import {RoleId} from "../type/RoleId.sol"; -import {SecondsLib} from "../type/Seconds.sol"; -import {UFixed, UFixedLib} from "../type/UFixed.sol"; -import {ADMIN_ROLE} from "../type/RoleId.sol"; -import {ObjectType, INSTANCE, BUNDLE, APPLICATION, CLAIM, DISTRIBUTION, INSTANCE, POLICY, POOL, PRODUCT, REGISTRY, STAKING} from "../type/ObjectType.sol"; +import {UFixed} from "../type/UFixed.sol"; +import {ObjectType, INSTANCE, COMPONENT, INSTANCE, REGISTRY, STAKING} from "../type/ObjectType.sol"; import {Service} from "../shared/Service.sol"; -import {IInstanceLinkedComponent} from "../shared/IInstanceLinkedComponent.sol"; -import {IService} from "../shared/IService.sol"; - -import {IDistributionComponent} from "../distribution/IDistributionComponent.sol"; -import {IPoolComponent} from "../pool/IPoolComponent.sol"; -import {IProductComponent} from "../product/IProductComponent.sol"; import {IRegistry} from "../registry/IRegistry.sol"; import {IRegistryService} from "../registry/IRegistryService.sol"; import {IStakingService} from "../staking/IStakingService.sol"; import {TargetManagerLib} from "../staking/TargetManagerLib.sol"; +import {IComponentService} from "../shared/IComponentService.sol"; import {Instance} from "./Instance.sol"; import {IInstance} from "./IInstance.sol"; import {InstanceAdmin} from "./InstanceAdmin.sol"; @@ -36,7 +28,7 @@ import {IInstanceService} from "./IInstanceService.sol"; import {InstanceReader} from "./InstanceReader.sol"; import {InstanceStore} from "./InstanceStore.sol"; import {Seconds} from "../type/Seconds.sol"; -import {VersionPart, VersionPartLib} from "../type/Version.sol"; +import {VersionPart} from "../type/Version.sol"; contract InstanceService is @@ -49,6 +41,7 @@ contract InstanceService is IRegistryService internal _registryService; IStakingService internal _stakingService; + IComponentService internal _componentService; address internal _masterAccessManager; address internal _masterInstanceAdmin; @@ -177,33 +170,6 @@ contract InstanceService is dipAmount); } - - function setComponentLocked(bool locked) - external - virtual - onlyComponent() - { - // checks - address componentAddress = msg.sender; - - if (!IInstanceLinkedComponent(componentAddress).supportsInterface(type(IInstanceLinkedComponent).interfaceId)) { - revert ErrorInstanceServiceComponentNotInstanceLinked(componentAddress); - } - - IRegistry registry = getRegistry(); - NftId instanceNftId = registry.getObjectInfo(componentAddress).parentNftId; - - IInstance instance = IInstance( - registry.getObjectAddress(instanceNftId)); - - // no revert in case already locked - // TODO refactor/implement - // instance.getInstanceAdmin().setTargetLockedByService( - // componentAddress, - // locked); - } - - function getMasterInstanceReader() external view returns (address) { return _masterInstanceReader; } @@ -386,6 +352,7 @@ contract InstanceService is _registryService = IRegistryService(_getServiceAddress(REGISTRY())); _stakingService = IStakingService(_getServiceAddress(STAKING())); + _componentService = IComponentService(_getServiceAddress(COMPONENT())); _registerInterface(type(IInstanceService).interfaceId); } diff --git a/contracts/registry/ServiceAuthorizationV3.sol b/contracts/registry/ServiceAuthorizationV3.sol index c572e51d0..4a14f2475 100644 --- a/contracts/registry/ServiceAuthorizationV3.sol +++ b/contracts/registry/ServiceAuthorizationV3.sol @@ -9,6 +9,7 @@ import {IAccess} from "../authorization/IAccess.sol"; import {IBundleService} from "../pool/IBundleService.sol"; import {IDistributionService} from "../distribution/IDistributionService.sol"; import {IInstanceService} from "../instance/IInstanceService.sol"; +import {InstanceAdmin} from "../instance/InstanceAdmin.sol"; import {IPoolService} from "../pool/IPoolService.sol"; import {IStakingService} from "../staking/IStakingService.sol"; import {IRegistryService} from "./IRegistryService.sol"; diff --git a/contracts/shared/Component.sol b/contracts/shared/Component.sol index 8a9c76403..670ad14ff 100644 --- a/contracts/shared/Component.sol +++ b/contracts/shared/Component.sol @@ -4,17 +4,15 @@ pragma solidity ^0.8.20; import {AccessManagedUpgradeable} from "@openzeppelin/contracts-upgradeable/access/manager/AccessManagedUpgradeable.sol"; import {IAccessManaged} from "@openzeppelin/contracts/access/manager/IAccessManaged.sol"; import {IERC20Metadata} from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol"; -import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; -import {Amount, AmountLib} from "../type/Amount.sol"; +import {Amount} from "../type/Amount.sol"; import {IComponent} from "./IComponent.sol"; import {IComponents} from "../instance/module/IComponents.sol"; import {IComponentService} from "./IComponentService.sol"; -import {NftId, NftIdLib} from "../type/NftId.sol"; +import {NftId} from "../type/NftId.sol"; import {ObjectType, COMPONENT} from "../type/ObjectType.sol"; import {Registerable} from "../shared/Registerable.sol"; import {TokenHandler} from "../shared/TokenHandler.sol"; -import {VersionPartLib} from "../type/Version.sol"; abstract contract Component is AccessManagedUpgradeable, @@ -195,6 +193,13 @@ abstract contract Component is _getComponentStorage()._componentService.setWallet(newWallet); } + function _setLocked(bool locked) + internal + virtual + { + _getComponentStorage()._componentService.setLockedFromComponent(address(this), locked); + } + /// @dev depending on the source of the component information this function needs to be overwritten. /// eg for instance linked components that externally store this information with the instance store contract diff --git a/contracts/shared/ComponentService.sol b/contracts/shared/ComponentService.sol index 2f55f8c70..abd58ae25 100644 --- a/contracts/shared/ComponentService.sol +++ b/contracts/shared/ComponentService.sol @@ -3,6 +3,11 @@ pragma solidity ^0.8.20; import {IERC20Metadata} from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol"; +import {AccessAdmin} from "../authorization/AccessAdmin.sol"; +import {Amount, AmountLib} from "../type/Amount.sol"; +import {ComponentVerifyingService} from "../shared/ComponentVerifyingService.sol"; +import {ContractLib} from "../shared/ContractLib.sol"; +import {Fee, FeeLib} from "../type/Fee.sol"; import {IComponent} from "../shared/IComponent.sol"; import {IComponents} from "../instance/module/IComponents.sol"; import {IComponentService} from "./IComponentService.sol"; @@ -27,6 +32,7 @@ import {ObjectType, REGISTRY, BUNDLE, COMPONENT, DISTRIBUTION, DISTRIBUTOR, INST import {Service} from "../shared/Service.sol"; import {TokenHandler} from "../shared/TokenHandler.sol"; import {TokenHandlerDeployerLib} from "../shared/TokenHandlerDeployerLib.sol"; +import {VersionPart} from "../type/Version.sol"; contract ComponentService is @@ -40,9 +46,26 @@ contract ComponentService is IInstanceService private _instanceService; modifier onlyComponent(address component) { - if (!ContractLib.supportsInterface(component, type(IInstanceLinkedComponent).interfaceId)) { - revert ErrorComponentServiceNotInstanceLinkedComponent(component); + _checkSupportsInterface(component); + _; + } + + modifier onlyInstance() { + NftId instanceNftId = getRegistry().getNftIdForAddress(msg.sender); + if (instanceNftId.eqz()) { + revert ErrorComponentServiceNotRegistered(msg.sender); } + + ObjectType objectType = getRegistry().getObjectInfo(instanceNftId).objectType; + if (objectType != INSTANCE()) { + revert ErrorComponentServiceNotInstance(msg.sender, objectType); + } + + VersionPart instanceVersion = IInstance(msg.sender).getRelease(); + if (instanceVersion != getVersion().toMajorPart()) { + revert ErrorComponentServiceInstanceVersionMismatch(msg.sender, instanceVersion); + } + _; } @@ -140,11 +163,31 @@ contract ComponentService is tokenHandler.setWallet(newWallet); } - // TODO implement - function lock() external virtual {} + /// @inheritdoc IComponentService + function setLockedFromInstance(address componentAddress, bool locked) + external + virtual + onlyInstance() + { + address instanceAddress = msg.sender; + // NftId instanceNftId = getRegistry().getNftIdForAddress(msg.sender); + IInstance instance = IInstance(instanceAddress); + _setLocked(instance.getInstanceAdmin(), componentAddress, locked); + } - // TODO implement - function unlock() external virtual {} + /// @inheritdoc IComponentService + function setLockedFromComponent(address componentAddress, bool locked) + external + virtual + onlyComponent(msg.sender) + { + (, IInstance instance) = _getAndVerifyComponentInfo( + getRegistry().getNftIdForAddress(msg.sender), + COMPONENT(), + false); // only active + + _setLocked(instance.getInstanceAdmin(), componentAddress, locked); + } function withdrawFees(Amount amount) external @@ -774,6 +817,10 @@ contract ComponentService is instance = IInstance(registry.getObjectAddress(instanceNftId)); } + function _setLocked(InstanceAdmin instanceAdmin, address componentAddress, bool locked) internal { + instanceAdmin.setTargetLocked(componentAddress, locked); + } + function _getAndVerifyActiveComponent(ObjectType expectedType) internal view @@ -806,4 +853,10 @@ contract ComponentService is function _getDomain() internal pure virtual override returns(ObjectType) { return COMPONENT(); } + + function _checkSupportsInterface(address component) internal view { + if (!ContractLib.supportsInterface(component, type(IInstanceLinkedComponent).interfaceId)) { + revert ErrorComponentServiceNotInstanceLinkedComponent(component); + } + } } \ No newline at end of file diff --git a/contracts/shared/IComponentService.sol b/contracts/shared/IComponentService.sol index 5a2a37f63..b2986f5f9 100644 --- a/contracts/shared/IComponentService.sol +++ b/contracts/shared/IComponentService.sol @@ -28,6 +28,10 @@ interface IComponentService is error ErrorComponentServiceParentNotInstance(NftId nftId, ObjectType objectType); error ErrorComponentServiceParentNotProduct(NftId nftId, ObjectType objectType); + error ErrorComponentServiceNotRegistered(address instanceAddress); + error ErrorComponentServiceNotInstance(address instanceAddress, ObjectType objectType); + error ErrorComponentServiceInstanceVersionMismatch(address instanceAddress, VersionPart instanceVersion); + error ErrorProductServiceNoDistributionExpected(NftId productNftId); error ErrorProductServiceDistributionAlreadyRegistered(NftId productNftId, NftId distributionNftId); error ErrorProductServiceNoOraclesExpected(NftId productNftId); @@ -71,11 +75,11 @@ interface IComponentService is /// @dev Sets the components associated wallet address function setWallet(address newWallet) external; - /// @dev Locks the component associated with the caller - function lock() external; + /// @dev Locks/Unlocks the given component - call from instanceService + function setLockedFromInstance(address componentAddress, bool locked) external; - /// @dev Unlocks the component associated with the caller - function unlock() external; + /// @dev Locks/Unlocks the given component - call from component + function setLockedFromComponent(address componentAddress, bool locked) external; /// @dev Withdraw fees from the distribution component. Only component owner is allowed to withdraw fees. /// @param withdrawAmount the amount to withdraw diff --git a/contracts/shared/IInstanceLinkedComponent.sol b/contracts/shared/IInstanceLinkedComponent.sol index f231296c4..06a3b9436 100644 --- a/contracts/shared/IInstanceLinkedComponent.sol +++ b/contracts/shared/IInstanceLinkedComponent.sol @@ -1,9 +1,6 @@ // SPDX-License-Identifier: Apache-2.0 pragma solidity ^0.8.20; -import {IAccessManaged} from "@openzeppelin/contracts/access/manager/IAccessManaged.sol"; -import {IERC20Metadata} from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol"; - import {Amount} from "../type/Amount.sol"; import {IComponent} from "../shared/IComponent.sol"; import {IAuthorization} from "../authorization/IAuthorization.sol"; @@ -19,14 +16,6 @@ interface IInstanceLinkedComponent is error ErrorInstanceLinkedComponentTypeMismatch(ObjectType requiredType, ObjectType objectType); error ErrorInstanceLinkedComponentNotProduct(NftId nftId, ObjectType objectType); - /// @dev locks component to disable functions that may change state related to this component, the only exception is function "unlock" - /// only component owner (nft holder) is authorizes to call this function - function lock() external; - - /// @dev unlocks component to (re-)enable functions that may change state related to this component - /// only component owner (nft holder) is authorizes to call this function - function unlock() external; - /// @dev Withdraw fees from the distribution component. Only component owner is allowed to withdraw fees. /// @param amount the amount to withdraw /// @return withdrawnAmount the amount that was actually withdrawn diff --git a/contracts/shared/InstanceLinkedComponent.sol b/contracts/shared/InstanceLinkedComponent.sol index fc0589855..39afcc414 100644 --- a/contracts/shared/InstanceLinkedComponent.sol +++ b/contracts/shared/InstanceLinkedComponent.sol @@ -40,14 +40,6 @@ abstract contract InstanceLinkedComponent is IComponentService _componentService; } - function lock() external onlyOwner { - IInstanceService(_getServiceAddress(INSTANCE())).setComponentLocked(true); - } - - function unlock() external onlyOwner { - IInstanceService(_getServiceAddress(INSTANCE())).setComponentLocked(false); - } - /// @inheritdoc IInstanceLinkedComponent function withdrawFees(Amount amount) external diff --git a/test/Component.t.sol b/test/Component.t.sol index 3dd437a04..dddd55511 100644 --- a/test/Component.t.sol +++ b/test/Component.t.sol @@ -2,6 +2,7 @@ pragma solidity ^0.8.20; import {console} from "../lib/forge-std/src/Test.sol"; +import {IAccessManaged} from "@openzeppelin/contracts/access/manager/IAccessManaged.sol"; import {BasicDistributionAuthorization} from "../contracts/distribution/BasicDistributionAuthorization.sol"; import {Fee, FeeLib} from "../contracts/type/Fee.sol"; @@ -258,33 +259,34 @@ contract TestComponent is GifTest { assertEq(token.balanceOf(externallyOwnedWallet2), INITIAL_BALANCE, "externally owned wallet 2 balance not 100000"); } - // TODO re-enable - function skip_test_component_lock() public { + function test_component_lock() public { // GIVEN - just setUp - Fee memory newDistributionFee = FeeLib.toFee(UFixedLib.toUFixed(123,0), 456); Fee memory newMinDistributionOwnerFee = FeeLib.toFee(UFixedLib.toUFixed(124,0), 457); + vm.startPrank(distributionOwner); + // WHEN - distribution.lock(); + distribution.setLocked(true); // THEN - vm.expectRevert(abi.encodeWithSelector(IAccess.ErrorIAccessTargetLocked.selector, address(distribution))); + vm.expectRevert(abi.encodeWithSelector(IAccessManaged.AccessManagedUnauthorized.selector, address(distributionOwner))); distribution.setFees(newMinDistributionOwnerFee, newDistributionFee); } - // TODO re-enable - function skip_test_component_unlock() public { + function test_component_unlock() public { // GIVEN - just setUp + Fee memory newDistributionFee = FeeLib.toFee(UFixedLib.toUFixed(123,0), 456); + Fee memory newMinDistributionOwnerFee = FeeLib.toFee(UFixedLib.toUFixed(124,0), 457); + + vm.startPrank(distributionOwner); - distribution.lock(); + distribution.setLocked(true); // WHEN - distribution.unlock(); + distribution.setLocked(false); // THEN - Fee memory newDistributionFee = FeeLib.toFee(UFixedLib.toUFixed(123,0), 456); - Fee memory newMinDistributionOwnerFee = FeeLib.toFee(UFixedLib.toUFixed(124,0), 457); distribution.setFees(newMinDistributionOwnerFee, newDistributionFee); } diff --git a/test/instance/Instance.t.sol b/test/instance/Instance.t.sol new file mode 100644 index 000000000..540c3f1ef --- /dev/null +++ b/test/instance/Instance.t.sol @@ -0,0 +1,100 @@ +// SPDX-License-Identifier: APACHE-2.0 +pragma solidity ^0.8.20; + +import {IAccessManaged} from "@openzeppelin/contracts/access/manager/IAccessManaged.sol"; + +import {Fee, FeeLib} from "../../contracts/type/Fee.sol"; +import {GifTest} from "../base/GifTest.sol"; +import {IAccessAdmin} from "../../contracts/authorization/IAccessAdmin.sol"; +import {IInstance} from "../../contracts/instance/IInstance.sol"; +import {IInstanceService} from "../../contracts/instance/IInstanceService.sol"; +import {InstanceAdmin} from "../../contracts/instance/InstanceAdmin.sol"; +import {InstanceReader} from "../../contracts/instance/InstanceReader.sol"; +import {NftId} from "../../contracts/type/NftId.sol"; +import {UFixed, UFixedLib} from "../../contracts/type/UFixed.sol"; + +contract TestInstance is GifTest { + + function setUp() public override { + super.setUp(); + _prepareProduct(); // also deploys and registers distribution + } + + function test_Instance_setLocked_lock() public { + // GIVEN + Fee memory newDistributionFee = FeeLib.toFee(UFixedLib.toUFixed(123,0), 456); + Fee memory newMinDistributionOwnerFee = FeeLib.toFee(UFixedLib.toUFixed(124,0), 457); + + vm.startPrank(instanceOwner); + instance.setLocked(address(distribution), true); + vm.stopPrank(); + + vm.startPrank(distributionOwner); + + // THEN + vm.expectRevert(abi.encodeWithSelector(IAccessManaged.AccessManagedUnauthorized.selector, address(distributionOwner))); + + // WHEN + distribution.setFees(newMinDistributionOwnerFee, newDistributionFee); + } + + function test_Instance_setLocked_unlock() public { + // GIVEN + Fee memory newDistributionFee = FeeLib.toFee(UFixedLib.toUFixed(123,0), 456); + Fee memory newMinDistributionOwnerFee = FeeLib.toFee(UFixedLib.toUFixed(124,0), 457); + + vm.startPrank(instanceOwner); + instance.setLocked(address(distribution), true); + instance.setLocked(address(distribution), false); + vm.stopPrank(); + + vm.startPrank(distributionOwner); + + // THEN - WHEN - make sure no revert + distribution.setFees(newMinDistributionOwnerFee, newDistributionFee); + } + + function test_Instance_setLocked_invalidTarget() public { + // GIVEN + Fee memory newDistributionFee = FeeLib.toFee(UFixedLib.toUFixed(123,0), 456); + Fee memory newMinDistributionOwnerFee = FeeLib.toFee(UFixedLib.toUFixed(124,0), 457); + + vm.startPrank(instanceOwner); + + // THEN + vm.expectRevert(abi.encodeWithSelector(IAccessAdmin.ErrorTargetUnknown.selector, address(this))); + + // WHEN + instance.setLocked(address(this), true); + } + + function test_Instance_setLocked_alreadyLocked() public { + // GIVEN + Fee memory newDistributionFee = FeeLib.toFee(UFixedLib.toUFixed(123,0), 456); + Fee memory newMinDistributionOwnerFee = FeeLib.toFee(UFixedLib.toUFixed(124,0), 457); + + vm.startPrank(instanceOwner); + instance.setLocked(address(distribution), true); + + // THEN + vm.expectRevert(abi.encodeWithSelector(IAccessAdmin.ErrorTargetAlreadyLocked.selector, address(distribution), true)); + + // WHEN + instance.setLocked(address(distribution), true); + } + + function test_Instance_setLocked_alreadyUnlocked() public { + // GIVEN + Fee memory newDistributionFee = FeeLib.toFee(UFixedLib.toUFixed(123,0), 456); + Fee memory newMinDistributionOwnerFee = FeeLib.toFee(UFixedLib.toUFixed(124,0), 457); + + vm.startPrank(instanceOwner); + + // THEN + vm.expectRevert(abi.encodeWithSelector(IAccessAdmin.ErrorTargetAlreadyLocked.selector, address(distribution), false)); + + // WHEN + instance.setLocked(address(distribution), false); + } + +} From 4973cefd19e46b530b704ea672483ebba7060b32 Mon Sep 17 00:00:00 2001 From: Marc Doerflinger Date: Thu, 15 Aug 2024 06:57:49 +0000 Subject: [PATCH 2/9] add todo --- contracts/shared/ComponentService.sol | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/contracts/shared/ComponentService.sol b/contracts/shared/ComponentService.sol index abd58ae25..886f13418 100644 --- a/contracts/shared/ComponentService.sol +++ b/contracts/shared/ComponentService.sol @@ -181,11 +181,12 @@ contract ComponentService is virtual onlyComponent(msg.sender) { - (, IInstance instance) = _getAndVerifyComponentInfo( - getRegistry().getNftIdForAddress(msg.sender), - COMPONENT(), - false); // only active - + // FIXME: needs to work with locked components + // (, IInstance instance) = _getAndVerifyComponentInfo( + // getRegistry().getNftIdForAddress(msg.sender), + // COMPONENT(), + // false); // only active + (, IInstance instance) = _getAndVerifyActiveComponent(COMPONENT()); _setLocked(instance.getInstanceAdmin(), componentAddress, locked); } From ef3d74591c173807301b01b167b3629041d47477 Mon Sep 17 00:00:00 2001 From: Marc Doerflinger Date: Thu, 15 Aug 2024 07:06:36 +0000 Subject: [PATCH 3/9] make _getAndVerifyComponent work for active and inactive components (#590) --- contracts/shared/ComponentService.sol | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/contracts/shared/ComponentService.sol b/contracts/shared/ComponentService.sol index 886f13418..ad179d373 100644 --- a/contracts/shared/ComponentService.sol +++ b/contracts/shared/ComponentService.sol @@ -181,12 +181,7 @@ contract ComponentService is virtual onlyComponent(msg.sender) { - // FIXME: needs to work with locked components - // (, IInstance instance) = _getAndVerifyComponentInfo( - // getRegistry().getNftIdForAddress(msg.sender), - // COMPONENT(), - // false); // only active - (, IInstance instance) = _getAndVerifyActiveComponent(COMPONENT()); + (, IInstance instance) = _getAndVerifyComponent(COMPONENT(), false); _setLocked(instance.getInstanceAdmin(), componentAddress, locked); } @@ -829,6 +824,17 @@ contract ComponentService is NftId componentNftId, IInstance instance ) + { + return _getAndVerifyComponent(expectedType, true); // only active + } + + function _getAndVerifyComponent(ObjectType expectedType, bool isActive) + internal + view + returns ( + NftId componentNftId, + IInstance instance + ) { IRegistry.ObjectInfo memory info; address instanceAddress; @@ -838,12 +844,12 @@ contract ComponentService is getRegistry(), msg.sender, // caller expectedType, - true); // only active + isActive); } else { (info, instanceAddress) = ContractLib.getAndVerifyAnyComponent( getRegistry(), msg.sender, - true); // only active + isActive); } // get component nft id and instance From a5327b0a894a4f8a8b7c700f234947740c4fa75e Mon Sep 17 00:00:00 2001 From: Marc Doerflinger Date: Thu, 15 Aug 2024 07:51:09 +0000 Subject: [PATCH 4/9] authorize call to setTargetLocked (#590) --- contracts/instance/InstanceAdmin.sol | 2 +- contracts/instance/InstanceAuthorizationV3.sol | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/contracts/instance/InstanceAdmin.sol b/contracts/instance/InstanceAdmin.sol index b12c8288a..96e5f8c0e 100644 --- a/contracts/instance/InstanceAdmin.sol +++ b/contracts/instance/InstanceAdmin.sol @@ -162,7 +162,7 @@ contract InstanceAdmin is function setTargetLocked(address target, bool locked) external - // FIXME: restricted() + restricted() { _setTargetClosed(target, locked); } diff --git a/contracts/instance/InstanceAuthorizationV3.sol b/contracts/instance/InstanceAuthorizationV3.sol index 9c0504124..09678d337 100644 --- a/contracts/instance/InstanceAuthorizationV3.sol +++ b/contracts/instance/InstanceAuthorizationV3.sol @@ -134,8 +134,9 @@ contract InstanceAuthorizationV3 functions = _authorizeForTarget(INSTANCE_ADMIN_TARGET_NAME, _toTargetRoleId(INSTANCE())); _authorize(functions, InstanceAdmin.grantRole.selector, "grantRole"); - // authorize instance service role - // functions = _authorizeForTarget(INSTANCE_ADMIN_TARGET_NAME, getServiceRole(INSTANCE())); + // authorize component service role + functions = _authorizeForTarget(INSTANCE_ADMIN_TARGET_NAME, getServiceRole(COMPONENT())); + _authorize(functions, InstanceAdmin.setTargetLocked.selector, "setTargetLocked"); } From 93e8c6e316224911fa9ce279ca271ac9a7b18a8b Mon Sep 17 00:00:00 2001 From: Matthias Zimmermann Date: Thu, 15 Aug 2024 10:46:39 +0000 Subject: [PATCH 5/9] minor refactorings --- contracts/distribution/BasicDistribution.sol | 8 +-- contracts/distribution/Distribution.sol | 51 ++++++++++--------- contracts/examples/fire/FirePool.sol | 8 ++- .../examples/fire/FirePoolAuthorization.sol | 1 + contracts/examples/fire/FireProduct.sol | 5 ++ .../fire/FireProductAuthorization.sol | 1 + .../unpermissioned/SimpleDistribution.sol | 20 +++----- .../SimpleDistributionAuthorization.sol | 28 ++++++++++ .../examples/unpermissioned/SimplePool.sol | 15 +++--- .../SimplePoolAuthorization.sol | 28 ++++++++++ .../examples/unpermissioned/SimpleProduct.sol | 7 +++ .../SimpleProductAuthorization.sol | 28 ++++++++++ contracts/instance/InstanceReader.sol | 2 +- .../product/BasicProductAuthorization.sol | 3 +- contracts/registry/RegistryAdmin.sol | 8 ++- contracts/shared/Component.sol | 13 ++--- contracts/shared/IComponent.sol | 5 -- contracts/shared/InstanceLinkedComponent.sol | 2 +- contracts/staking/Staking.sol | 10 ++++ test/Component.t.sol | 22 +++++--- test/base/GifTest.sol | 13 +++-- .../composeability/PoolWithReinsurance.sol | 1 - .../PoolWithReinsuranceAuthorization.sol | 2 + 23 files changed, 197 insertions(+), 84 deletions(-) create mode 100644 contracts/examples/unpermissioned/SimpleDistributionAuthorization.sol create mode 100644 contracts/examples/unpermissioned/SimplePoolAuthorization.sol create mode 100644 contracts/examples/unpermissioned/SimpleProductAuthorization.sol diff --git a/contracts/distribution/BasicDistribution.sol b/contracts/distribution/BasicDistribution.sol index 9e647ffe8..a1287cf77 100644 --- a/contracts/distribution/BasicDistribution.sol +++ b/contracts/distribution/BasicDistribution.sol @@ -44,8 +44,8 @@ contract BasicDistribution is ) external virtual - onlyOwner() restricted() + onlyOwner() returns (DistributorType distributorType) { return _createDistributorType( @@ -67,8 +67,8 @@ contract BasicDistribution is ) external virtual - onlyOwner() restricted() + onlyOwner() returns(NftId distributorNftId) { return _createDistributor(distributor, distributorType, data); @@ -81,9 +81,9 @@ contract BasicDistribution is ) external virtual + restricted() onlyOwner() onlyNftOfType(distributorNftId, DISTRIBUTOR()) - restricted() { _changeDistributorType(distributorNftId, distributorType, data); } @@ -101,8 +101,8 @@ contract BasicDistribution is ) external virtual - onlyDistributor() restricted() + onlyDistributor() returns (ReferralId referralId) { NftId distributorNftId = getDistributorNftId(msg.sender); diff --git a/contracts/distribution/Distribution.sol b/contracts/distribution/Distribution.sol index c5da00203..fb8d14a92 100644 --- a/contracts/distribution/Distribution.sol +++ b/contracts/distribution/Distribution.sol @@ -37,6 +37,32 @@ abstract contract Distribution is } + function processRenewal( + ReferralId referralId, + uint256 feeAmount + ) + external + virtual + restricted() + { + // default is no action + } + + + /// @inheritdoc IDistributionComponent + function withdrawCommission(NftId distributorNftId, Amount amount) + external + virtual + restricted() + onlyDistributor() + onlyNftOfType(distributorNftId, DISTRIBUTOR()) + onlyNftOwner(distributorNftId) + returns (Amount withdrawnAmount) + { + return _withdrawCommission(distributorNftId, amount); + } + + function isDistributor(address candidate) public view @@ -97,35 +123,12 @@ abstract contract Distribution is } - function processRenewal( - ReferralId referralId, - uint256 feeAmount - ) - external - virtual - restricted() - { - // default is no action - } - /// @dev Returns true iff the component needs to be called when selling/renewing policis function isVerifying() external pure returns (bool verifying) { return true; } - /// @inheritdoc IDistributionComponent - function withdrawCommission(NftId distributorNftId, Amount amount) - external - virtual - restricted() - onlyDistributor() - onlyNftOfType(distributorNftId, DISTRIBUTOR()) - onlyNftOwner(distributorNftId) - returns (Amount withdrawnAmount) - { - return _withdrawCommission(distributorNftId, amount); - } - + function _initializeDistribution( address registry, NftId productNftId, diff --git a/contracts/examples/fire/FirePool.sol b/contracts/examples/fire/FirePool.sol index 3ddd7fa51..e133f4568 100644 --- a/contracts/examples/fire/FirePool.sol +++ b/contracts/examples/fire/FirePool.sol @@ -1,6 +1,8 @@ // SPDX-License-Identifier: Apache-2.0 pragma solidity ^0.8.20; +import {IERC20Metadata} from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol"; + import {Amount, AmountLib} from "../../type/Amount.sol"; import {BasicPool} from "../../pool/BasicPool.sol"; import {Fee} from "../../type/Fee.sol"; @@ -12,8 +14,7 @@ import {UFixed, UFixedLib} from "../../type/UFixed.sol"; contract FirePool is BasicPool -{ - +{ constructor( address registry, NftId fireProductNftId, @@ -83,4 +84,7 @@ contract FirePool is netStakedAmount = _stake(bundleNftId, initialAmount); } + function approveTokenHandler(IERC20Metadata token, Amount amount) external restricted() onlyOwner() { _approveTokenHandler(token, amount); } + function setLocked(bool locked) external onlyOwner() { _setLocked(locked); } + function setWallet(address newWallet) external restricted() onlyOwner() { _setWallet(newWallet); } } \ No newline at end of file diff --git a/contracts/examples/fire/FirePoolAuthorization.sol b/contracts/examples/fire/FirePoolAuthorization.sol index a25c14565..ac7f0bd30 100644 --- a/contracts/examples/fire/FirePoolAuthorization.sol +++ b/contracts/examples/fire/FirePoolAuthorization.sol @@ -27,6 +27,7 @@ contract FirePoolAuthorization // authorize public role (open access to any account, only allows to lock target) functions = _authorizeForTarget(getTargetName(), PUBLIC_ROLE()); // TODO: FirePool.createBundle must require a custom role (e.g. INVESTOR) instead of PUBLIC_ROLE + _authorize(functions, FirePool.approveTokenHandler.selector, "approveTokenHandler"); _authorize(functions, FirePool.createBundle.selector, "createBundle"); } diff --git a/contracts/examples/fire/FireProduct.sol b/contracts/examples/fire/FireProduct.sol index f72fd9995..e4aec27b8 100644 --- a/contracts/examples/fire/FireProduct.sol +++ b/contracts/examples/fire/FireProduct.sol @@ -1,6 +1,8 @@ // SPDX-License-Identifier: Apache-2.0 pragma solidity ^0.8.20; +import {IERC20Metadata} from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol"; + import {ACTIVE, PAUSED} from "../../type/StateId.sol"; import {Amount, AmountLib} from "../../type/Amount.sol"; import {BasicProduct} from "../../product/BasicProduct.sol"; @@ -428,4 +430,7 @@ contract FireProduct is } } + function approveTokenHandler(IERC20Metadata token, Amount amount) external restricted() onlyOwner() { _approveTokenHandler(token, amount); } + function setLocked(bool locked) external onlyOwner() { _setLocked(locked); } + function setWallet(address newWallet) external restricted() onlyOwner() { _setWallet(newWallet); } } \ No newline at end of file diff --git a/contracts/examples/fire/FireProductAuthorization.sol b/contracts/examples/fire/FireProductAuthorization.sol index 8a5df73a8..d7dd1a426 100644 --- a/contracts/examples/fire/FireProductAuthorization.sol +++ b/contracts/examples/fire/FireProductAuthorization.sol @@ -25,6 +25,7 @@ contract FireProductAuthorization // authorize public role (open access to any account, only allows to lock target) functions = _authorizeForTarget(getTargetName(), PUBLIC_ROLE()); // fully public functions + _authorize(functions, FireProduct.approveTokenHandler.selector, "approveTokenHandler"); _authorize(functions, FireProduct.createApplication.selector, "createApplication"); // only owner diff --git a/contracts/examples/unpermissioned/SimpleDistribution.sol b/contracts/examples/unpermissioned/SimpleDistribution.sol index 5d3023979..e6bf37910 100644 --- a/contracts/examples/unpermissioned/SimpleDistribution.sol +++ b/contracts/examples/unpermissioned/SimpleDistribution.sol @@ -1,6 +1,9 @@ // SPDX-License-Identifier: Apache-2.0 pragma solidity ^0.8.20; +import {IERC20Metadata} from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol"; + +import {Amount} from "../../type/Amount.sol"; import {BasicDistribution} from "../../distribution/BasicDistribution.sol"; import {IAuthorization} from "../../authorization/IAuthorization.sol"; import {NftId} from "../../type/NftId.sol"; @@ -48,18 +51,7 @@ contract SimpleDistribution is token); } - - function setWallet(address newWallet) - external - onlyOwner() - { - _setWallet(newWallet); - } - - function setLocked(bool locked) - external - onlyOwner() - { - _setLocked(locked); - } + function approveTokenHandler(IERC20Metadata token, Amount amount) external restricted() onlyOwner() { _approveTokenHandler(token, amount); } + function setLocked(bool locked) external onlyOwner() { _setLocked(locked); } + function setWallet(address newWallet) external restricted() onlyOwner() { _setWallet(newWallet); } } \ No newline at end of file diff --git a/contracts/examples/unpermissioned/SimpleDistributionAuthorization.sol b/contracts/examples/unpermissioned/SimpleDistributionAuthorization.sol new file mode 100644 index 000000000..c746a1eb4 --- /dev/null +++ b/contracts/examples/unpermissioned/SimpleDistributionAuthorization.sol @@ -0,0 +1,28 @@ +// SPDX-License-Identifier: Apache-2.0 +pragma solidity ^0.8.20; + +import {BasicDistributionAuthorization} from "../../distribution/BasicDistributionAuthorization.sol"; +import {IAccess} from "../../authorization/IAccess.sol"; +import {PUBLIC_ROLE} from "../../type/RoleId.sol"; +import {SimpleDistribution} from "./SimpleDistribution.sol"; + +contract SimpleDistributionAuthorization + is BasicDistributionAuthorization +{ + constructor(string memory componentName) + BasicDistributionAuthorization(componentName) + {} + + function _setupTargetAuthorizations() + internal + virtual override + { + super._setupTargetAuthorizations(); + + // authorize public role (open access to any account, only allows to lock target) + IAccess.FunctionInfo[] storage functions; + functions = _authorizeForTarget(getTargetName(), PUBLIC_ROLE()); + _authorize(functions, SimpleDistribution.approveTokenHandler.selector, "approveTokenHandler"); + _authorize(functions, SimpleDistribution.setWallet.selector, "setWallet"); + } +} \ No newline at end of file diff --git a/contracts/examples/unpermissioned/SimplePool.sol b/contracts/examples/unpermissioned/SimplePool.sol index 816cec58e..539977d84 100644 --- a/contracts/examples/unpermissioned/SimplePool.sol +++ b/contracts/examples/unpermissioned/SimplePool.sol @@ -1,6 +1,8 @@ // SPDX-License-Identifier: Apache-2.0 pragma solidity ^0.8.20; +import {IERC20Metadata} from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol"; + import {Amount, AmountLib} from "../../type/Amount.sol"; import {BasicPool} from "../../pool/BasicPool.sol"; import {BasicPoolAuthorization} from "../../pool/BasicPoolAuthorization.sol"; @@ -58,14 +60,6 @@ contract SimplePool is } - function setWallet(address newWallet) - external - onlyOwner() - { - _setWallet(newWallet); - } - - function createBundle( Fee memory fee, uint256 initialAmount, @@ -105,4 +99,9 @@ contract SimplePool is { _defundPoolWallet(amount); } + + + function approveTokenHandler(IERC20Metadata token, Amount amount) external restricted() onlyOwner() { _approveTokenHandler(token, amount); } + function setLocked(bool locked) external onlyOwner() { _setLocked(locked); } + function setWallet(address newWallet) external restricted() onlyOwner() { _setWallet(newWallet); } } \ No newline at end of file diff --git a/contracts/examples/unpermissioned/SimplePoolAuthorization.sol b/contracts/examples/unpermissioned/SimplePoolAuthorization.sol new file mode 100644 index 000000000..76939282f --- /dev/null +++ b/contracts/examples/unpermissioned/SimplePoolAuthorization.sol @@ -0,0 +1,28 @@ +// SPDX-License-Identifier: Apache-2.0 +pragma solidity ^0.8.20; + +import {BasicPoolAuthorization} from "../../pool/BasicPoolAuthorization.sol"; +import {IAccess} from "../../authorization/IAccess.sol"; +import {PUBLIC_ROLE} from "../../type/RoleId.sol"; +import {SimplePool} from "./SimplePool.sol"; + +contract SimplePoolAuthorization + is BasicPoolAuthorization +{ + constructor(string memory poolName) + BasicPoolAuthorization(poolName) + {} + + function _setupTargetAuthorizations() + internal + virtual override + { + super._setupTargetAuthorizations(); + + // authorize public role (open access to any account, only allows to lock target) + IAccess.FunctionInfo[] storage functions; + functions = _authorizeForTarget(getTargetName(), PUBLIC_ROLE()); + _authorize(functions, SimplePool.approveTokenHandler.selector, "approveTokenHandler"); + _authorize(functions, SimplePool.setWallet.selector, "setWallet"); + } +} \ No newline at end of file diff --git a/contracts/examples/unpermissioned/SimpleProduct.sol b/contracts/examples/unpermissioned/SimpleProduct.sol index 9f02e5c4b..5fe7977b6 100644 --- a/contracts/examples/unpermissioned/SimpleProduct.sol +++ b/contracts/examples/unpermissioned/SimpleProduct.sol @@ -1,6 +1,8 @@ // SPDX-License-Identifier: Apache-2.0 pragma solidity ^0.8.20; +import {IERC20Metadata} from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol"; + import {Amount, AmountLib} from "../../type/Amount.sol"; import {BasicProduct} from "../../product/BasicProduct.sol"; import {ClaimId} from "../../type/ClaimId.sol"; @@ -77,6 +79,7 @@ contract SimpleProduct is _oracleService = IOracleService(_getServiceAddress(ORACLE())); } + function createRisk( RiskId id, bytes memory data @@ -342,4 +345,8 @@ contract SimpleProduct is function getOracleService() public view returns (IOracleService) { return _oracleService; } + + function approveTokenHandler(IERC20Metadata token, Amount amount) external restricted() onlyOwner() { _approveTokenHandler(token, amount); } + function setLocked(bool locked) external onlyOwner() { _setLocked(locked); } + function setWallet(address newWallet) external restricted() onlyOwner() { _setWallet(newWallet); } } \ No newline at end of file diff --git a/contracts/examples/unpermissioned/SimpleProductAuthorization.sol b/contracts/examples/unpermissioned/SimpleProductAuthorization.sol new file mode 100644 index 000000000..0c632404e --- /dev/null +++ b/contracts/examples/unpermissioned/SimpleProductAuthorization.sol @@ -0,0 +1,28 @@ +// SPDX-License-Identifier: Apache-2.0 +pragma solidity ^0.8.20; + +import {BasicProductAuthorization} from "../../product/BasicProductAuthorization.sol"; +import {IAccess} from "../../authorization/IAccess.sol"; +import {PUBLIC_ROLE} from "../../type/RoleId.sol"; +import {SimpleProduct} from "./SimpleProduct.sol"; + +contract SimpleProductAuthorization + is BasicProductAuthorization +{ + constructor(string memory componentName) + BasicProductAuthorization(componentName) + {} + + function _setupTargetAuthorizations() + internal + virtual override + { + super._setupTargetAuthorizations(); + + // authorize public role (open access to any account, only allows to lock target) + IAccess.FunctionInfo[] storage functions; + functions = _authorizeForTarget(getTargetName(), PUBLIC_ROLE()); + _authorize(functions, SimpleProduct.approveTokenHandler.selector, "approveTokenHandler"); + _authorize(functions, SimpleProduct.setWallet.selector, "setWallet"); + } +} \ No newline at end of file diff --git a/contracts/instance/InstanceReader.sol b/contracts/instance/InstanceReader.sol index eb534495e..cfd9c285f 100644 --- a/contracts/instance/InstanceReader.sol +++ b/contracts/instance/InstanceReader.sol @@ -518,7 +518,7 @@ contract InstanceReader { } - function isTargetLocked(address target) public view returns (bool) { + function isLocked(address target) public view returns (bool) { return _instance.getInstanceAdmin().isTargetLocked(target); } diff --git a/contracts/product/BasicProductAuthorization.sol b/contracts/product/BasicProductAuthorization.sol index 031e248a7..4d434ab4f 100644 --- a/contracts/product/BasicProductAuthorization.sol +++ b/contracts/product/BasicProductAuthorization.sol @@ -6,8 +6,7 @@ import {BasicProduct} from "./BasicProduct.sol"; import {PRODUCT} from "../type/ObjectType.sol"; import {IAccess} from "../authorization/IAccess.sol"; import {IInstanceLinkedComponent} from "../shared/IInstanceLinkedComponent.sol"; -import {PUBLIC_ROLE} from "../../contracts/type/RoleId.sol"; -import {RoleId} from "../type/RoleId.sol"; +import {RoleId, PUBLIC_ROLE} from "../type/RoleId.sol"; contract BasicProductAuthorization diff --git a/contracts/registry/RegistryAdmin.sol b/contracts/registry/RegistryAdmin.sol index ddd98dd2a..906352486 100644 --- a/contracts/registry/RegistryAdmin.sol +++ b/contracts/registry/RegistryAdmin.sol @@ -10,6 +10,7 @@ import {IStaking} from "../staking/IStaking.sol"; import {ObjectType, ObjectTypeLib, ALL, REGISTRY, STAKING, POOL, RELEASE} from "../type/ObjectType.sol"; import {ReleaseRegistry} from "./ReleaseRegistry.sol"; import {RoleId, RoleIdLib, ADMIN_ROLE, GIF_MANAGER_ROLE, GIF_ADMIN_ROLE, PUBLIC_ROLE} from "../type/RoleId.sol"; +import {Staking} from "../staking/Staking.sol"; import {StakingStore} from "../staking/StakingStore.sol"; import {TokenHandler} from "../shared/TokenHandler.sol"; import {TokenRegistry} from "./TokenRegistry.sol"; @@ -367,6 +368,12 @@ contract RegistryAdmin is _createTarget(address(IStaking(_staking).getTokenHandler()), STAKING_TH_TARGET_NAME, true, false); _createTarget(_stakingStore, STAKING_STORE_TARGET_NAME, true, false); + // staking function authorization for public role + FunctionInfo[] memory functions; + functions = new FunctionInfo[](1); + functions[0] = toFunction(Staking.approveTokenHandler.selector, "approveTokenHandler"); + _authorizeTargetFunctions(_staking, PUBLIC_ROLE(), functions); // only owner protected + // staking function authorization for staking service RoleId stakingServiceRoleId = RoleIdLib.roleForTypeAndAllVersions(STAKING()); _createRole( @@ -377,7 +384,6 @@ contract RegistryAdmin is maxMemberCount: MAX_NUM_RELEASES, name: STAKING_SERVICE_ROLE_NAME})); - FunctionInfo[] memory functions; functions = new FunctionInfo[](12); functions[0] = toFunction(IStaking.registerTarget.selector, "registerTarget"); functions[1] = toFunction(IStaking.setLockingPeriod.selector, "setLockingPeriod"); diff --git a/contracts/shared/Component.sol b/contracts/shared/Component.sol index 670ad14ff..0d02fc616 100644 --- a/contracts/shared/Component.sol +++ b/contracts/shared/Component.sol @@ -38,12 +38,14 @@ abstract contract Component is _; } + function _getComponentStorage() private pure returns (ComponentStorage storage $) { assembly { $.slot := COMPONENT_LOCATION_V1 } } + function _initializeComponent( address authority, address registry, @@ -91,14 +93,6 @@ abstract contract Component is } - function approveTokenHandler(IERC20Metadata token, Amount amount) - public - onlyOwner() - { - _approveTokenHandler(token, amount); - } - - // function setWallet(address newWallet) // external // virtual @@ -164,6 +158,9 @@ abstract contract Component is } + /// @dev Approves token hanlder to spend up to the specified amount of tokens. + /// Reverts if component wallet is not token handler itself. + /// Only component owner (nft holder) is authorizes to call this function. function _approveTokenHandler(IERC20Metadata token, Amount amount) internal virtual diff --git a/contracts/shared/IComponent.sol b/contracts/shared/IComponent.sol index fadf446d9..a84d72620 100644 --- a/contracts/shared/IComponent.sol +++ b/contracts/shared/IComponent.sol @@ -33,11 +33,6 @@ interface IComponent is event LogComponentWalletTokensTransferred(address from, address to, uint256 amount); event LogComponentTokenHandlerApproved(address tokenHandler, address token, Amount limit, bool isMaxAmount); - /// @dev Approves token hanlder to spend up to the specified amount of tokens. - /// Reverts if component wallet is not token handler itself. - /// Only component owner (nft holder) is authorizes to call this function. - function approveTokenHandler(IERC20Metadata token, Amount spendingLimitAmount) external; - /// @dev returns the name of this component /// to successfully register the component with an instance the name MUST be unique in the linked instance function getName() external view returns (string memory name); diff --git a/contracts/shared/InstanceLinkedComponent.sol b/contracts/shared/InstanceLinkedComponent.sol index 39afcc414..7f8d2b973 100644 --- a/contracts/shared/InstanceLinkedComponent.sol +++ b/contracts/shared/InstanceLinkedComponent.sol @@ -44,8 +44,8 @@ abstract contract InstanceLinkedComponent is function withdrawFees(Amount amount) external virtual - onlyOwner() restricted() + onlyOwner() returns (Amount withdrawnAmount) { return _withdrawFees(amount); diff --git a/contracts/staking/Staking.sol b/contracts/staking/Staking.sol index 1be0eaba6..ed025a84d 100644 --- a/contracts/staking/Staking.sol +++ b/contracts/staking/Staking.sol @@ -77,6 +77,16 @@ contract Staking is getRegistry().getAuthority()); } + + function approveTokenHandler(IERC20Metadata token, Amount amount) + public + restricted() + onlyOwner() + { + _approveTokenHandler(token, amount); + } + + // set/update staking reader function setStakingReader(StakingReader stakingReader) external diff --git a/test/Component.t.sol b/test/Component.t.sol index dddd55511..7e730df31 100644 --- a/test/Component.t.sol +++ b/test/Component.t.sol @@ -259,33 +259,43 @@ contract TestComponent is GifTest { assertEq(token.balanceOf(externallyOwnedWallet2), INITIAL_BALANCE, "externally owned wallet 2 balance not 100000"); } - function test_component_lock() public { + function test_componentSetLockedTrue() public { // GIVEN - just setUp Fee memory newDistributionFee = FeeLib.toFee(UFixedLib.toUFixed(123,0), 456); Fee memory newMinDistributionOwnerFee = FeeLib.toFee(UFixedLib.toUFixed(124,0), 457); - vm.startPrank(distributionOwner); + assertFalse(instanceReader.isLocked(address(distribution)), "distribution locked"); // WHEN + vm.startPrank(distributionOwner); distribution.setLocked(true); + assertTrue(instanceReader.isLocked(address(distribution)), "distribution not locked"); + // THEN - vm.expectRevert(abi.encodeWithSelector(IAccessManaged.AccessManagedUnauthorized.selector, address(distributionOwner))); + vm.expectRevert( + abi.encodeWithSelector( + IAccessManaged.AccessManagedUnauthorized.selector, + address(distributionOwner))); + distribution.setFees(newMinDistributionOwnerFee, newDistributionFee); } - function test_component_unlock() public { + function test_componentSetLockedFalse() public { // GIVEN - just setUp Fee memory newDistributionFee = FeeLib.toFee(UFixedLib.toUFixed(123,0), 456); Fee memory newMinDistributionOwnerFee = FeeLib.toFee(UFixedLib.toUFixed(124,0), 457); vm.startPrank(distributionOwner); - distribution.setLocked(true); - + + assertTrue(instanceReader.isLocked(address(distribution)), "distribution not locked"); + // WHEN distribution.setLocked(false); + assertFalse(instanceReader.isLocked(address(distribution)), "distribution not locked"); + // THEN distribution.setFees(newMinDistributionOwnerFee, newDistributionFee); } diff --git a/test/base/GifTest.sol b/test/base/GifTest.sol index 7771b9283..b857740a1 100644 --- a/test/base/GifTest.sol +++ b/test/base/GifTest.sol @@ -17,11 +17,10 @@ import {Timestamp} from "../../contracts/type/Timestamp.sol"; import {IAccess} from "../../contracts/authorization/IAccess.sol"; import {IAccessAdmin} from "../../contracts/authorization/IAccessAdmin.sol"; -import {BasicDistributionAuthorization} from "../../contracts/distribution/BasicDistributionAuthorization.sol"; +import {SimpleDistributionAuthorization} from "../../contracts/examples/unpermissioned/SimpleDistributionAuthorization.sol"; import {BasicOracleAuthorization} from "../../contracts/oracle/BasicOracleAuthorization.sol"; -import {BasicPoolAuthorization} from "../../contracts/pool/BasicPoolAuthorization.sol"; -import {BasicProductAuthorization} from "../../contracts/product/BasicProductAuthorization.sol"; - +import {SimplePoolAuthorization} from "../../contracts/examples/unpermissioned/SimplePoolAuthorization.sol"; +import {SimpleProductAuthorization} from "../../contracts/examples/unpermissioned/SimpleProductAuthorization.sol"; import {IServiceAuthorization} from "../../contracts/authorization/IServiceAuthorization.sol"; import {RegistryAdmin} from "../../contracts/registry/RegistryAdmin.sol"; import {ReleaseRegistry} from "../../contracts/registry/ReleaseRegistry.sol"; @@ -486,7 +485,7 @@ contract GifTest is GifDeployer { "SimpleProduct", address(token), _getSimpleProductInfo(), - new BasicProductAuthorization(name), + new SimpleProductAuthorization(name), productOwner // initial owner ); vm.stopPrank(); @@ -545,7 +544,7 @@ contract GifTest is GifDeployer { productNftId, address(token), _getDefaultSimplePoolInfo(), - new BasicPoolAuthorization("SimplePool"), + new SimplePoolAuthorization("SimplePool"), poolOwner ); vm.stopPrank(); @@ -578,7 +577,7 @@ contract GifTest is GifDeployer { distribution = new SimpleDistribution( address(registry), productNftId, - new BasicDistributionAuthorization("SimpleDistribution"), + new SimpleDistributionAuthorization("SimpleDistribution"), distributionOwner, address(token)); vm.stopPrank(); diff --git a/test/examples/composeability/PoolWithReinsurance.sol b/test/examples/composeability/PoolWithReinsurance.sol index 2ddc599ee..22884a896 100644 --- a/test/examples/composeability/PoolWithReinsurance.sol +++ b/test/examples/composeability/PoolWithReinsurance.sol @@ -73,7 +73,6 @@ contract PoolWithReinsurance is _initializePolicyHolder(registry); } - function createReinsurance( SimpleProduct product, // product that provides reinsurance uint256 sumInsured // sum insured for reinsurance diff --git a/test/examples/composeability/PoolWithReinsuranceAuthorization.sol b/test/examples/composeability/PoolWithReinsuranceAuthorization.sol index 663d1d186..350fe9e72 100644 --- a/test/examples/composeability/PoolWithReinsuranceAuthorization.sol +++ b/test/examples/composeability/PoolWithReinsuranceAuthorization.sol @@ -6,6 +6,7 @@ import {IAccess} from "../../../contracts/authorization/IAccess.sol"; import {IInstanceLinkedComponent} from "../../../contracts/shared/IInstanceLinkedComponent.sol"; import {IPolicyHolder} from "../../../contracts/shared/IPolicyHolder.sol"; import {BasicPool} from "../../../contracts/pool/BasicPool.sol"; +import {SimplePool} from "../../../contracts/examples/unpermissioned/SimplePool.sol"; import {IPoolComponent} from "../../../contracts/pool/IPoolComponent.sol"; import {CLAIM, POOL, POLICY} from "../../../contracts/type/ObjectType.sol"; import {PUBLIC_ROLE} from "../../../contracts/type/RoleId.sol"; @@ -50,6 +51,7 @@ contract PoolWithReinsuranceAuthorization _authorize(functions, BasicPool.unstake.selector, "unstake"); _authorize(functions, BasicPool.extend.selector, "extend"); _authorize(functions, BasicPool.withdrawBundleFees.selector, "withdrawBundleFees"); + _authorize(functions, SimplePool.approveTokenHandler.selector, "approveTokenHandler"); _authorize(functions, IInstanceLinkedComponent.withdrawFees.selector, "withdrawFees"); // authorize claim service for callback From 77f65bd203c60e85d0771ad18d03b1ead161016c Mon Sep 17 00:00:00 2001 From: Marc Doerflinger Date: Thu, 15 Aug 2024 11:55:27 +0000 Subject: [PATCH 6/9] copy&paste implementation of AccountingService (#599) --- contracts/shared/AccountingService.sol | 254 ++++++++++++++++++ contracts/shared/AccountingServiceManager.sol | 38 +++ contracts/shared/IAccountingService.sol | 44 +++ contracts/type/ObjectType.sol | 8 + 4 files changed, 344 insertions(+) create mode 100644 contracts/shared/AccountingService.sol create mode 100644 contracts/shared/AccountingServiceManager.sol create mode 100644 contracts/shared/IAccountingService.sol diff --git a/contracts/shared/AccountingService.sol b/contracts/shared/AccountingService.sol new file mode 100644 index 000000000..9698115fd --- /dev/null +++ b/contracts/shared/AccountingService.sol @@ -0,0 +1,254 @@ +// SPDX-License-Identifier: Apache-2.0 +pragma solidity ^0.8.20; + +import {IERC20Metadata} from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol"; + +import {AccessAdmin} from "../authorization/AccessAdmin.sol"; +import {Amount, AmountLib} from "../type/Amount.sol"; +import {ComponentVerifyingService} from "../shared/ComponentVerifyingService.sol"; +import {ContractLib} from "../shared/ContractLib.sol"; +import {Fee, FeeLib} from "../type/Fee.sol"; +import {IAccountingService} from "./IAccountingService.sol"; +import {IComponent} from "../shared/IComponent.sol"; +import {IComponents} from "../instance/module/IComponents.sol"; +import {IComponentService} from "./IComponentService.sol"; +import {IInstance} from "../instance/IInstance.sol"; +import {IInstanceLinkedComponent} from "./IInstanceLinkedComponent.sol"; +import {InstanceAdmin} from "../instance/InstanceAdmin.sol"; +import {InstanceReader} from "../instance/InstanceReader.sol"; +import {InstanceStore} from "../instance/InstanceStore.sol"; +import {IInstanceService} from "../instance/IInstanceService.sol"; +import {IPoolComponent} from "../pool/IPoolComponent.sol"; +import {IProductComponent} from "../product/IProductComponent.sol"; +import {IRegisterable} from "../shared/IRegisterable.sol"; +import {IRegistry} from "../registry/IRegistry.sol"; +import {IRegistryService} from "../registry/IRegistryService.sol"; + +import {Amount, AmountLib} from "../type/Amount.sol"; +import {ContractLib} from "../shared/ContractLib.sol"; +import {Fee, FeeLib} from "../type/Fee.sol"; +import {KEEP_STATE} from "../type/StateId.sol"; +import {NftId} from "../type/NftId.sol"; +import {ObjectType, REGISTRY, ACCOUNTING, BUNDLE, COMPONENT, DISTRIBUTION, DISTRIBUTOR, INSTANCE, ORACLE, POOL, PRODUCT, STAKING} from "../type/ObjectType.sol"; +import {Service} from "../shared/Service.sol"; +import {TokenHandler} from "../shared/TokenHandler.sol"; +import {TokenHandlerDeployerLib} from "../shared/TokenHandlerDeployerLib.sol"; +import {VersionPart} from "../type/Version.sol"; + + +contract AccountingService is + Service, + IAccountingService +{ + bool private constant INCREASE = true; + bool private constant DECREASE = false; + + function _initialize( + address owner, + bytes memory data + ) + internal + virtual override + initializer() + { + ( + address registryAddress, + address authority + ) = abi.decode(data, (address, address)); + + _initializeService(registryAddress, authority, owner); + + _registerInterface(type(IAccountingService).interfaceId); + } + + function increaseProductFees( + InstanceStore instanceStore, + NftId productNftId, + Amount feeAmount + ) + external + virtual + // TODO re-enable once role granting is stable and fixed + // restricted() + { + _checkNftType(productNftId, PRODUCT()); + _changeTargetBalance(INCREASE, instanceStore, productNftId, AmountLib.zero(), feeAmount); + } + + + function decreaseProductFees(InstanceStore instanceStore, NftId productNftId, Amount feeAmount) + external + virtual + // TODO re-enable once role granting is stable and fixed + // restricted() + { + _checkNftType(productNftId, PRODUCT()); + _changeTargetBalance(DECREASE, instanceStore, productNftId, AmountLib.zero(), feeAmount); + } + + function increaseDistributionBalance( + InstanceStore instanceStore, + NftId distributionNftId, + Amount amount, + Amount feeAmount + ) + external + virtual + // TODO re-enable once role granting is stable and fixed + // restricted() + { + _checkNftType(distributionNftId, DISTRIBUTION()); + _changeTargetBalance(INCREASE, instanceStore, distributionNftId, amount, feeAmount); + } + + + function decreaseDistributionBalance( + InstanceStore instanceStore, + NftId distributionNftId, + Amount amount, + Amount feeAmount + ) + external + virtual + // TODO re-enable once role granting is stable and fixed + // restricted() + { + _checkNftType(distributionNftId, DISTRIBUTION()); + _changeTargetBalance(DECREASE, instanceStore, distributionNftId, amount, feeAmount); + } + + function increaseDistributorBalance( + InstanceStore instanceStore, + NftId distributorNftId, + Amount amount, + Amount feeAmount + ) + external + virtual + // TODO re-enable once role granting is stable and fixed + // restricted() + { + _checkNftType(distributorNftId, DISTRIBUTOR()); + _changeTargetBalance(INCREASE, instanceStore, distributorNftId, amount, feeAmount); + } + + function decreaseDistributorBalance( + InstanceStore instanceStore, + NftId distributorNftId, + Amount amount, + Amount feeAmount + ) + external + virtual + // TODO re-enable once role granting is stable and fixed + // restricted() + { + _checkNftType(distributorNftId, DISTRIBUTOR()); + _changeTargetBalance(DECREASE, instanceStore, distributorNftId, amount, feeAmount); + } + + function increasePoolBalance( + InstanceStore instanceStore, + NftId poolNftId, + Amount amount, + Amount feeAmount + ) + public + virtual + // TODO re-enable once role granting is stable and fixed + // restricted() + { + _checkNftType(poolNftId, POOL()); + _changeTargetBalance(INCREASE, instanceStore, poolNftId, amount, feeAmount); + } + + function decreasePoolBalance( + InstanceStore instanceStore, + NftId poolNftId, + Amount amount, + Amount feeAmount + ) + public + virtual + // TODO re-enable once role granting is stable and fixed + // restricted() + { + _checkNftType(poolNftId, POOL()); + _changeTargetBalance(DECREASE, instanceStore, poolNftId, amount, feeAmount); + } + + function increaseBundleBalance( + InstanceStore instanceStore, + NftId bundleNftId, + Amount amount, + Amount feeAmount + ) + external + virtual + // TODO re-enable once role granting is stable and fixed + // restricted() + { + _checkNftType(bundleNftId, BUNDLE()); + _changeTargetBalance(INCREASE, instanceStore, bundleNftId, amount, feeAmount); + } + + function decreaseBundleBalance( + InstanceStore instanceStore, + NftId bundleNftId, + Amount amount, + Amount feeAmount + ) + external + virtual + // TODO re-enable once role granting is stable and fixed + // restricted() + { + _checkNftType(bundleNftId, BUNDLE()); + _changeTargetBalance(DECREASE, instanceStore, bundleNftId, amount, feeAmount); + } + + + //-------- internal functions ------------------------------------------// + + function _changeTargetBalance( + bool increase, + InstanceStore instanceStore, + NftId targetNftId, + Amount amount, + Amount feeAmount + ) + internal + virtual + { + Amount totalAmount = amount + feeAmount; + + if(increase) { + if(totalAmount.gtz()) { instanceStore.increaseBalance(targetNftId, totalAmount); } + if(feeAmount.gtz()) { instanceStore.increaseFees(targetNftId, feeAmount); } + } else { + if(totalAmount.gtz()) { instanceStore.decreaseBalance(targetNftId, totalAmount); } + if(feeAmount.gtz()) { instanceStore.decreaseFees(targetNftId, feeAmount); } + } + } + + + function _logUpdateFee(NftId productNftId, string memory name, Fee memory feeBefore, Fee memory feeAfter) + internal + virtual + { + emit LogComponentServiceUpdateFee( + productNftId, + name, + feeBefore.fractionalFee, + feeBefore.fixedFee, + feeAfter.fractionalFee, + feeAfter.fixedFee + ); + } + + function _getDomain() internal pure virtual override returns(ObjectType) { + return ACCOUNTING(); + } + + +} \ No newline at end of file diff --git a/contracts/shared/AccountingServiceManager.sol b/contracts/shared/AccountingServiceManager.sol new file mode 100644 index 000000000..8b33fff42 --- /dev/null +++ b/contracts/shared/AccountingServiceManager.sol @@ -0,0 +1,38 @@ +// SPDX-License-Identifier: Apache-2.0 +pragma solidity ^0.8.20; + +import {AccountingService} from "./AccountingService.sol"; +import {IVersionable} from "../upgradeability/IVersionable.sol"; +import {ProxyManager} from "../upgradeability/ProxyManager.sol"; + +contract AccountingServiceManager is ProxyManager { + + AccountingService private _accountingService; + + /// @dev initializes proxy manager with service implementation + constructor( + address authority, + address registry, + bytes32 salt + ) + { + AccountingService svc = new AccountingService(); + bytes memory data = abi.encode(registry, authority); + IVersionable versionable = initialize( + registry, + address(svc), + data, + salt); + + _accountingService = AccountingService(address(versionable)); + } + + //--- view functions ----------------------------------------------------// + function getAccountingService() + external + view + returns (AccountingService) + { + return _accountingService; + } +} \ No newline at end of file diff --git a/contracts/shared/IAccountingService.sol b/contracts/shared/IAccountingService.sol new file mode 100644 index 000000000..116479c7e --- /dev/null +++ b/contracts/shared/IAccountingService.sol @@ -0,0 +1,44 @@ +// SPDX-License-Identifier: Apache-2.0 +pragma solidity ^0.8.20; + +import {IERC20Metadata} from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol"; + +import {Amount} from "../type/Amount.sol"; +import {Fee} from "../type/Fee.sol"; +import {InstanceStore} from "../instance/InstanceStore.sol"; +import {IService} from "../shared/IService.sol"; +import {NftId} from "../type/NftId.sol"; +import {ObjectType} from "../type/ObjectType.sol"; +import {UFixed} from "../type/UFixed.sol"; +import {VersionPart} from "../type/Version.sol"; + +/// @dev component base class +/// component examples are staking, product, distribution, pool and oracle +interface IAccountingService is + IService +{ + event LogComponentServiceUpdateFee( + NftId nftId, + string feeName, + UFixed previousFractionalFee, + uint256 previousFixedFee, + UFixed newFractionalFee, + uint256 newFixedFee + ); + + function increaseProductFees(InstanceStore instanceStore, NftId productNftId, Amount feeAmount) external; + function decreaseProductFees(InstanceStore instanceStore, NftId productNftId, Amount feeAmount) external; + + function increaseDistributionBalance(InstanceStore instanceStore, NftId distributionNftId, Amount amount, Amount feeAmount) external; + function decreaseDistributionBalance(InstanceStore instanceStore, NftId distributionNftId, Amount amount, Amount feeAmount) external; + + function increaseDistributorBalance(InstanceStore instanceStore, NftId distributorNftId, Amount amount, Amount feeAmount) external; + function decreaseDistributorBalance(InstanceStore instanceStore, NftId distributorNftId, Amount amount, Amount feeAmount) external; + + function increasePoolBalance(InstanceStore instanceStore, NftId poolNftId, Amount amount, Amount feeAmount) external; + function decreasePoolBalance(InstanceStore instanceStore, NftId poolNftId, Amount amount, Amount feeAmount) external; + + function increaseBundleBalance(InstanceStore instanceStore, NftId bundleNftId, Amount amount, Amount feeAmount) external; + function decreaseBundleBalance(InstanceStore instanceStore, NftId bundleNftId, Amount amount, Amount feeAmount) external; + +} \ No newline at end of file diff --git a/contracts/type/ObjectType.sol b/contracts/type/ObjectType.sol index c5bd083c8..dafd95e78 100644 --- a/contracts/type/ObjectType.sol +++ b/contracts/type/ObjectType.sol @@ -120,6 +120,12 @@ function STAKE() pure returns (ObjectType) { return ObjectType.wrap(33); } +// TODO: change id for accounting +function ACCOUNTING() pure returns (ObjectType) { + return ObjectType.wrap(34); +} + + /// @dev Object type that includes any other object type. /// Note that eq()/'==' does not take this property into account. function ALL() pure returns (ObjectType) { @@ -204,6 +210,8 @@ library ObjectTypeLib { return "Bundle"; } else if (objectType == RISK()) { return "Risk"; + } else if (objectType == ACCOUNTING()) { + return "Accounting"; } // fallback: ObjectType From eb0d8bec61bdc59e954c009d0e0234ef2d5d5e4d Mon Sep 17 00:00:00 2001 From: Marc Doerflinger Date: Thu, 15 Aug 2024 12:41:55 +0000 Subject: [PATCH 7/9] deployment of AccountingService (#599) --- .../AccountingService.sol | 29 +-------- .../AccountingServiceManager.sol | 0 .../IAccountingService.sol | 4 -- contracts/accounting/README.adoc | 8 +++ contracts/registry/ServiceAuthorizationV3.sol | 25 ++++---- docs/modules/api/nav.adoc | 1 + scripts/deploy_gif.ts | 4 ++ scripts/libs/services.ts | 61 ++++++++++++++++++- test/TestDeployAll.t.sol | 6 +- test/base/GifDeployer.sol | 10 +++ test/examples/verifying/VerifyingPool.t.sol | 6 +- 11 files changed, 105 insertions(+), 49 deletions(-) rename contracts/{shared => accounting}/AccountingService.sol (79%) rename contracts/{shared => accounting}/AccountingServiceManager.sol (100%) rename contracts/{shared => accounting}/IAccountingService.sol (89%) create mode 100644 contracts/accounting/README.adoc diff --git a/contracts/shared/AccountingService.sol b/contracts/accounting/AccountingService.sol similarity index 79% rename from contracts/shared/AccountingService.sol rename to contracts/accounting/AccountingService.sol index 9698115fd..478c1123b 100644 --- a/contracts/shared/AccountingService.sol +++ b/contracts/accounting/AccountingService.sol @@ -1,39 +1,16 @@ // SPDX-License-Identifier: Apache-2.0 pragma solidity ^0.8.20; -import {IERC20Metadata} from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol"; - -import {AccessAdmin} from "../authorization/AccessAdmin.sol"; import {Amount, AmountLib} from "../type/Amount.sol"; -import {ComponentVerifyingService} from "../shared/ComponentVerifyingService.sol"; -import {ContractLib} from "../shared/ContractLib.sol"; -import {Fee, FeeLib} from "../type/Fee.sol"; +import {Fee} from "../type/Fee.sol"; import {IAccountingService} from "./IAccountingService.sol"; -import {IComponent} from "../shared/IComponent.sol"; -import {IComponents} from "../instance/module/IComponents.sol"; -import {IComponentService} from "./IComponentService.sol"; -import {IInstance} from "../instance/IInstance.sol"; -import {IInstanceLinkedComponent} from "./IInstanceLinkedComponent.sol"; -import {InstanceAdmin} from "../instance/InstanceAdmin.sol"; -import {InstanceReader} from "../instance/InstanceReader.sol"; import {InstanceStore} from "../instance/InstanceStore.sol"; -import {IInstanceService} from "../instance/IInstanceService.sol"; -import {IPoolComponent} from "../pool/IPoolComponent.sol"; -import {IProductComponent} from "../product/IProductComponent.sol"; -import {IRegisterable} from "../shared/IRegisterable.sol"; -import {IRegistry} from "../registry/IRegistry.sol"; -import {IRegistryService} from "../registry/IRegistryService.sol"; import {Amount, AmountLib} from "../type/Amount.sol"; -import {ContractLib} from "../shared/ContractLib.sol"; -import {Fee, FeeLib} from "../type/Fee.sol"; -import {KEEP_STATE} from "../type/StateId.sol"; +import {Fee} from "../type/Fee.sol"; import {NftId} from "../type/NftId.sol"; -import {ObjectType, REGISTRY, ACCOUNTING, BUNDLE, COMPONENT, DISTRIBUTION, DISTRIBUTOR, INSTANCE, ORACLE, POOL, PRODUCT, STAKING} from "../type/ObjectType.sol"; +import {ObjectType, ACCOUNTING, BUNDLE, DISTRIBUTION, DISTRIBUTOR, POOL, PRODUCT} from "../type/ObjectType.sol"; import {Service} from "../shared/Service.sol"; -import {TokenHandler} from "../shared/TokenHandler.sol"; -import {TokenHandlerDeployerLib} from "../shared/TokenHandlerDeployerLib.sol"; -import {VersionPart} from "../type/Version.sol"; contract AccountingService is diff --git a/contracts/shared/AccountingServiceManager.sol b/contracts/accounting/AccountingServiceManager.sol similarity index 100% rename from contracts/shared/AccountingServiceManager.sol rename to contracts/accounting/AccountingServiceManager.sol diff --git a/contracts/shared/IAccountingService.sol b/contracts/accounting/IAccountingService.sol similarity index 89% rename from contracts/shared/IAccountingService.sol rename to contracts/accounting/IAccountingService.sol index 116479c7e..2ca29fca7 100644 --- a/contracts/shared/IAccountingService.sol +++ b/contracts/accounting/IAccountingService.sol @@ -1,16 +1,12 @@ // SPDX-License-Identifier: Apache-2.0 pragma solidity ^0.8.20; -import {IERC20Metadata} from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol"; import {Amount} from "../type/Amount.sol"; -import {Fee} from "../type/Fee.sol"; import {InstanceStore} from "../instance/InstanceStore.sol"; import {IService} from "../shared/IService.sol"; import {NftId} from "../type/NftId.sol"; -import {ObjectType} from "../type/ObjectType.sol"; import {UFixed} from "../type/UFixed.sol"; -import {VersionPart} from "../type/Version.sol"; /// @dev component base class /// component examples are staking, product, distribution, pool and oracle diff --git a/contracts/accounting/README.adoc b/contracts/accounting/README.adoc new file mode 100644 index 000000000..2c4752b12 --- /dev/null +++ b/contracts/accounting/README.adoc @@ -0,0 +1,8 @@ += Accounting + +Contains contracts related to accounting and financial operations. + +== Contracts + +{{AccountingService}} +{{AccountingServiceManager}} diff --git a/contracts/registry/ServiceAuthorizationV3.sol b/contracts/registry/ServiceAuthorizationV3.sol index 4a14f2475..3d72d52cd 100644 --- a/contracts/registry/ServiceAuthorizationV3.sol +++ b/contracts/registry/ServiceAuthorizationV3.sol @@ -2,14 +2,12 @@ pragma solidity ^0.8.20; import { - ALL, REGISTRY, RISK, ORACLE, POOL, INSTANCE, COMPONENT, DISTRIBUTION, DISTRIBUTOR, APPLICATION, POLICY, CLAIM, BUNDLE, STAKING, PRICE + ALL, ACCOUNTING, REGISTRY, RISK, ORACLE, POOL, INSTANCE, COMPONENT, DISTRIBUTION, APPLICATION, POLICY, CLAIM, BUNDLE, STAKING, PRICE } from "../../contracts/type/ObjectType.sol"; import {IAccess} from "../authorization/IAccess.sol"; import {IBundleService} from "../pool/IBundleService.sol"; import {IDistributionService} from "../distribution/IDistributionService.sol"; -import {IInstanceService} from "../instance/IInstanceService.sol"; -import {InstanceAdmin} from "../instance/InstanceAdmin.sol"; import {IPoolService} from "../pool/IPoolService.sol"; import {IStakingService} from "../staking/IStakingService.sol"; import {IRegistryService} from "./IRegistryService.sol"; @@ -31,16 +29,17 @@ contract ServiceAuthorizationV3 _authorizeDomain(REGISTRY(), address(1)); _authorizeDomain(STAKING(), address(2)); _authorizeDomain(INSTANCE(), address(3)); - _authorizeDomain(COMPONENT(), address(4)); - _authorizeDomain(DISTRIBUTION(), address(5)); - _authorizeDomain(PRICE(), address(6)); - _authorizeDomain(BUNDLE(), address(7)); - _authorizeDomain(POOL(), address(8)); - _authorizeDomain(ORACLE(), address(9)); - _authorizeDomain(RISK(), address(10)); - _authorizeDomain(POLICY(), address(11)); - _authorizeDomain(CLAIM(), address(12)); - _authorizeDomain(APPLICATION(), address(13)); + _authorizeDomain(ACCOUNTING(), address(4)); + _authorizeDomain(COMPONENT(), address(5)); + _authorizeDomain(DISTRIBUTION(), address(6)); + _authorizeDomain(PRICE(), address(7)); + _authorizeDomain(BUNDLE(), address(8)); + _authorizeDomain(POOL(), address(9)); + _authorizeDomain(ORACLE(), address(10)); + _authorizeDomain(RISK(), address(11)); + _authorizeDomain(POLICY(), address(12)); + _authorizeDomain(CLAIM(), address(13)); + _authorizeDomain(APPLICATION(), address(14)); } diff --git a/docs/modules/api/nav.adoc b/docs/modules/api/nav.adoc index 06a3c6dfa..2ae341243 100644 --- a/docs/modules/api/nav.adoc +++ b/docs/modules/api/nav.adoc @@ -1,4 +1,5 @@ .Contracts +* xref:accounting.adoc[accounting] * xref:authorization.adoc[authorization] * xref:distribution.adoc[distribution] * xref:instance.adoc[instance] diff --git a/scripts/deploy_gif.ts b/scripts/deploy_gif.ts index f88f9fba9..7cdfa5cc5 100644 --- a/scripts/deploy_gif.ts +++ b/scripts/deploy_gif.ts @@ -180,6 +180,10 @@ function printAddresses( addresses += `INSTANCE_SERVICE_ADDRESS=${services.instanceServiceAddress}\n`; addresses += `INSTANCE_SERVICE_NFT_ID=${services.instanceServiceNftId}\n`; + addresses += `ACCOUNTING_SERVICE_MANAGER_ADDRESS=${services.accountingServiceManagerAddress}\n`; + addresses += `ACCOUNTING_SERVICE_ADDRESS=${services.accountingServiceAddress}\n`; + addresses += `ACCOUNTING_SERVICE_NFT_ID=${services.accountingServiceNftId}\n`; + addresses += `COMPONENT_SERVICE_MANAGER_ADDRESS=${services.componentServiceManagerAddress}\n`; addresses += `COMPONENT_SERVICE_ADDRESS=${services.componentServiceAddress}\n`; addresses += `COMPONENT_SERVICE_NFT_ID=${services.componentServiceNftId}\n`; diff --git a/scripts/libs/services.ts b/scripts/libs/services.ts index 0875a6234..769650bb1 100644 --- a/scripts/libs/services.ts +++ b/scripts/libs/services.ts @@ -3,6 +3,9 @@ import { getImplementationAddress } from '@openzeppelin/upgrades-core'; import { AddressLike, BytesLike, Signer, id } from "ethers"; import { ethers as hhEthers } from "hardhat"; import { + AccountingService, + AccountingServiceManager, + AccountingService__factory, ApplicationService, ApplicationServiceManager, ApplicationService__factory, BundleService, BundleServiceManager, BundleService__factory, ClaimService, ClaimServiceManager, ClaimService__factory, @@ -13,9 +16,11 @@ import { PolicyService, PolicyServiceManager, PolicyService__factory, PoolService, PoolServiceManager, PoolService__factory, PricingService, PricingServiceManager, PricingService__factory, + RegistryService, + RegistryServiceManager, + RegistryService__factory, RiskService, RiskServiceManager, RiskService__factory, - RegistryService, RegistryServiceManager, RegistryService__factory, - StakingService, StakingServiceManager, StakingService__factory, + StakingService, StakingServiceManager, StakingService__factory } from "../../typechain-types"; import { logger } from "../logger"; import { deployContract } from "./deployment"; @@ -37,6 +42,11 @@ export type ServiceAddresses = { instanceService: InstanceService, instanceServiceManagerAddress: AddressLike, + accountingServiceNftId: string, + accountingServiceAddress: AddressLike, + accountingService: AccountingService, + accountingServiceManagerAddress: AddressLike, + componentServiceNftId: string, componentServiceAddress: AddressLike, componentService: ComponentService, @@ -229,6 +239,48 @@ export async function deployAndRegisterServices(owner: Signer, registry: Registr ); logger.info(`instanceServiceManager deployed - instanceServiceAddress: ${instanceServiceAddress} instanceServiceManagerAddress: ${instanceServiceManagerAddress} nftId: ${instanceServiceNfdId}`); + logger.info("-------- accounting service --------"); + const { address: accountingServiceManagerAddress, contract: accountingServiceManagerBaseContract, } = await deployContract( + "AccountingServiceManager", + owner, + [ + authority, + registry.registryAddress, + release.salt + ], + { libraries: { + AmountLib: libraries.amountLibAddress, + ContractLib: libraries.contractLibAddress, + NftIdLib: libraries.nftIdLibAddress, + RoleIdLib: libraries.roleIdLibAddress, + TimestampLib: libraries.timestampLibAddress, + VersionLib: libraries.versionLibAddress, + VersionPartLib: libraries.versionPartLibAddress, + }}); + + const accountingServiceManager = accountingServiceManagerBaseContract as AccountingServiceManager; + const accountingServiceAddress = await accountingServiceManager.getAccountingService(); + const accountingService = AccountingService__factory.connect(accountingServiceAddress, owner); + + // verify service implementation + prepareVerificationData( + "AccountingService", + await getImplementationAddress(hhEthers.provider, await accountingServiceManager.getProxy()), + [], + undefined); + + const rcptAcct = await executeTx( + async () => await releaseRegistry.registerService(accountingServiceAddress, getTxOpts()), + "registerService - accountingService" + ); + const logRegistrationInfoAcct = getFieldFromTxRcptLogs(rcptAcct!, registry.registry.interface, "LogRegistration", "nftId"); + const accountingServiceNfdId = (logRegistrationInfoAcct as unknown); + await executeTx( + async () => await accountingServiceManager.linkToProxy(getTxOpts()), + "linkToProxy - accountingService" + ); + logger.info(`accountingServiceManager deployed - accountingServiceAddress: ${accountingServiceAddress} accountingServiceManagerAddress: ${accountingServiceManagerAddress} nftId: ${accountingServiceNfdId}`); + logger.info("-------- component service --------"); const { address: componentServiceManagerAddress, contract: componentServiceManagerBaseContract, } = await deployContract( "ComponentServiceManager", @@ -678,6 +730,11 @@ export async function deployAndRegisterServices(owner: Signer, registry: Registr instanceService: instanceService, instanceServiceManagerAddress: instanceServiceManagerAddress, + accountingServiceNftId: accountingServiceNfdId as string, + accountingServiceAddress: accountingServiceAddress, + accountingService: accountingService, + accountingServiceManagerAddress: accountingServiceManagerAddress, + componentServiceNftId: componentServiceNftId as string, componentServiceAddress: componentServiceAddress, componentService: componentService, diff --git a/test/TestDeployAll.t.sol b/test/TestDeployAll.t.sol index d275eb61f..3a854fb45 100644 --- a/test/TestDeployAll.t.sol +++ b/test/TestDeployAll.t.sol @@ -69,7 +69,7 @@ contract TestDeployAll is GifTest { function test_deployServicesOverview() public { - assertEq(registry.getObjectCount(), 24, "invalid object count for base setup"); + assertEq(registry.getObjectCount(), 25, "invalid object count for base setup"); // validate registry service assertTrue(registry.getNftIdForAddress(address(registryService)).eq(registryServiceNftId), "registry service nft does not match"); @@ -79,6 +79,10 @@ contract TestDeployAll is GifTest { assertTrue(registry.getNftIdForAddress(address(stakingService)).eq(stakingServiceNftId), "staking service nft does not match"); assertTrue(address(stakingServiceManager) != address(0), "staking service manager is zero address"); + // validate accounting service + assertTrue(registry.getNftIdForAddress(address(accountingService)).eq(accountingServiceNftId), "accounting service nft does not match"); + assertTrue(address(accountingServiceManager) != address(0), "accounting service manager is zero address"); + // validate instance service assertTrue(registry.getNftIdForAddress(address(instanceService)).eq(instanceServiceNftId), "instance service nft does not match"); assertTrue(address(instanceServiceManager) != address(0), "instance service manager is zero address"); diff --git a/test/base/GifDeployer.sol b/test/base/GifDeployer.sol index 525840bb9..921b9537c 100644 --- a/test/base/GifDeployer.sol +++ b/test/base/GifDeployer.sol @@ -29,6 +29,8 @@ import {TokenRegistry} from "../../contracts/registry/TokenRegistry.sol"; // service and proxy contracts import {IService} from "../../contracts/shared/IService.sol"; +import {AccountingService} from "../../contracts/accounting/AccountingService.sol"; +import {AccountingServiceManager} from "../../contracts/accounting/AccountingServiceManager.sol"; import {ApplicationService} from "../../contracts/product/ApplicationService.sol"; import {ApplicationServiceManager} from "../../contracts/product/ApplicationServiceManager.sol"; import {BundleService} from "../../contracts/pool/BundleService.sol"; @@ -116,6 +118,10 @@ contract GifDeployer is Test { PolicyService public policyService; NftId public policyServiceNftId; + AccountingServiceManager public accountingServiceManager; + AccountingService public accountingService; + NftId public accountingServiceNftId; + mapping(ObjectType domain => DeployedServiceInfo info) public serviceForDomain; function deployCore( @@ -312,6 +318,10 @@ contract GifDeployer is Test { instanceService = instanceServiceManager.getInstanceService(); instanceServiceNftId = _registerService(releaseRegistry, instanceServiceManager, instanceService); + accountingServiceManager = new AccountingServiceManager{salt: salt}(authority, registryAddress, salt); + accountingService = accountingServiceManager.getAccountingService(); + accountingServiceNftId = _registerService(releaseRegistry, accountingServiceManager, accountingService); + componentServiceManager = new ComponentServiceManager{salt: salt}(authority, registryAddress, salt); componentService = componentServiceManager.getComponentService(); componentServiceNftId = _registerService(releaseRegistry, componentServiceManager, componentService); diff --git a/test/examples/verifying/VerifyingPool.t.sol b/test/examples/verifying/VerifyingPool.t.sol index 3ba7483b5..4a5a4f8b7 100644 --- a/test/examples/verifying/VerifyingPool.t.sol +++ b/test/examples/verifying/VerifyingPool.t.sol @@ -64,8 +64,8 @@ contract VerifyingPoolTest is GifTest { NftId bundle2NftId = vPool.bundleTwoNftId(); Amount expectedCollateralizationAmount = AmountLib.toAmount(1000); - NftId expectedPolicy1NftId = NftIdLib.toNftId(233133705); - NftId expectedPolicy2NftId = NftIdLib.toNftId(243133705); + NftId expectedPolicy1NftId = NftIdLib.toNftId(243133705); + NftId expectedPolicy2NftId = NftIdLib.toNftId(253133705); // WHEN just setUp vm.startPrank(customer); @@ -95,7 +95,7 @@ contract VerifyingPoolTest is GifTest { // GIVEN NftId bundle1NftId = vPool.bundleOneNftId(); NftId bundle2NftId = vPool.bundleTwoNftId(); - NftId expectedPolicyNftId = NftIdLib.toNftId(233133705); + NftId expectedPolicyNftId = NftIdLib.toNftId(243133705); // WHEN + THEN vm.startPrank(customer); From 66728e3411c6bf46614ae5459495793bf6f2522a Mon Sep 17 00:00:00 2001 From: Marc Doerflinger Date: Thu, 15 Aug 2024 13:07:48 +0000 Subject: [PATCH 8/9] usee AccountingService instead of ComponentService (#599) --- contracts/accounting/AccountingService.sol | 13 ++ contracts/accounting/IAccountingService.sol | 2 + .../distribution/DistributionService.sol | 17 +- .../instance/InstanceAuthorizationV3.sol | 16 +- contracts/pool/BundleService.sol | 16 +- contracts/pool/PoolService.sol | 19 +- contracts/product/PolicyService.sol | 7 +- contracts/shared/ComponentService.sol | 183 +----------------- contracts/shared/IComponentService.sol | 20 +- 9 files changed, 66 insertions(+), 227 deletions(-) diff --git a/contracts/accounting/AccountingService.sol b/contracts/accounting/AccountingService.sol index 478c1123b..a415b96fd 100644 --- a/contracts/accounting/AccountingService.sol +++ b/contracts/accounting/AccountingService.sol @@ -38,6 +38,19 @@ contract AccountingService is _registerInterface(type(IAccountingService).interfaceId); } + function decreaseComponentFees( + InstanceStore instanceStore, + NftId componentNftId, + Amount feeAmount + ) + external + virtual + { + // TODO: validate that the nft belongs to a component + _changeTargetBalance(DECREASE, instanceStore, componentNftId, AmountLib.zero(), feeAmount); + } + + function increaseProductFees( InstanceStore instanceStore, NftId productNftId, diff --git a/contracts/accounting/IAccountingService.sol b/contracts/accounting/IAccountingService.sol index 2ca29fca7..7c0563707 100644 --- a/contracts/accounting/IAccountingService.sol +++ b/contracts/accounting/IAccountingService.sol @@ -22,6 +22,8 @@ interface IAccountingService is uint256 newFixedFee ); + function decreaseComponentFees(InstanceStore instanceStore, NftId componentNftId, Amount feeAmount) external; + function increaseProductFees(InstanceStore instanceStore, NftId productNftId, Amount feeAmount) external; function decreaseProductFees(InstanceStore instanceStore, NftId productNftId, Amount feeAmount) external; diff --git a/contracts/distribution/DistributionService.sol b/contracts/distribution/DistributionService.sol index ac05a2e5c..803a6196e 100644 --- a/contracts/distribution/DistributionService.sol +++ b/contracts/distribution/DistributionService.sol @@ -1,6 +1,7 @@ // SPDX-License-Identifier: Apache-2.0 pragma solidity ^0.8.20; +import {IAccountingService} from "../accounting/IAccountingService.sol"; import {IRegistry} from "../registry/IRegistry.sol"; import {IInstance} from "../instance/IInstance.sol"; import {IComponentService} from "../shared/IComponentService.sol"; @@ -13,7 +14,7 @@ import {IPolicy} from "../instance/module/IPolicy.sol"; import {Amount, AmountLib} from "../type/Amount.sol"; import {NftId, NftIdLib} from "../type/NftId.sol"; import {KEEP_STATE} from "../type/StateId.sol"; -import {ObjectType, COMPONENT, DISTRIBUTION, INSTANCE, DISTRIBUTION, DISTRIBUTOR, REGISTRY} from "../type/ObjectType.sol"; +import {ObjectType, ACCOUNTING, COMPONENT, DISTRIBUTION, INSTANCE, DISTRIBUTION, DISTRIBUTOR, REGISTRY} from "../type/ObjectType.sol"; import {ComponentVerifyingService} from "../shared/ComponentVerifyingService.sol"; import {IDistributionService} from "./IDistributionService.sol"; import {UFixed} from "../type/UFixed.sol"; @@ -28,6 +29,7 @@ contract DistributionService is ComponentVerifyingService, IDistributionService { + IAccountingService private _accountingService; IComponentService private _componentService; IInstanceService private _instanceService; IRegistryService private _registryService; @@ -47,6 +49,7 @@ contract DistributionService is _initializeService(registryAddress, authority, owner); + _accountingService = IAccountingService(_getServiceAddress(ACCOUNTING())); _componentService = IComponentService(_getServiceAddress(COMPONENT())); _instanceService = IInstanceService(_getServiceAddress(INSTANCE())); _registryService = IRegistryService(_getServiceAddress(REGISTRY())); @@ -70,7 +73,7 @@ contract DistributionService is returns (DistributorType distributorType) { (NftId distributionNftId,, IInstance instance) = _getAndVerifyActiveComponent(DISTRIBUTION()); - InstanceReader instanceReader = instance.getInstanceReader(); + // InstanceReader instanceReader = instance.getInstanceReader(); { NftId productNftId = _getProductNftId(distributionNftId); @@ -255,12 +258,12 @@ contract DistributionService is // increase distribution balance by commission amount and distribution owner fee Amount commissionAmount = premium.commissionAmount; - _componentService.increaseDistributionBalance(store, distributionNftId, commissionAmount, distributionOwnerFee); + _accountingService.increaseDistributionBalance(store, distributionNftId, commissionAmount, distributionOwnerFee); // update book keeping for referral info IDistribution.ReferralInfo memory referralInfo = reader.getReferralInfo(referralId); - _componentService.increaseDistributorBalance(store, referralInfo.distributorNftId, AmountLib.zero(), commissionAmount); + _accountingService.increaseDistributorBalance(store, referralInfo.distributorNftId, AmountLib.zero(), commissionAmount); // update book keeping for distributor info IDistribution.DistributorInfo memory distributorInfo = reader.getDistributorInfo(referralInfo.distributorNftId); @@ -268,7 +271,7 @@ contract DistributionService is store.updateDistributor(referralInfo.distributorNftId, distributorInfo, KEEP_STATE()); } else { // increase distribution balance by distribution owner fee - _componentService.increaseDistributionBalance(store, distributionNftId, AmountLib.zero(), distributionOwnerFee); + _accountingService.increaseDistributionBalance(store, distributionNftId, AmountLib.zero(), distributionOwnerFee); } } @@ -302,9 +305,9 @@ contract DistributionService is { InstanceStore store = instance.getInstanceStore(); // decrease fee counter for distribution balance - _componentService.decreaseDistributionBalance(store, distributionNftId, withdrawnAmount, AmountLib.zero()); + _accountingService.decreaseDistributionBalance(store, distributionNftId, withdrawnAmount, AmountLib.zero()); // decrease fee counter for distributor fee - _componentService.decreaseDistributorBalance(store, distributorNftId, AmountLib.zero(), withdrawnAmount); + _accountingService.decreaseDistributorBalance(store, distributorNftId, AmountLib.zero(), withdrawnAmount); } // transfer amount to distributor diff --git a/contracts/instance/InstanceAuthorizationV3.sol b/contracts/instance/InstanceAuthorizationV3.sol index 09678d337..b72796b12 100644 --- a/contracts/instance/InstanceAuthorizationV3.sol +++ b/contracts/instance/InstanceAuthorizationV3.sol @@ -2,7 +2,7 @@ pragma solidity ^0.8.20; import { - PRODUCT, ORACLE, POOL, INSTANCE, COMPONENT, DISTRIBUTION, APPLICATION, POLICY, CLAIM, BUNDLE, RISK + ACCOUNTING, ORACLE, POOL, INSTANCE, COMPONENT, DISTRIBUTION, APPLICATION, POLICY, CLAIM, BUNDLE, RISK } from "../../contracts/type/ObjectType.sol"; import {BundleSet} from "../instance/BundleSet.sol"; @@ -55,6 +55,7 @@ contract InstanceAuthorizationV3 // service targets relevant to instance _addServiceTargetWithRole(INSTANCE()); + _addServiceTargetWithRole(ACCOUNTING()); _addServiceTargetWithRole(COMPONENT()); _addServiceTargetWithRole(DISTRIBUTION()); _addServiceTargetWithRole(ORACLE()); @@ -145,6 +146,13 @@ contract InstanceAuthorizationV3 { IAccess.FunctionInfo[] storage functions; + // authorize accounting service role + functions = _authorizeForTarget(INSTANCE_STORE_TARGET_NAME, getServiceRole(ACCOUNTING())); + _authorize(functions, InstanceStore.increaseBalance.selector, "increaseBalance"); + _authorize(functions, InstanceStore.decreaseBalance.selector, "decreaseBalance"); + _authorize(functions, InstanceStore.increaseFees.selector, "increaseFees"); + _authorize(functions, InstanceStore.decreaseFees.selector, "decreaseFees"); + // authorize component service role functions = _authorizeForTarget(INSTANCE_STORE_TARGET_NAME, getServiceRole(COMPONENT())); _authorize(functions, InstanceStore.createComponent.selector, "createComponent"); @@ -152,11 +160,7 @@ contract InstanceAuthorizationV3 _authorize(functions, InstanceStore.createPool.selector, "createPool"); _authorize(functions, InstanceStore.createProduct.selector, "createProduct"); _authorize(functions, InstanceStore.updateProduct.selector, "updateProduct"); - _authorize(functions, InstanceStore.increaseBalance.selector, "increaseBalance"); - _authorize(functions, InstanceStore.decreaseBalance.selector, "decreaseBalance"); - _authorize(functions, InstanceStore.increaseFees.selector, "increaseFees"); - _authorize(functions, InstanceStore.decreaseFees.selector, "decreaseFees"); - + // authorize distribution service role functions = _authorizeForTarget(INSTANCE_STORE_TARGET_NAME, getServiceRole(DISTRIBUTION())); _authorize(functions, InstanceStore.createDistributorType.selector, "createDistributorType"); diff --git a/contracts/pool/BundleService.sol b/contracts/pool/BundleService.sol index 7023ceff8..c0bee287d 100644 --- a/contracts/pool/BundleService.sol +++ b/contracts/pool/BundleService.sol @@ -1,6 +1,7 @@ // SPDX-License-Identifier: Apache-2.0 pragma solidity ^0.8.20; +import {IAccountingService} from "../accounting/IAccountingService.sol"; import {IBundle} from "../instance/module/IBundle.sol"; import {IBundleService} from "./IBundleService.sol"; import {IComponents} from "../instance/module/IComponents.sol"; @@ -9,7 +10,6 @@ import {IRegistry} from "../registry/IRegistry.sol"; import {IRegistryService} from "../registry/IRegistryService.sol"; import {IInstance} from "../instance/IInstance.sol"; import {InstanceStore} from "../instance/InstanceStore.sol"; -import {IPolicy} from "../instance/module/IPolicy.sol"; import {Amount, AmountLib} from "../type/Amount.sol"; import {BundleSet} from "../instance/BundleSet.sol"; @@ -17,7 +17,7 @@ import {ComponentVerifyingService} from "../shared/ComponentVerifyingService.sol import {Fee} from "../type/Fee.sol"; import {InstanceReader} from "../instance/InstanceReader.sol"; import {NftId, NftIdLib} from "../type/NftId.sol"; -import {ObjectType, COMPONENT, POOL, BUNDLE, POLICY, REGISTRY} from "../type/ObjectType.sol"; +import {ObjectType, ACCOUNTING, COMPONENT, POOL, BUNDLE, POLICY, REGISTRY} from "../type/ObjectType.sol"; import {StateId, ACTIVE, PAUSED, CLOSED, KEEP_STATE} from "../type/StateId.sol"; import {Seconds} from "../type/Seconds.sol"; import {Timestamp, TimestampLib, zeroTimestamp} from "../type/Timestamp.sol"; @@ -33,6 +33,7 @@ contract BundleService is address private _registryAddress; IRegistryService private _registryService; + IAccountingService private _accountingService; IComponentService private _componentService; function _initialize( @@ -51,6 +52,7 @@ contract BundleService is _initializeService(registryAddress, authority, owner); _registryService = IRegistryService(_getServiceAddress(REGISTRY())); + _accountingService = IAccountingService(_getServiceAddress(ACCOUNTING())); _componentService = IComponentService(_getServiceAddress(COMPONENT())); _registerInterface(type(IBundleService).interfaceId); @@ -248,7 +250,7 @@ contract BundleService is Amount balanceAmountWithFees = instanceReader.getBalanceAmount(bundleNftId); feeAmount = instanceReader.getFeeAmount(bundleNftId); unstakedAmount = balanceAmountWithFees - feeAmount; - _componentService.decreaseBundleBalance(instanceStore, bundleNftId, unstakedAmount, feeAmount); + _accountingService.decreaseBundleBalance(instanceStore, bundleNftId, unstakedAmount, feeAmount); } } @@ -273,7 +275,7 @@ contract BundleService is revert ErrorBundleServiceBundleNotOpen(bundleNftId, bundleState, bundleInfo.expiredAt); } - _componentService.increaseBundleBalance( + _accountingService.increaseBundleBalance( instance.getInstanceStore(), bundleNftId, amount, @@ -313,7 +315,7 @@ contract BundleService is revert ErrorBundleServiceUnstakeAmountExceedsLimit(amount, availableAmount); } - _componentService.decreaseBundleBalance( + _accountingService.decreaseBundleBalance( instanceStore, bundleNftId, unstakedAmount, @@ -404,9 +406,9 @@ contract BundleService is { InstanceStore store = instance.getInstanceStore(); // decrease fee amount of the bundle - _componentService.decreaseBundleBalance(store, bundleNftId, AmountLib.zero(), withdrawnAmount); + _accountingService.decreaseBundleBalance(store, bundleNftId, AmountLib.zero(), withdrawnAmount); // decrease pool balance - _componentService.decreasePoolBalance(store, poolNftId, withdrawnAmount, AmountLib.zero()); + _accountingService.decreasePoolBalance(store, poolNftId, withdrawnAmount, AmountLib.zero()); } // transfer amount to bundle owner diff --git a/contracts/pool/PoolService.sol b/contracts/pool/PoolService.sol index cffd22db6..b1b72029b 100644 --- a/contracts/pool/PoolService.sol +++ b/contracts/pool/PoolService.sol @@ -1,6 +1,7 @@ // SPDX-License-Identifier: Apache-2.0 pragma solidity ^0.8.20; +import {IAccountingService} from "../accounting/IAccountingService.sol"; import {IBundle} from "../instance/module/IBundle.sol"; import {IBundleService} from "./IBundleService.sol"; import {IComponents} from "../instance/module/IComponents.sol"; @@ -18,7 +19,7 @@ import {ClaimId} from "../type/ClaimId.sol"; import {ContractLib} from "../shared/ContractLib.sol"; import {Fee, FeeLib} from "../type/Fee.sol"; import {NftId} from "../type/NftId.sol"; -import {ObjectType, POOL, BUNDLE, PRODUCT, POLICY, COMPONENT} from "../type/ObjectType.sol"; +import {ObjectType, ACCOUNTING, POOL, BUNDLE, PRODUCT, POLICY, COMPONENT} from "../type/ObjectType.sol"; import {Fee, FeeLib} from "../type/Fee.sol"; import {KEEP_STATE} from "../type/StateId.sol"; import {UFixed} from "../type/UFixed.sol"; @@ -32,6 +33,7 @@ contract PoolService is Service, IPoolService { + IAccountingService private _accountingService; IBundleService internal _bundleService; IComponentService internal _componentService; IStaking private _staking; @@ -51,6 +53,7 @@ contract PoolService is _initializeService(registryAddress, authority, owner); + _accountingService = IAccountingService(_getServiceAddress(ACCOUNTING())); _bundleService = IBundleService(_getServiceAddress(BUNDLE())); _componentService = IComponentService(_getServiceAddress(COMPONENT())); _staking = IStaking(getRegistry().getStakingAddress()); @@ -89,7 +92,7 @@ contract PoolService is // releasing collateral in bundle (Amount unstakedAmount, Amount feeAmount) = _bundleService.close(instance, bundleNftId); - _componentService.decreasePoolBalance( + _accountingService.decreasePoolBalance( instance.getInstanceStore(), poolNftId, unstakedAmount + feeAmount, @@ -182,7 +185,7 @@ contract PoolService is } // do all the book keeping - _componentService.increasePoolBalance( + _accountingService.increasePoolBalance( instance.getInstanceStore(), poolNftId, netAmount, @@ -234,7 +237,7 @@ contract PoolService is netAmount = unstakedAmount; // update pool bookkeeping - performance fees stay in the pool, but as fees - _componentService.decreasePoolBalance( + _accountingService.decreasePoolBalance( instanceStore, poolNftId, unstakedAmount, @@ -331,13 +334,13 @@ contract PoolService is Amount bundleNetAmount = premium.netPremiumAmount; InstanceStore instanceStore = instance.getInstanceStore(); - _componentService.increasePoolBalance( + _accountingService.increasePoolBalance( instanceStore, poolNftId, bundleNetAmount + bundleFeeAmount, poolFeeAmount); - _componentService.increaseBundleBalance( + _accountingService.increaseBundleBalance( instanceStore, bundleNftId, bundleNetAmount, @@ -423,13 +426,13 @@ contract PoolService is NftId poolNftId = getRegistry().getObjectInfo(bundleNftId).parentNftId; InstanceStore instanceStore = instance.getInstanceStore(); - _componentService.decreasePoolBalance( + _accountingService.decreasePoolBalance( instanceStore, poolNftId, payoutAmount, AmountLib.zero()); - _componentService.decreaseBundleBalance( + _accountingService.decreaseBundleBalance( instanceStore, bundleNftId, payoutAmount, diff --git a/contracts/product/PolicyService.sol b/contracts/product/PolicyService.sol index f75e04cf7..858de176a 100644 --- a/contracts/product/PolicyService.sol +++ b/contracts/product/PolicyService.sol @@ -3,6 +3,7 @@ pragma solidity ^0.8.20; import {IERC20Metadata} from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol"; +import {IAccountingService} from "../accounting/IAccountingService.sol"; import {IComponentService} from "../shared/IComponentService.sol"; import {IComponents} from "../instance/module/IComponents.sol"; import {IDistributionService} from "../distribution/IDistributionService.sol"; @@ -20,7 +21,7 @@ import {Amount} from "../type/Amount.sol"; import {APPLIED, COLLATERALIZED, KEEP_STATE, CLOSED, DECLINED, PAID} from "../type/StateId.sol"; import {ContractLib} from "../shared/ContractLib.sol"; import {NftId} from "../type/NftId.sol"; -import {ObjectType, COMPONENT, DISTRIBUTION, PRODUCT, POOL, POLICY, PRICE} from "../type/ObjectType.sol"; +import {ObjectType, ACCOUNTING, COMPONENT, DISTRIBUTION, PRODUCT, POOL, POLICY, PRICE} from "../type/ObjectType.sol"; import {ReferralId} from "../type/Referral.sol"; import {RiskId} from "../type/RiskId.sol"; import {Service} from "../shared/Service.sol"; @@ -34,6 +35,7 @@ contract PolicyService is Service, IPolicyService { + IAccountingService private _accountingService; IComponentService internal _componentService; IDistributionService internal _distributionService; IPoolService internal _poolService; @@ -55,6 +57,7 @@ contract PolicyService is _initializeService(registryAddress, authority, owner); VersionPart majorVersion = getVersion().toMajorPart(); + _accountingService = IAccountingService(getRegistry().getServiceAddress(ACCOUNTING(), majorVersion)); _componentService = IComponentService(getRegistry().getServiceAddress(COMPONENT(), majorVersion)); _poolService = IPoolService(getRegistry().getServiceAddress(POOL(), majorVersion)); _distributionService = IDistributionService(getRegistry().getServiceAddress(DISTRIBUTION(), majorVersion)); @@ -504,7 +507,7 @@ contract PolicyService is productNftId); // update product fees, distribution and pool fees - _componentService.increaseProductFees( + _accountingService.increaseProductFees( instanceStore, productNftId, premium.productFeeVarAmount + premium.productFeeFixAmount); diff --git a/contracts/shared/ComponentService.sol b/contracts/shared/ComponentService.sol index ad179d373..09dfb8769 100644 --- a/contracts/shared/ComponentService.sol +++ b/contracts/shared/ComponentService.sol @@ -3,11 +3,10 @@ pragma solidity ^0.8.20; import {IERC20Metadata} from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol"; -import {AccessAdmin} from "../authorization/AccessAdmin.sol"; import {Amount, AmountLib} from "../type/Amount.sol"; -import {ComponentVerifyingService} from "../shared/ComponentVerifyingService.sol"; import {ContractLib} from "../shared/ContractLib.sol"; import {Fee, FeeLib} from "../type/Fee.sol"; +import {IAccountingService} from "../accounting/IAccountingService.sol"; import {IComponent} from "../shared/IComponent.sol"; import {IComponents} from "../instance/module/IComponents.sol"; import {IComponentService} from "./IComponentService.sol"; @@ -28,7 +27,7 @@ import {ContractLib} from "../shared/ContractLib.sol"; import {Fee, FeeLib} from "../type/Fee.sol"; import {KEEP_STATE} from "../type/StateId.sol"; import {NftId} from "../type/NftId.sol"; -import {ObjectType, REGISTRY, BUNDLE, COMPONENT, DISTRIBUTION, DISTRIBUTOR, INSTANCE, ORACLE, POOL, PRODUCT, STAKING} from "../type/ObjectType.sol"; +import {ObjectType, ACCOUNTING, REGISTRY, COMPONENT, DISTRIBUTION, INSTANCE, ORACLE, POOL, PRODUCT} from "../type/ObjectType.sol"; import {Service} from "../shared/Service.sol"; import {TokenHandler} from "../shared/TokenHandler.sol"; import {TokenHandlerDeployerLib} from "../shared/TokenHandlerDeployerLib.sol"; @@ -42,6 +41,7 @@ contract ComponentService is bool private constant INCREASE = true; bool private constant DECREASE = false; + IAccountingService private _accountingService; IRegistryService private _registryService; IInstanceService private _instanceService; @@ -85,6 +85,7 @@ contract ComponentService is _initializeService(registryAddress, authority, owner); + _accountingService = IAccountingService(_getServiceAddress(ACCOUNTING())); _registryService = IRegistryService(_getServiceAddress(REGISTRY())); _instanceService = IInstanceService(_getServiceAddress(INSTANCE())); @@ -208,7 +209,7 @@ contract ComponentService is } // decrease fee counters by withdrawnAmount - _changeTargetBalance(DECREASE, instance.getInstanceStore(), componentNftId, AmountLib.zero(), withdrawnAmount); + _accountingService.decreaseComponentFees(instance.getInstanceStore(), componentNftId, withdrawnAmount); // transfer amount to component owner address componentOwner = getRegistry().ownerOf(componentNftId); @@ -278,31 +279,6 @@ contract ComponentService is } } - function increaseProductFees( - InstanceStore instanceStore, - NftId productNftId, - Amount feeAmount - ) - external - virtual - // TODO re-enable once role granting is stable and fixed - // restricted() - { - _checkNftType(productNftId, PRODUCT()); - _changeTargetBalance(INCREASE, instanceStore, productNftId, AmountLib.zero(), feeAmount); - } - - - function decreaseProductFees(InstanceStore instanceStore, NftId productNftId, Amount feeAmount) - external - virtual - // TODO re-enable once role granting is stable and fixed - // restricted() - { - _checkNftType(productNftId, PRODUCT()); - _changeTargetBalance(DECREASE, instanceStore, productNftId, AmountLib.zero(), feeAmount); - } - //-------- distribution -------------------------------------------------// /// @dev registers the sending component as a distribution component @@ -372,69 +348,6 @@ contract ComponentService is } } - function increaseDistributionBalance( - InstanceStore instanceStore, - NftId distributionNftId, - Amount amount, - Amount feeAmount - ) - external - virtual - // TODO re-enable once role granting is stable and fixed - // restricted() - { - _checkNftType(distributionNftId, DISTRIBUTION()); - _changeTargetBalance(INCREASE, instanceStore, distributionNftId, amount, feeAmount); - } - - - function decreaseDistributionBalance( - InstanceStore instanceStore, - NftId distributionNftId, - Amount amount, - Amount feeAmount - ) - external - virtual - // TODO re-enable once role granting is stable and fixed - // restricted() - { - _checkNftType(distributionNftId, DISTRIBUTION()); - _changeTargetBalance(DECREASE, instanceStore, distributionNftId, amount, feeAmount); - } - - //-------- distributor -------------------------------------------------------// - - function increaseDistributorBalance( - InstanceStore instanceStore, - NftId distributorNftId, - Amount amount, - Amount feeAmount - ) - external - virtual - // TODO re-enable once role granting is stable and fixed - // restricted() - { - _checkNftType(distributorNftId, DISTRIBUTOR()); - _changeTargetBalance(INCREASE, instanceStore, distributorNftId, amount, feeAmount); - } - - function decreaseDistributorBalance( - InstanceStore instanceStore, - NftId distributorNftId, - Amount amount, - Amount feeAmount - ) - external - virtual - // TODO re-enable once role granting is stable and fixed - // restricted() - { - _checkNftType(distributorNftId, DISTRIBUTOR()); - _changeTargetBalance(DECREASE, instanceStore, distributorNftId, amount, feeAmount); - } - //-------- oracle -------------------------------------------------------// function _registerOracle(address oracleAddress) @@ -550,92 +463,6 @@ contract ComponentService is } } - function increasePoolBalance( - InstanceStore instanceStore, - NftId poolNftId, - Amount amount, - Amount feeAmount - ) - public - virtual - // TODO re-enable once role granting is stable and fixed - // restricted() - { - _checkNftType(poolNftId, POOL()); - _changeTargetBalance(INCREASE, instanceStore, poolNftId, amount, feeAmount); - } - - function decreasePoolBalance( - InstanceStore instanceStore, - NftId poolNftId, - Amount amount, - Amount feeAmount - ) - public - virtual - // TODO re-enable once role granting is stable and fixed - // restricted() - { - _checkNftType(poolNftId, POOL()); - _changeTargetBalance(DECREASE, instanceStore, poolNftId, amount, feeAmount); - } - - //-------- bundle -------------------------------------------------------// - - function increaseBundleBalance( - InstanceStore instanceStore, - NftId bundleNftId, - Amount amount, - Amount feeAmount - ) - external - virtual - // TODO re-enable once role granting is stable and fixed - // restricted() - { - _checkNftType(bundleNftId, BUNDLE()); - _changeTargetBalance(INCREASE, instanceStore, bundleNftId, amount, feeAmount); - } - - function decreaseBundleBalance( - InstanceStore instanceStore, - NftId bundleNftId, - Amount amount, - Amount feeAmount - ) - external - virtual - // TODO re-enable once role granting is stable and fixed - // restricted() - { - _checkNftType(bundleNftId, BUNDLE()); - _changeTargetBalance(DECREASE, instanceStore, bundleNftId, amount, feeAmount); - } - - - //-------- internal functions ------------------------------------------// - - function _changeTargetBalance( - bool increase, - InstanceStore instanceStore, - NftId targetNftId, - Amount amount, - Amount feeAmount - ) - internal - virtual - { - Amount totalAmount = amount + feeAmount; - - if(increase) { - if(totalAmount.gtz()) { instanceStore.increaseBalance(targetNftId, totalAmount); } - if(feeAmount.gtz()) { instanceStore.increaseFees(targetNftId, feeAmount); } - } else { - if(totalAmount.gtz()) { instanceStore.decreaseBalance(targetNftId, totalAmount); } - if(feeAmount.gtz()) { instanceStore.decreaseFees(targetNftId, feeAmount); } - } - } - /// @dev Registers the component represented by the provided address. function _register( address componentAddress, // address of component to register diff --git a/contracts/shared/IComponentService.sol b/contracts/shared/IComponentService.sol index b2986f5f9..77db16a53 100644 --- a/contracts/shared/IComponentService.sol +++ b/contracts/shared/IComponentService.sol @@ -5,7 +5,6 @@ import {IERC20Metadata} from "@openzeppelin/contracts/token/ERC20/extensions/IER import {Amount} from "../type/Amount.sol"; import {Fee} from "../type/Fee.sol"; -import {InstanceStore} from "../instance/InstanceStore.sol"; import {IService} from "../shared/IService.sol"; import {NftId} from "../type/NftId.sol"; import {ObjectType} from "../type/ObjectType.sol"; @@ -61,7 +60,7 @@ interface IComponentService is UFixed newFractionalFee, uint256 newFixedFee ); - + //-------- component ----------------------------------------------------// /// @dev Approves the callers token handler to spend up to the specified amount of tokens. @@ -99,9 +98,6 @@ interface IComponentService is Fee memory processingFee // product fee on payout amounts ) external; - function increaseProductFees(InstanceStore instanceStore, NftId productNftId, Amount feeAmount) external; - function decreaseProductFees(InstanceStore instanceStore, NftId productNftId, Amount feeAmount) external; - //-------- distribution -------------------------------------------------// function setDistributionFees( @@ -109,13 +105,6 @@ interface IComponentService is Fee memory minDistributionOwnerFee // min fee required by distribution owner (not including commissions for distributors) ) external; - function increaseDistributionBalance(InstanceStore instanceStore, NftId distributionNftId, Amount amount, Amount feeAmount) external; - function decreaseDistributionBalance(InstanceStore instanceStore, NftId distributionNftId, Amount amount, Amount feeAmount) external; - - //-------- distributor --------------------------------------------------// - function increaseDistributorBalance(InstanceStore instanceStore, NftId distributorNftId, Amount amount, Amount feeAmount) external; - function decreaseDistributorBalance(InstanceStore instanceStore, NftId distributorNftId, Amount amount, Amount feeAmount) external; - //-------- pool ---------------------------------------------------------// function setPoolFees( @@ -124,11 +113,4 @@ interface IComponentService is Fee memory performanceFee // pool fee on profits from capital investors ) external; - function increasePoolBalance(InstanceStore instanceStore, NftId poolNftId, Amount amount, Amount feeAmount) external; - function decreasePoolBalance(InstanceStore instanceStore, NftId poolNftId, Amount amount, Amount feeAmount) external; - - //-------- bundle -------------------------------------------------------// - function increaseBundleBalance(InstanceStore instanceStore, NftId bundleNftId, Amount amount, Amount feeAmount) external; - function decreaseBundleBalance(InstanceStore instanceStore, NftId bundleNftId, Amount amount, Amount feeAmount) external; - } \ No newline at end of file From 8e9617f2f9f424c84af8e83253c58c190705ac8b Mon Sep 17 00:00:00 2001 From: Marc Doerflinger Date: Thu, 15 Aug 2024 13:29:31 +0000 Subject: [PATCH 9/9] fix permissions (#599) --- contracts/accounting/AccountingService.sol | 57 ++++++++++++------- contracts/accounting/IAccountingService.sol | 3 + contracts/pool/IBundleService.sol | 1 - contracts/pool/PoolService.sol | 4 +- contracts/registry/ServiceAuthorizationV3.sol | 32 +++++++++++ 5 files changed, 75 insertions(+), 22 deletions(-) diff --git a/contracts/accounting/AccountingService.sol b/contracts/accounting/AccountingService.sol index a415b96fd..5a1ea8eb7 100644 --- a/contracts/accounting/AccountingService.sol +++ b/contracts/accounting/AccountingService.sol @@ -45,8 +45,8 @@ contract AccountingService is ) external virtual + restricted() { - // TODO: validate that the nft belongs to a component _changeTargetBalance(DECREASE, instanceStore, componentNftId, AmountLib.zero(), feeAmount); } @@ -58,8 +58,7 @@ contract AccountingService is ) external virtual - // TODO re-enable once role granting is stable and fixed - // restricted() + restricted() { _checkNftType(productNftId, PRODUCT()); _changeTargetBalance(INCREASE, instanceStore, productNftId, AmountLib.zero(), feeAmount); @@ -69,8 +68,7 @@ contract AccountingService is function decreaseProductFees(InstanceStore instanceStore, NftId productNftId, Amount feeAmount) external virtual - // TODO re-enable once role granting is stable and fixed - // restricted() + restricted() { _checkNftType(productNftId, PRODUCT()); _changeTargetBalance(DECREASE, instanceStore, productNftId, AmountLib.zero(), feeAmount); @@ -84,8 +82,7 @@ contract AccountingService is ) external virtual - // TODO re-enable once role granting is stable and fixed - // restricted() + restricted() { _checkNftType(distributionNftId, DISTRIBUTION()); _changeTargetBalance(INCREASE, instanceStore, distributionNftId, amount, feeAmount); @@ -100,8 +97,7 @@ contract AccountingService is ) external virtual - // TODO re-enable once role granting is stable and fixed - // restricted() + restricted() { _checkNftType(distributionNftId, DISTRIBUTION()); _changeTargetBalance(DECREASE, instanceStore, distributionNftId, amount, feeAmount); @@ -115,8 +111,7 @@ contract AccountingService is ) external virtual - // TODO re-enable once role granting is stable and fixed - // restricted() + restricted() { _checkNftType(distributorNftId, DISTRIBUTOR()); _changeTargetBalance(INCREASE, instanceStore, distributorNftId, amount, feeAmount); @@ -130,8 +125,7 @@ contract AccountingService is ) external virtual - // TODO re-enable once role granting is stable and fixed - // restricted() + restricted() { _checkNftType(distributorNftId, DISTRIBUTOR()); _changeTargetBalance(DECREASE, instanceStore, distributorNftId, amount, feeAmount); @@ -145,8 +139,7 @@ contract AccountingService is ) public virtual - // TODO re-enable once role granting is stable and fixed - // restricted() + restricted() { _checkNftType(poolNftId, POOL()); _changeTargetBalance(INCREASE, instanceStore, poolNftId, amount, feeAmount); @@ -175,8 +168,7 @@ contract AccountingService is ) external virtual - // TODO re-enable once role granting is stable and fixed - // restricted() + restricted() { _checkNftType(bundleNftId, BUNDLE()); _changeTargetBalance(INCREASE, instanceStore, bundleNftId, amount, feeAmount); @@ -190,8 +182,35 @@ contract AccountingService is ) external virtual - // TODO re-enable once role granting is stable and fixed - // restricted() + restricted() + { + _checkNftType(bundleNftId, BUNDLE()); + _changeTargetBalance(DECREASE, instanceStore, bundleNftId, amount, feeAmount); + } + + function increaseBundleBalanceForPool( + InstanceStore instanceStore, + NftId bundleNftId, + Amount amount, + Amount feeAmount + ) + external + virtual + restricted() + { + _checkNftType(bundleNftId, BUNDLE()); + _changeTargetBalance(INCREASE, instanceStore, bundleNftId, amount, feeAmount); + } + + function decreaseBundleBalanceForPool( + InstanceStore instanceStore, + NftId bundleNftId, + Amount amount, + Amount feeAmount + ) + external + virtual + restricted() { _checkNftType(bundleNftId, BUNDLE()); _changeTargetBalance(DECREASE, instanceStore, bundleNftId, amount, feeAmount); diff --git a/contracts/accounting/IAccountingService.sol b/contracts/accounting/IAccountingService.sol index 7c0563707..14207a090 100644 --- a/contracts/accounting/IAccountingService.sol +++ b/contracts/accounting/IAccountingService.sol @@ -39,4 +39,7 @@ interface IAccountingService is function increaseBundleBalance(InstanceStore instanceStore, NftId bundleNftId, Amount amount, Amount feeAmount) external; function decreaseBundleBalance(InstanceStore instanceStore, NftId bundleNftId, Amount amount, Amount feeAmount) external; + function increaseBundleBalanceForPool(InstanceStore instanceStore, NftId bundleNftId, Amount amount, Amount feeAmount) external; + function decreaseBundleBalanceForPool(InstanceStore instanceStore, NftId bundleNftId, Amount amount, Amount feeAmount) external; + } \ No newline at end of file diff --git a/contracts/pool/IBundleService.sol b/contracts/pool/IBundleService.sol index 81ac8115e..1e545ded7 100644 --- a/contracts/pool/IBundleService.sol +++ b/contracts/pool/IBundleService.sol @@ -5,7 +5,6 @@ import {Amount} from "../type/Amount.sol"; import {NftId} from "../type/NftId.sol"; import {Fee} from "../type/Fee.sol"; import {IService} from "../shared/IService.sol"; -import {IBundle} from "../instance/module/IBundle.sol"; import {IInstance} from "../instance/IInstance.sol"; import {Seconds} from "../type/Seconds.sol"; import {StateId} from "../type/StateId.sol"; diff --git a/contracts/pool/PoolService.sol b/contracts/pool/PoolService.sol index b1b72029b..5c0f33677 100644 --- a/contracts/pool/PoolService.sol +++ b/contracts/pool/PoolService.sol @@ -340,7 +340,7 @@ contract PoolService is bundleNetAmount + bundleFeeAmount, poolFeeAmount); - _accountingService.increaseBundleBalance( + _accountingService.increaseBundleBalanceForPool( instanceStore, bundleNftId, bundleNetAmount, @@ -432,7 +432,7 @@ contract PoolService is payoutAmount, AmountLib.zero()); - _accountingService.decreaseBundleBalance( + _accountingService.decreaseBundleBalanceForPool( instanceStore, bundleNftId, payoutAmount, diff --git a/contracts/registry/ServiceAuthorizationV3.sol b/contracts/registry/ServiceAuthorizationV3.sol index 3d72d52cd..339856894 100644 --- a/contracts/registry/ServiceAuthorizationV3.sol +++ b/contracts/registry/ServiceAuthorizationV3.sol @@ -6,6 +6,7 @@ import { } from "../../contracts/type/ObjectType.sol"; import {IAccess} from "../authorization/IAccess.sol"; +import {IAccountingService} from "../accounting/IAccountingService.sol"; import {IBundleService} from "../pool/IBundleService.sol"; import {IDistributionService} from "../distribution/IDistributionService.sol"; import {IPoolService} from "../pool/IPoolService.sol"; @@ -50,6 +51,7 @@ contract ServiceAuthorizationV3 _setupIRegistryServiceAuthorization(); _setupStakingServiceAuthorization(); _setupInstanceServiceAuthorization(); + _setupAccountingServiceAuthorization(); _setupComponentServiceAuthorization(); _setupDistributionServiceAuthorization(); _setupPoolServiceAuthorization(); @@ -115,6 +117,36 @@ contract ServiceAuthorizationV3 { } + /// @dev Accounting service function authorization. + function _setupAccountingServiceAuthorization() + internal + { + IAccess.FunctionInfo[] storage functions; + + functions = _authorizeForService(ACCOUNTING(), BUNDLE()); + _authorize(functions, IAccountingService.increaseBundleBalance.selector, "increaseBundleBalance"); + _authorize(functions, IAccountingService.decreaseBundleBalance.selector, "decreaseBundleBalance"); + + functions = _authorizeForService(ACCOUNTING(), COMPONENT()); + _authorize(functions, IAccountingService.decreaseComponentFees.selector, "decreaseComponentFees"); + + functions = _authorizeForService(ACCOUNTING(), DISTRIBUTION()); + _authorize(functions, IAccountingService.increaseDistributionBalance.selector, "increaseDistributionBalance"); + _authorize(functions, IAccountingService.decreaseDistributionBalance.selector, "decreaseDistributionBalance"); + _authorize(functions, IAccountingService.increaseDistributorBalance.selector, "increaseDistributorBalance"); + _authorize(functions, IAccountingService.decreaseDistributorBalance.selector, "decreaseDistributorBalance"); + + functions = _authorizeForService(ACCOUNTING(), POLICY()); + _authorize(functions, IAccountingService.increaseProductFees.selector, "increaseProductFees"); + + functions = _authorizeForService(ACCOUNTING(), POOL()); + _authorize(functions, IAccountingService.increasePoolBalance.selector, "increasePoolBalance"); + _authorize(functions, IAccountingService.decreasePoolBalance.selector, "decreasePoolBalance"); + _authorize(functions, IAccountingService.increaseBundleBalanceForPool.selector, "increaseBundleBalanceForPool"); + _authorize(functions, IAccountingService.decreaseBundleBalanceForPool.selector, "decreaseBundleBalanceForPool"); + + } + /// @dev Component service function authorization. function _setupComponentServiceAuthorization()