From c778c387f9d7224bc591b238bc4511d385f75df0 Mon Sep 17 00:00:00 2001 From: rapiddenis <41779817+rapidddenis@users.noreply.github.com> Date: Thu, 29 Aug 2024 12:35:15 +0000 Subject: [PATCH 1/3] unify storage locations add upgradability --- README.md | 8 + contracts/distribution/Distribution.sol | 6 +- .../distribution/DistributionService.sol | 47 ++++-- contracts/instance/InstanceService.sol | 149 +++++++++------- contracts/oracle/Oracle.sol | 6 +- contracts/pool/BundleService.sol | 37 ++-- contracts/pool/Pool.sol | 6 +- contracts/pool/PoolService.sol | 77 ++++++--- contracts/product/ApplicationService.sol | 34 +++- contracts/product/ClaimService.sol | 76 +++++---- contracts/product/PolicyService.sol | 159 +++++++++++------- contracts/product/PricingService.sol | 22 ++- contracts/product/Product.sol | 6 +- contracts/shared/Component.sol | 6 +- contracts/shared/ComponentService.sol | 42 +++-- contracts/shared/InitializableERC165.sol | 22 ++- contracts/shared/InstanceLinkedComponent.sol | 6 +- contracts/shared/NftOwnable.sol | 6 +- contracts/shared/Registerable.sol | 6 +- contracts/staking/Staking.sol | 6 +- contracts/staking/StakingService.sol | 7 +- test/component/product/ProductPayout.t.sol | 4 +- test/component/product/ProductPolicy.t.sol | 101 ++++++++--- 23 files changed, 539 insertions(+), 300 deletions(-) diff --git a/README.md b/README.md index ae7d1b541..da3dba91f 100644 --- a/README.md +++ b/README.md @@ -176,6 +176,14 @@ hh run scripts/find_missing_virtual_methods.ts.ts Checks all public/external methods in services and components for methods that are not marked as `virtual` but should be. +### find upgradeable contracts storage locations + +```bash +grep -o -h -E '([A-Z0-9_]+_LOCATION_V[0-9]+_[0-9]+\s*=\s*0x[A-Ha-h0-9]+)' -r contracts +``` + +Prints addresses for each storage struct of each release of each upgradeable contract + ## Forge ### Commands diff --git a/contracts/distribution/Distribution.sol b/contracts/distribution/Distribution.sol index bab899424..bec2407b5 100644 --- a/contracts/distribution/Distribution.sol +++ b/contracts/distribution/Distribution.sol @@ -22,8 +22,8 @@ abstract contract Distribution is InstanceLinkedComponent, IDistributionComponent { - // keccak256(abi.encode(uint256(keccak256("etherisc.storage.Distribution")) - 1)) & ~bytes32(uint256(0xff)); - bytes32 public constant DISTRIBUTION_STORAGE_LOCATION_V1 = 0xaab7c5ea03d290056d6c060e0833d3ebcbe647f7694616a2ec52738a64b2f900; + // keccak256(abi.encode(uint256(keccak256("etherisc.gif.Distribution@3.0.0")) - 1)) & ~bytes32(uint256(0xff)); + bytes32 public constant DISTRIBUTION_STORAGE_LOCATION_V3_0 = 0xf05be9ae88f1a13816beae3bf4177e164907f5d055d6c38ea0a1fabc08c6c500; struct DistributionStorage { IComponentService _componentService; @@ -239,7 +239,7 @@ abstract contract Distribution is function _getDistributionStorage() private pure returns (DistributionStorage storage $) { assembly { - $.slot := DISTRIBUTION_STORAGE_LOCATION_V1 + $.slot := DISTRIBUTION_STORAGE_LOCATION_V3_0 } } } diff --git a/contracts/distribution/DistributionService.sol b/contracts/distribution/DistributionService.sol index 68cd39315..cb6617a4f 100644 --- a/contracts/distribution/DistributionService.sol +++ b/contracts/distribution/DistributionService.sol @@ -33,11 +33,18 @@ contract DistributionService is Service, IDistributionService { - IAccountingService private _accountingService; - IComponentService private _componentService; - IInstanceService private _instanceService; - IRegistryService private _registryService; - + // TODO make all storage slot constants private? + // keccak256(abi.encode(uint256(keccak256("etherisc.gif.DistributionService@3.0.0")) - 1)) & ~bytes32(uint256(0xff)); + bytes32 public constant DISTRIBUTION_SERVICE_STORAGE_LOCATION_V3_0 = 0xabfd9ba715b54654d8f0ef9f71a2076277bb1487677f05f4685d1bba29453600; + + struct DistributionServiceStorage { + IAccountingService _accountingService; + IComponentService _componentService; + IInstanceService _instanceService; + IRegistryService _registryService; + } + + function _initialize( address owner, bytes memory data @@ -53,10 +60,11 @@ contract DistributionService is __Service_init(authority, registry, owner); - _accountingService = IAccountingService(_getServiceAddress(ACCOUNTING())); - _componentService = IComponentService(_getServiceAddress(COMPONENT())); - _instanceService = IInstanceService(_getServiceAddress(INSTANCE())); - _registryService = IRegistryService(_getServiceAddress(REGISTRY())); + DistributionServiceStorage storage $ = _getDistributionServiceStorage(); + $._accountingService = IAccountingService(_getServiceAddress(ACCOUNTING())); + $._componentService = IComponentService(_getServiceAddress(COMPONENT())); + $._instanceService = IInstanceService(_getServiceAddress(INSTANCE())); + $._registryService = IRegistryService(_getServiceAddress(REGISTRY())); _registerInterface(type(IDistributionService).interfaceId); } @@ -129,7 +137,8 @@ contract DistributionService is (NftId distributionNftId, IInstance instance) = _getAndVerifyActiveDistribution(); _checkDistributionType(instance.getInstanceReader(), distributorType, distributionNftId); - distributorNftId = _registryService.registerDistributor( + DistributionServiceStorage storage $ = _getDistributionServiceStorage(); + distributorNftId = $._registryService.registerDistributor( IRegistry.ObjectInfo( NftIdLib.zero(), distributionNftId, @@ -279,17 +288,19 @@ contract DistributionService is // get distribution owner fee amount Amount distributionOwnerFee = premium.distributionOwnerFeeFixAmount + premium.distributionOwnerFeeVarAmount; + DistributionServiceStorage storage $ = _getDistributionServiceStorage(); + // update referral/distributor info if applicable if (referralIsValid(distributionNftId, referralId)) { // increase distribution balance by commission amount and distribution owner fee Amount commissionAmount = premium.commissionAmount; - _accountingService.increaseDistributionBalance(store, distributionNftId, commissionAmount, distributionOwnerFee); + $._accountingService.increaseDistributionBalance(store, distributionNftId, commissionAmount, distributionOwnerFee); // update book keeping for referral info IDistribution.ReferralInfo memory referralInfo = reader.getReferralInfo(referralId); - _accountingService.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); @@ -297,7 +308,7 @@ contract DistributionService is store.updateDistributor(referralInfo.distributorNftId, distributorInfo, KEEP_STATE()); } else { // increase distribution balance by distribution owner fee - _accountingService.increaseDistributionBalance(store, distributionNftId, AmountLib.zero(), distributionOwnerFee); + $._accountingService.increaseDistributionBalance(store, distributionNftId, AmountLib.zero(), distributionOwnerFee); } emit LogDistributionServiceSaleProcessed(distributionNftId, referralId); @@ -331,11 +342,12 @@ contract DistributionService is // decrease fee counters by withdrawnAmount and update distributor info { + DistributionServiceStorage storage $ = _getDistributionServiceStorage(); InstanceStore store = instance.getInstanceStore(); // decrease fee counter for distribution balance - _accountingService.decreaseDistributionBalance(store, distributionNftId, withdrawnAmount, AmountLib.zero()); + $._accountingService.decreaseDistributionBalance(store, distributionNftId, withdrawnAmount, AmountLib.zero()); // decrease fee counter for distributor fee - _accountingService.decreaseDistributorBalance(store, distributorNftId, AmountLib.zero(), withdrawnAmount); + $._accountingService.decreaseDistributorBalance(store, distributorNftId, AmountLib.zero(), withdrawnAmount); } // transfer amount to distributor @@ -441,6 +453,11 @@ contract DistributionService is return PoolLib.getAndVerifyActiveComponent(getRegistry(), msg.sender, DISTRIBUTION()); } + function _getDistributionServiceStorage() private pure returns (DistributionServiceStorage storage $) { + assembly { + $.slot := DISTRIBUTION_SERVICE_STORAGE_LOCATION_V3_0 + } + } function _getDomain() internal pure override returns(ObjectType) { return DISTRIBUTION(); diff --git a/contracts/instance/InstanceService.sol b/contracts/instance/InstanceService.sol index e10a641f6..f90f362ed 100644 --- a/contracts/instance/InstanceService.sol +++ b/contracts/instance/InstanceService.sol @@ -36,21 +36,25 @@ contract InstanceService is IInstanceService { - // TODO update to real hash when instance is stable bytes32 public constant INSTANCE_CREATION_CODE_HASH = bytes32(0); - IRegistryService internal _registryService; - IStakingService internal _stakingService; - IComponentService internal _componentService; - - address internal _masterAccessManager; - address internal _masterInstanceAdmin; - address internal _masterInstance; - address internal _masterInstanceReader; - address internal _masterInstanceBundleSet; - address internal _masterInstanceRiskSet; - address internal _masterInstanceStore; - address internal _masterProductStore; + // keccak256(abi.encode(uint256(keccak256("etherisc.gif.InstanceService@3.0.0")) - 1)) & ~bytes32(uint256(0xff)); + bytes32 public constant INSTANCE_SERVICE_STORAGE_LOCATION_V3_0 = 0xd3b64f4b2eed579b20c89ed2b45eb50bb8adfc41b6a240bf0795686c0bce0500; + + struct InstanceServiceStorage { + IRegistryService _registryService; + IStakingService _stakingService; + IComponentService _componentService; + + address _masterAccessManager; + address _masterInstanceAdmin; + address _masterInstance; + address _masterInstanceReader; + address _masterInstanceBundleSet; + address _masterInstanceRiskSet; + address _masterInstanceStore; + address _masterProductStore; + } modifier onlyInstance() { @@ -198,11 +202,12 @@ contract InstanceService is instance = _createInstance(instanceAdmin, instanceOwner, allowAnyToken); // register cloned instance with registry - instanceNftId = _registryService.registerInstance( + InstanceServiceStorage storage $ = _getInstanceServiceStorage(); + instanceNftId = $._registryService.registerInstance( instance, instanceOwner).nftId; // MUST be set after instance is set up and registered - IAuthorization instanceAuthorization = InstanceAdmin(_masterInstanceAdmin).getInstanceAuthorization(); + IAuthorization instanceAuthorization = InstanceAdmin($._masterInstanceAdmin).getInstanceAuthorization(); instanceAdmin.completeSetup( address(getRegistry()), address(instanceAuthorization), @@ -214,7 +219,7 @@ contract InstanceService is assert(instance.getRelease() == getRelease()); // register cloned instance as staking target - _stakingService.createInstanceTarget( + $._stakingService.createInstanceTarget( instanceNftId, TargetManagerLib.getDefaultLockingPeriod(), TargetManagerLib.getDefaultRewardRate()); @@ -232,7 +237,8 @@ contract InstanceService is onlyInstance() { NftId instanceNftId = getRegistry().getNftIdForAddress(msg.sender); - _stakingService.setInstanceLockingPeriod( + InstanceServiceStorage storage $ = _getInstanceServiceStorage(); + $._stakingService.setInstanceLockingPeriod( instanceNftId, stakeLockingPeriod); } @@ -245,7 +251,8 @@ contract InstanceService is onlyInstance() { NftId instanceNftId = getRegistry().getNftIdForAddress(msg.sender); - _stakingService.setInstanceRewardRate( + InstanceServiceStorage storage $ = _getInstanceServiceStorage(); + $._stakingService.setInstanceRewardRate( instanceNftId, rewardRate); } @@ -257,7 +264,8 @@ contract InstanceService is onlyInstance() { NftId instanceNftId = getRegistry().getNftIdForAddress(msg.sender); - _stakingService.setInstanceMaxStakedAmount( + InstanceServiceStorage storage $ = _getInstanceServiceStorage(); + $._stakingService.setInstanceMaxStakedAmount( instanceNftId, maxStakedAmount); } @@ -271,7 +279,8 @@ contract InstanceService is returns (Amount newBalance) { NftId instanceNftId = getRegistry().getNftIdForAddress(msg.sender); - newBalance = _stakingService.refillInstanceRewardReserves( + InstanceServiceStorage storage $ = _getInstanceServiceStorage(); + newBalance = $._stakingService.refillInstanceRewardReserves( instanceNftId, rewardProvider, dipAmount); @@ -286,7 +295,8 @@ contract InstanceService is returns (Amount newBalance) { NftId instanceNftId = getRegistry().getNftIdForAddress(msg.sender); - newBalance = _stakingService.withdrawInstanceRewardReserves( + InstanceServiceStorage storage $ = _getInstanceServiceStorage(); + newBalance = $._stakingService.withdrawInstanceRewardReserves( instanceNftId, dipAmount); } @@ -301,8 +311,9 @@ contract InstanceService is address instanceAddress = msg.sender; IInstance instance = IInstance(msg.sender); + InstanceServiceStorage storage $ = _getInstanceServiceStorage(); InstanceReader upgradedInstanceReaderClone = InstanceReader( - Clones.clone(address(_masterInstanceReader))); + Clones.clone(address($._masterInstanceReader))); upgradedInstanceReaderClone.initializeWithInstance(instanceAddress); instance.setInstanceReader(upgradedInstanceReaderClone); @@ -319,35 +330,31 @@ contract InstanceService is onlyOwner() returns(NftId masterInstanceNftId) { - if(_masterInstance != address(0)) { revert ErrorInstanceServiceMasterInstanceAlreadySet(); } - if(_masterInstanceAdmin != address(0)) { revert ErrorInstanceServiceMasterInstanceAdminAlreadySet(); } - if(_masterInstanceBundleSet != address(0)) { revert ErrorInstanceServiceMasterBundleSetAlreadySet(); } - if(_masterInstanceRiskSet != address(0)) { revert ErrorInstanceServiceMasterRiskSetAlreadySet(); } + InstanceServiceStorage storage $ = _getInstanceServiceStorage(); + + if($._masterInstance != address(0)) { revert ErrorInstanceServiceMasterInstanceAlreadySet(); } + if($._masterInstanceAdmin != address(0)) { revert ErrorInstanceServiceMasterInstanceAdminAlreadySet(); } + if($._masterInstanceBundleSet != address(0)) { revert ErrorInstanceServiceMasterBundleSetAlreadySet(); } + if($._masterInstanceRiskSet != address(0)) { revert ErrorInstanceServiceMasterRiskSetAlreadySet(); } if(instanceAddress == address(0)) { revert ErrorInstanceServiceInstanceAddressZero(); } { IInstance instance = IInstance(instanceAddress); address accessManagerAddress = instance.authority(); InstanceAdmin instanceAdmin = instance.getInstanceAdmin(); - address instanceAdminAddress = address(instanceAdmin); InstanceReader instanceReader = instance.getInstanceReader(); - address instanceReaderAddress = address(instanceReader); BundleSet bundleSet = instance.getBundleSet(); - address bundleSetAddress = address(bundleSet); RiskSet riskSet = instance.getRiskSet(); - address riskSetAddress = address(riskSet); InstanceStore instanceStore = instance.getInstanceStore(); - address instanceStoreAddress = address(instanceStore); ProductStore productStore = instance.getProductStore(); - address productStoreAddress = address(productStore); if(accessManagerAddress == address(0)) { revert ErrorInstanceServiceAccessManagerZero(); } - if(instanceAdminAddress == address(0)) { revert ErrorInstanceServiceInstanceAdminZero(); } - if(instanceReaderAddress == address(0)) { revert ErrorInstanceServiceInstanceReaderZero(); } - if(bundleSetAddress == address(0)) { revert ErrorInstanceServiceBundleSetZero(); } - if(riskSetAddress == address(0)) { revert ErrorInstanceServiceRiskSetZero(); } - if(instanceStoreAddress == address(0)) { revert ErrorInstanceServiceInstanceStoreZero(); } - if(productStoreAddress == address(0)) { revert ErrorInstanceServiceProductStoreZero(); } // TODO: rename exception + if(address(instanceAdmin) == address(0)) { revert ErrorInstanceServiceInstanceAdminZero(); } + if(address(instanceReader) == address(0)) { revert ErrorInstanceServiceInstanceReaderZero(); } + if(address(bundleSet) == address(0)) { revert ErrorInstanceServiceBundleSetZero(); } + if(address(riskSet) == address(0)) { revert ErrorInstanceServiceRiskSetZero(); } + if(address(instanceStore) == address(0)) { revert ErrorInstanceServiceInstanceStoreZero(); } + if(address(productStore) == address(0)) { revert ErrorInstanceServiceProductStoreZero(); } if(instance.authority() != instanceAdmin.authority()) { revert ErrorInstanceServiceInstanceAuthorityMismatch(); } if(bundleSet.authority() != instanceAdmin.authority()) { revert ErrorInstanceServiceBundleSetAuthorityMismatch(); } @@ -358,19 +365,19 @@ contract InstanceService is if(riskSet.getInstanceAddress() != address(instance)) { revert ErrorInstanceServiceRiskSetInstanceMismatch(); } if(instanceReader.getInstance() != instance) { revert ErrorInstanceServiceInstanceReaderInstanceMismatch2(); } - _masterAccessManager = accessManagerAddress; - _masterInstanceAdmin = instanceAdminAddress; - _masterInstance = instanceAddress; - _masterInstanceReader = instanceReaderAddress; - _masterInstanceBundleSet = bundleSetAddress; - _masterInstanceRiskSet = riskSetAddress; - _masterInstanceStore = instanceStoreAddress; - _masterProductStore = productStoreAddress; + $._masterAccessManager = accessManagerAddress; + $._masterInstanceAdmin = address(instanceAdmin); + $._masterInstance = instanceAddress; + $._masterInstanceReader = address(instanceReader); + $._masterInstanceBundleSet = address(bundleSet); + $._masterInstanceRiskSet = address(riskSet); + $._masterInstanceStore = address(instanceStore); + $._masterProductStore = address(productStore); } { - IInstance masterInstance = IInstance(_masterInstance); - IRegistry.ObjectInfo memory info = _registryService.registerInstance(masterInstance, getOwner()); + IInstance masterInstance = IInstance($._masterInstance); + IRegistry.ObjectInfo memory info = $._registryService.registerInstance(masterInstance, getOwner()); masterInstanceNftId = info.nftId; } } @@ -381,22 +388,25 @@ contract InstanceService is virtual onlyOwner { - if(_masterInstanceReader == address(0)) { revert ErrorInstanceServiceMasterInstanceReaderNotSet(); } + InstanceServiceStorage storage $ = _getInstanceServiceStorage(); + + if($._masterInstanceReader == address(0)) { revert ErrorInstanceServiceMasterInstanceReaderNotSet(); } if(instanceReaderAddress == address(0)) { revert ErrorInstanceServiceInstanceReaderAddressZero(); } - if(instanceReaderAddress == _masterInstanceReader) { revert ErrorInstanceServiceInstanceReaderSameAsMasterInstanceReader(); } + if(instanceReaderAddress == $._masterInstanceReader) { revert ErrorInstanceServiceInstanceReaderSameAsMasterInstanceReader(); } InstanceReader instanceReader = InstanceReader(instanceReaderAddress); - if(instanceReader.getInstance() != IInstance(_masterInstance)) { revert ErrorInstanceServiceInstanceReaderInstanceMismatch(); } + if(instanceReader.getInstance() != IInstance($._masterInstance)) { revert ErrorInstanceServiceInstanceReaderInstanceMismatch(); } - _masterInstanceReader = instanceReaderAddress; + $._masterInstanceReader = instanceReaderAddress; emit LogInstanceServiceMasterInstanceReaderUpgraded( - getRegistry().getNftIdForAddress(_masterInstance), + getRegistry().getNftIdForAddress($._masterInstance), instanceReaderAddress); } function getMasterInstanceReader() external virtual view returns (address) { - return _masterInstanceReader; + InstanceServiceStorage storage $ = _getInstanceServiceStorage(); + return $._masterInstanceReader; } //--- internal functions --------------------------------------------------------// @@ -408,14 +418,16 @@ contract InstanceService is virtual returns (InstanceAdmin clonedAdmin) { + InstanceServiceStorage storage $ = _getInstanceServiceStorage(); + // clone instance specific access manager AccessManagerCloneable clonedAccessManager = AccessManagerCloneable( Clones.clone( - InstanceAdmin(_masterInstanceAdmin).authority())); + InstanceAdmin($._masterInstanceAdmin).authority())); // set up the instance admin clonedAdmin = InstanceAdmin( - Clones.clone(_masterInstanceAdmin)); + Clones.clone($._masterInstanceAdmin)); clonedAdmin.initialize( address(clonedAccessManager), @@ -434,16 +446,17 @@ contract InstanceService is virtual returns (IInstance) { + InstanceServiceStorage storage $ = _getInstanceServiceStorage(); // clone instance - Instance clonedInstance = Instance(Clones.clone(_masterInstance)); + Instance clonedInstance = Instance(Clones.clone($._masterInstance)); clonedInstance.initialize( IInstance.InstanceContracts({ instanceAdmin: instanceAdmin, - instanceStore: InstanceStore(Clones.clone(address(_masterInstanceStore))), - productStore: ProductStore(Clones.clone(address(_masterProductStore))), - bundleSet: BundleSet(Clones.clone(_masterInstanceBundleSet)), - riskSet: RiskSet(Clones.clone(_masterInstanceRiskSet)), - instanceReader: InstanceReader(Clones.clone(address(_masterInstanceReader))) + instanceStore: InstanceStore(Clones.clone(address($._masterInstanceStore))), + productStore: ProductStore(Clones.clone(address($._masterProductStore))), + bundleSet: BundleSet(Clones.clone($._masterInstanceBundleSet)), + riskSet: RiskSet(Clones.clone($._masterInstanceRiskSet)), + instanceReader: InstanceReader(Clones.clone(address($._masterInstanceReader))) }), getRegistry(), getRelease(), @@ -470,9 +483,10 @@ contract InstanceService is __Service_init(authority, registry, owner); - _registryService = IRegistryService(_getServiceAddress(REGISTRY())); - _stakingService = IStakingService(_getServiceAddress(STAKING())); - _componentService = IComponentService(_getServiceAddress(COMPONENT())); + InstanceServiceStorage storage $ = _getInstanceServiceStorage(); + $._registryService = IRegistryService(_getServiceAddress(REGISTRY())); + $._stakingService = IStakingService(_getServiceAddress(STAKING())); + $._componentService = IComponentService(_getServiceAddress(COMPONENT())); _registerInterface(type(IInstanceService).interfaceId); } @@ -506,6 +520,11 @@ contract InstanceService is } } + function _getInstanceServiceStorage() private pure returns (InstanceServiceStorage storage $) { + assembly { + $.slot := INSTANCE_SERVICE_STORAGE_LOCATION_V3_0 + } + } // From IService function _getDomain() internal pure override returns(ObjectType) { diff --git a/contracts/oracle/Oracle.sol b/contracts/oracle/Oracle.sol index 45d5afaa0..d67b4dd35 100644 --- a/contracts/oracle/Oracle.sol +++ b/contracts/oracle/Oracle.sol @@ -18,8 +18,8 @@ abstract contract Oracle is InstanceLinkedComponent, IOracleComponent { - // keccak256(abi.encode(uint256(keccak256("etherisc.storage.Oracle")) - 1)) & ~bytes32(uint256(0xff)); - bytes32 public constant ORACLE_STORAGE_LOCATION_V1 = 0xaab7c0ea03d290e56d6c060e0733d3ebcbe647f7694616a2ec52738a64b2f900; + // keccak256(abi.encode(uint256(keccak256("etherisc.gif.Oracle@3.0.0")) - 1)) & ~bytes32(uint256(0xff)); + bytes32 public constant ORACLE_STORAGE_LOCATION_V3_0 = 0x189f66bb26b02fde8f3e50c7c928f45e0131f04edebf4d58daf0aee756df0e00; struct OracleStorage { IComponentService _componentService; @@ -146,7 +146,7 @@ abstract contract Oracle is function _getOracleStorage() private pure returns (OracleStorage storage $) { assembly { - $.slot := ORACLE_STORAGE_LOCATION_V1 + $.slot := ORACLE_STORAGE_LOCATION_V3_0 } } } diff --git a/contracts/pool/BundleService.sol b/contracts/pool/BundleService.sol index 6f78e04be..4ff3e1ec6 100644 --- a/contracts/pool/BundleService.sol +++ b/contracts/pool/BundleService.sol @@ -30,10 +30,15 @@ contract BundleService is string public constant NAME = "BundleService"; - address private _registryAddress; - IRegistryService private _registryService; - IAccountingService private _accountingService; - IComponentService private _componentService; + // keccak256(abi.encode(uint256(keccak256("etherisc.gif.BundleService@3.0.0")) - 1)) & ~bytes32(uint256(0xff)); + bytes32 public constant BUNDLE_SERVICE_STORAGE_LOCATION_V3_0 = 0x46b8ce3500c11673bb6f380e5e46ed88536ecfaa352cb594644cb469c5a14d00; + + struct BundleServiceStorage { + address _registryAddress; + IRegistryService _registryService; + IAccountingService _accountingService; + IComponentService _componentService; + } function _initialize( address owner, @@ -50,9 +55,10 @@ contract BundleService is __Service_init(authority, registry, owner); - _registryService = IRegistryService(_getServiceAddress(REGISTRY())); - _accountingService = IAccountingService(_getServiceAddress(ACCOUNTING())); - _componentService = IComponentService(_getServiceAddress(COMPONENT())); + BundleServiceStorage storage $ = _getBundleServiceStorage(); + $._registryService = IRegistryService(_getServiceAddress(REGISTRY())); + $._accountingService = IAccountingService(_getServiceAddress(ACCOUNTING())); + $._componentService = IComponentService(_getServiceAddress(COMPONENT())); _registerInterface(type(IBundleService).interfaceId); } @@ -100,7 +106,8 @@ contract BundleService is (NftId poolNftId, IInstance instance) = PoolLib.getAndVerifyActivePool(getRegistry(), msg.sender); // register bundle with registry - bundleNftId = _registryService.registerBundle( + BundleServiceStorage storage $ = _getBundleServiceStorage(); + bundleNftId = $._registryService.registerBundle( IRegistry.ObjectInfo( NftIdLib.zero(), poolNftId, @@ -241,7 +248,8 @@ contract BundleService is Amount balanceAmountWithFees = instanceReader.getBalanceAmount(bundleNftId); feeAmount = instanceReader.getFeeAmount(bundleNftId); unstakedAmount = balanceAmountWithFees - feeAmount; - _accountingService.decreaseBundleBalance(instanceStore, bundleNftId, unstakedAmount, feeAmount); + BundleServiceStorage storage $ = _getBundleServiceStorage(); + $._accountingService.decreaseBundleBalance(instanceStore, bundleNftId, unstakedAmount, feeAmount); } emit LogBundleServiceBundleClosed(bundleNftId); @@ -271,7 +279,8 @@ contract BundleService is } // effects - _accountingService.increaseBundleBalance( + BundleServiceStorage storage $ = _getBundleServiceStorage(); + $._accountingService.increaseBundleBalance( instanceStore, bundleNftId, amount, @@ -314,7 +323,8 @@ contract BundleService is } // effects - _accountingService.decreaseBundleBalance( + BundleServiceStorage storage $ = _getBundleServiceStorage(); + $._accountingService.decreaseBundleBalance( instanceStore, bundleNftId, unstakedAmount, @@ -379,6 +389,11 @@ contract BundleService is emit LogBundleServiceCollateralReleased(bundleNftId, policyNftId, collateralAmount); } + function _getBundleServiceStorage() private pure returns (BundleServiceStorage storage $) { + assembly { + $.slot := BUNDLE_SERVICE_STORAGE_LOCATION_V3_0 + } + } function _getDomain() internal pure override returns(ObjectType) { return BUNDLE(); diff --git a/contracts/pool/Pool.sol b/contracts/pool/Pool.sol index 9624ce216..68b8e0cb8 100644 --- a/contracts/pool/Pool.sol +++ b/contracts/pool/Pool.sol @@ -23,8 +23,8 @@ abstract contract Pool is InstanceLinkedComponent, IPoolComponent { - // keccak256(abi.encode(uint256(keccak256("etherisc.storage.Pool")) - 1)) & ~bytes32(uint256(0xff)); - bytes32 public constant POOL_STORAGE_LOCATION_V1 = 0x25e3e51823fbfffb988e0a2744bb93722d9f3e906c07cc0a9e77884c46c58300; + // keccak256(abi.encode(uint256(keccak256("etherisc.gif.Pool@3.0.0")) - 1)) & ~bytes32(uint256(0xff)); + bytes32 public constant POOL_STORAGE_LOCATION_V3_0 = 0xceba0dc57a322ad9ac8622c7bbee3a223850780710333daffeacd4b6d53d6e00; struct PoolStorage { IComponents.PoolInfo _poolInfo; @@ -325,7 +325,7 @@ abstract contract Pool is function _getPoolStorage() private pure returns (PoolStorage storage $) { assembly { - $.slot := POOL_STORAGE_LOCATION_V1 + $.slot := POOL_STORAGE_LOCATION_V3_0 } } } diff --git a/contracts/pool/PoolService.sol b/contracts/pool/PoolService.sol index c3c46a1a6..c87f218b4 100644 --- a/contracts/pool/PoolService.sol +++ b/contracts/pool/PoolService.sol @@ -31,10 +31,15 @@ contract PoolService is Service, IPoolService { - IAccountingService private _accountingService; - IBundleService internal _bundleService; - IComponentService internal _componentService; - IStaking private _staking; + // keccak256(abi.encode(uint256(keccak256("etherisc.gif.PoolService@3.0.0")) - 1)) & ~bytes32(uint256(0xff)); + bytes32 public constant POOL_SERVICE_STORAGE_LOCATION_V3_0 = 0x99b25f02200e1434f4531a215abb8fd8c59ad15f6abb6b06ffa8223eb7162700; + + struct PoolServiceStorage { + IAccountingService _accountingService; + IBundleService _bundleService; + IComponentService _componentService; + IStaking _staking; + } function _initialize( address owner, @@ -51,10 +56,11 @@ contract PoolService is __Service_init(authority, registry, owner); - _accountingService = IAccountingService(_getServiceAddress(ACCOUNTING())); - _bundleService = IBundleService(_getServiceAddress(BUNDLE())); - _componentService = IComponentService(_getServiceAddress(COMPONENT())); - _staking = IStaking(getRegistry().getStakingAddress()); + PoolServiceStorage storage $ = _getPoolServiceStorage(); + $._accountingService = IAccountingService(_getServiceAddress(ACCOUNTING())); + $._bundleService = IBundleService(_getServiceAddress(BUNDLE())); + $._componentService = IComponentService(_getServiceAddress(COMPONENT())); + $._staking = IStaking(getRegistry().getStakingAddress()); _registerInterface(type(IPoolService).interfaceId); } @@ -90,9 +96,10 @@ contract PoolService is // TODO get performance fee for pool (#477) // releasing collateral in bundle - (Amount unstakedAmount, Amount feeAmount) = _bundleService.close(instance, bundleNftId); + PoolServiceStorage storage $ = _getPoolServiceStorage(); + (Amount unstakedAmount, Amount feeAmount) = $._bundleService.close(instance, bundleNftId); - _accountingService.decreasePoolBalance( + $._accountingService.decreasePoolBalance( instance.getInstanceStore(), poolNftId, unstakedAmount + feeAmount, @@ -205,13 +212,14 @@ contract PoolService is amount); // do all the book keeping - _accountingService.increasePoolBalance( + PoolServiceStorage storage $ = _getPoolServiceStorage(); + $._accountingService.increasePoolBalance( instanceStore, poolNftId, netAmount, feeAmount); - _bundleService.stake(instanceReader, instanceStore, bundleNftId, netAmount); + $._bundleService.stake(instanceReader, instanceStore, bundleNftId, netAmount); emit LogPoolServiceBundleStaked(instanceNftId, poolNftId, bundleNftId, amount, netAmount); @@ -244,7 +252,8 @@ contract PoolService is ) = PoolLib.checkAndGetPoolInfo(getRegistry(), msg.sender, bundleNftId); // call bundle service for bookkeeping and additional checks - Amount unstakedAmount = _bundleService.unstake(instanceStore, bundleNftId, amount); + PoolServiceStorage storage $ = _getPoolServiceStorage(); + Amount unstakedAmount = $._bundleService.unstake(instanceStore, bundleNftId, amount); // Important: from now on work only with unstakedAmount as it is the only reliable amount. // if amount was max, this was set to the available amount @@ -252,8 +261,8 @@ contract PoolService is // TODO: handle performance fees (issue #477) netAmount = unstakedAmount; - // update pool bookkeeping - performance fees stay in the pool, but as fees - _accountingService.decreasePoolBalance( + // update pool bookkeeping - performance fees stay in the pool, but as fees + $._accountingService.decreasePoolBalance( instanceStore, poolNftId, unstakedAmount, @@ -350,13 +359,14 @@ contract PoolService is Amount bundleNetAmount = premium.netPremiumAmount; InstanceStore instanceStore = instance.getInstanceStore(); - _accountingService.increasePoolBalance( + PoolServiceStorage storage $ = _getPoolServiceStorage(); + $._accountingService.increasePoolBalance( instanceStore, poolNftId, bundleNetAmount + bundleFeeAmount, poolFeeAmount); - _accountingService.increaseBundleBalanceForPool( + $._accountingService.increaseBundleBalanceForPool( instanceStore, bundleNftId, bundleNetAmount, @@ -399,14 +409,15 @@ contract PoolService is sumInsuredAmount); // lock collateral amount from involved bundle - _bundleService.lockCollateral( + PoolServiceStorage storage $ = _getPoolServiceStorage(); + $._bundleService.lockCollateral( instance, applicationNftId, bundleNftId, localCollateralAmount); // update value locked with staking service - _staking.increaseTotalValueLocked( + $._staking.increaseTotalValueLocked( instance.getNftId(), token, totalCollateralAmount); @@ -455,20 +466,21 @@ contract PoolService is // effects NftId poolNftId = getRegistry().getParentNftId(bundleNftId); + PoolServiceStorage storage $ = _getPoolServiceStorage(); - _accountingService.decreasePoolBalance( + $._accountingService.decreasePoolBalance( instanceStore, poolNftId, payoutAmount, AmountLib.zero()); - _accountingService.decreaseBundleBalanceForPool( + $._accountingService.decreaseBundleBalanceForPool( instanceStore, bundleNftId, payoutAmount, AmountLib.zero()); - _bundleService.releaseCollateral( + $._bundleService.releaseCollateral( instanceStore, policyNftId, bundleNftId, @@ -477,7 +489,7 @@ contract PoolService is // update value locked with staking service TokenHandler poolTokenHandler = instanceReader.getTokenHandler(poolNftId); - _staking.decreaseTotalValueLocked( + $._staking.decreaseTotalValueLocked( instanceReader.getInstanceNftId(), address(poolTokenHandler.TOKEN()), payoutAmount); @@ -494,7 +506,7 @@ contract PoolService is payoutBeneficiary); if (processingFeeAmount.gtz()) { - _accountingService.increaseProductFeesForPool( + $._accountingService.increaseProductFeesForPool( instanceStore, productNftId, processingFeeAmount); @@ -541,10 +553,11 @@ contract PoolService is // decrease fee counters by withdrawnAmount { InstanceStore store = instance.getInstanceStore(); + PoolServiceStorage storage $ = _getPoolServiceStorage(); // decrease fee amount of the bundle - _accountingService.decreaseBundleBalanceForPool(store, bundleNftId, AmountLib.zero(), withdrawnAmount); + $._accountingService.decreaseBundleBalanceForPool(store, bundleNftId, AmountLib.zero(), withdrawnAmount); // decrease pool balance - _accountingService.decreasePoolBalance(store, poolNftId, withdrawnAmount, AmountLib.zero()); + $._accountingService.decreasePoolBalance(store, poolNftId, withdrawnAmount, AmountLib.zero()); } // interactions @@ -575,7 +588,9 @@ contract PoolService is Amount remainingCollateralAmount = policyInfo.sumInsuredAmount - policyInfo.claimAmount; - _bundleService.releaseCollateral( + // effects + PoolServiceStorage storage $ = _getPoolServiceStorage(); + $._bundleService.releaseCollateral( instance.getInstanceStore(), policyNftId, policyInfo.bundleNftId, @@ -583,7 +598,7 @@ contract PoolService is // update value locked with staking service InstanceReader instanceReader = instance.getInstanceReader(); - _staking.decreaseTotalValueLocked( + $._staking.decreaseTotalValueLocked( instanceReader.getInstanceNftId(), address(instanceReader.getToken(policyInfo.productNftId)), remainingCollateralAmount); @@ -607,6 +622,12 @@ contract PoolService is return PoolLib.getAndVerifyActivePool(getRegistry(), msg.sender); } + function _getPoolServiceStorage() private pure returns (PoolServiceStorage storage $) { + assembly { + $.slot := POOL_SERVICE_STORAGE_LOCATION_V3_0 + } + } + function _getDomain() internal pure override returns(ObjectType) { return POOL(); diff --git a/contracts/product/ApplicationService.sol b/contracts/product/ApplicationService.sol index 68e9e243f..e416a33a2 100644 --- a/contracts/product/ApplicationService.sol +++ b/contracts/product/ApplicationService.sol @@ -28,9 +28,14 @@ contract ApplicationService is Service, IApplicationService { - IDistributionService private _distributionService; - IPricingService private _pricingService; - IRegistryService private _registryService; + // keccak256(abi.encode(uint256(keccak256("etherisc.gif.ApplicationService@3.0.0")) - 1)) & ~bytes32(uint256(0xff)); + bytes32 public constant APPLICATION_SERVICE_STORAGE_LOCATION_V3_0 = 0x24398a4c04c22224171b7c40b992e9eaa2a85b3256e3f65fd9ba6cedb9459300; + + struct ApplicationServiceStorage { + IDistributionService _distributionService; + IPricingService _pricingService; + IRegistryService _registryService; + } function _initialize( address owner, @@ -47,9 +52,10 @@ contract ApplicationService is __Service_init(authority, registry, owner); - _distributionService = IDistributionService(_getServiceAddress(DISTRIBUTION())); - _pricingService = IPricingService(_getServiceAddress(PRICE())); - _registryService = IRegistryService(_getServiceAddress(REGISTRY())); + ApplicationServiceStorage storage $ = _getApplicationServiceStorage(); + $._distributionService = IDistributionService(_getServiceAddress(DISTRIBUTION())); + $._pricingService = IPricingService(_getServiceAddress(PRICE())); + $._registryService = IRegistryService(_getServiceAddress(REGISTRY())); _registerInterface(type(IApplicationService).interfaceId); } @@ -88,7 +94,8 @@ contract ApplicationService is // check referral with distribution { if (productInfo.hasDistribution && ! referralId.eqz()) { - if (!_distributionService.referralIsValid(productInfo.distributionNftId, referralId)) { + ApplicationServiceStorage storage $ = _getApplicationServiceStorage(); + if (!$._distributionService.referralIsValid(productInfo.distributionNftId, referralId)) { revert ErrorApplicationServiceReferralInvalid(productNftId, productInfo.distributionNftId, referralId); } } @@ -113,7 +120,8 @@ contract ApplicationService is applicationOwner, ""); - applicationNftId = _registryService.registerPolicy(objectInfo); + ApplicationServiceStorage storage $ = _getApplicationServiceStorage(); + applicationNftId = $._registryService.registerPolicy(objectInfo); } @@ -125,7 +133,9 @@ contract ApplicationService is view returns (Amount premiumAmount) { - return _pricingService.calculatePremium( + ApplicationServiceStorage storage $ = _getApplicationServiceStorage(); + + return $._pricingService.calculatePremium( info.productNftId, info.riskId, info.sumInsuredAmount, @@ -320,6 +330,12 @@ contract ApplicationService is instance = IInstance(instanceAddress); } + function _getApplicationServiceStorage() private pure returns (ApplicationServiceStorage storage $) { + // solhint-disable-next-line no-inline-assembly + assembly { + $.slot := APPLICATION_SERVICE_STORAGE_LOCATION_V3_0 + } + } function _getDomain() internal pure override returns(ObjectType) { return APPLICATION(); diff --git a/contracts/product/ClaimService.sol b/contracts/product/ClaimService.sol index 7cfa40f8f..4fb5e2e31 100644 --- a/contracts/product/ClaimService.sol +++ b/contracts/product/ClaimService.sol @@ -29,9 +29,13 @@ contract ClaimService is Service, IClaimService { + // keccak256(abi.encode(uint256(keccak256("etherisc.gif.ClaimService@3.0.0")) - 1)) & ~bytes32(uint256(0xff)); + bytes32 public constant CLAIM_SERVICE_STORAGE_LOCATION_V3_0 = 0x4771e227a17c94c5fcc3dc9ac7f68c26e56ef575e6191f6a42c4520a600ee000; - IPolicyService internal _policyService; - IPoolService internal _poolService; + struct ClaimServiceStorage { + IPolicyService _policyService; + IPoolService _poolService; + } function _initialize( address owner, @@ -48,8 +52,9 @@ contract ClaimService is __Service_init(authority, registry, owner); - _policyService = IPolicyService(getRegistry().getServiceAddress(POLICY(), getVersion().toMajorPart())); - _poolService = IPoolService(getRegistry().getServiceAddress(POOL(), getVersion().toMajorPart())); + ClaimServiceStorage storage $ = _getClaimServiceStorage(); + $._policyService = IPolicyService(getRegistry().getServiceAddress(POLICY(), getVersion().toMajorPart())); + $._poolService = IPoolService(getRegistry().getServiceAddress(POOL(), getVersion().toMajorPart())); _registerInterface(type(IClaimService).interfaceId); } @@ -140,7 +145,8 @@ contract ClaimService is // should policy still be active it needs to become expired if (policyInfo.claimAmount >= policyInfo.sumInsuredAmount) { - _policyService.expirePolicy(instance, policyNftId, TimestampLib.current()); + ClaimServiceStorage storage $ = _getClaimServiceStorage(); + $._policyService.expirePolicy(instance, policyNftId, TimestampLib.current()); } emit LogClaimServiceClaimConfirmed(policyNftId, claimId, confirmedAmount); @@ -314,39 +320,40 @@ contract ClaimService is IPolicy.PolicyInfo memory policyInfo ) = _verifyCallerWithPolicy(policyNftId); - IPolicy.ClaimInfo memory claimInfo; - address payoutBeneficiary; - Amount payoutAmount; - + // check that payout exists and is open { - // check that payout exists and is open - IPolicy.PayoutInfo memory payoutInfo = instanceContracts.instanceReader.getPayoutInfo(policyNftId, payoutId); - payoutBeneficiary = payoutInfo.beneficiary; - payoutAmount = payoutInfo.amount; StateId payoutState = instanceContracts.instanceReader.getPayoutState(policyNftId, payoutId); if(payoutState != EXPECTED()) { revert ErrorClaimServicePayoutNotExpected(policyNftId, payoutId, payoutState); } + } + + Amount payoutAmount; + address payoutBeneficiary; + + { + ClaimId claimId = payoutId.toClaimId(); + IPolicy.ClaimInfo memory claimInfo = instanceContracts.instanceReader.getClaimInfo(policyNftId, claimId); + IPolicy.PayoutInfo memory payoutInfo = instanceContracts.instanceReader.getPayoutInfo(policyNftId, payoutId); + + payoutAmount = payoutInfo.amount; + payoutBeneficiary = payoutInfo.beneficiary; // check that payout amount does not violate claim amount - claimInfo = instanceContracts.instanceReader.getClaimInfo(policyNftId, payoutId.toClaimId()); - if(claimInfo.paidAmount + payoutInfo.amount > claimInfo.claimAmount) { + if(claimInfo.paidAmount + payoutAmount > claimInfo.claimAmount) { revert ErrorClaimServicePayoutExceedsClaimAmount( policyNftId, - payoutId.toClaimId(), + claimId, claimInfo.claimAmount, - claimInfo.paidAmount + payoutInfo.amount); + claimInfo.paidAmount + payoutAmount); } // effects // update and save payout info with instance payoutInfo.paidAt = TimestampLib.current(); instanceContracts.productStore.updatePayout(policyNftId, payoutId, payoutInfo, PAID()); - } - // update and save claim info with instance - { - ClaimId claimId = payoutId.toClaimId(); + // update and save claim info with instance claimInfo.paidAmount = claimInfo.paidAmount.add(payoutAmount); claimInfo.openPayoutsCount -= 1; @@ -370,15 +377,18 @@ contract ClaimService is // effects + interactions (push tokens to beneficiary, product) // delegate to pool to update book keeping and moving tokens payout - (netPayoutAmount, processingFeeAmount) = _poolService.processPayout( - instanceContracts.instanceReader, - instanceContracts.instanceStore, - policyInfo.productNftId, // product nft id - policyNftId, - policyInfo.bundleNftId, - payoutId, - payoutAmount, - payoutBeneficiary); + { + ClaimServiceStorage storage $ = _getClaimServiceStorage(); + (netPayoutAmount, processingFeeAmount) = $._poolService.processPayout( + instanceContracts.instanceReader, + instanceContracts.instanceStore, + policyInfo.productNftId, // product nft id + policyNftId, + policyInfo.bundleNftId, + payoutId, + payoutAmount, + payoutBeneficiary); + } } function cancelPayout( @@ -619,6 +629,12 @@ contract ClaimService is } } + function _getClaimServiceStorage() private pure returns (ClaimServiceStorage storage $) { + // solhint-disable-next-line no-inline-assembly + assembly { + $.slot := CLAIM_SERVICE_STORAGE_LOCATION_V3_0 + } + } function _getDomain() internal pure override returns(ObjectType) { return CLAIM(); diff --git a/contracts/product/PolicyService.sol b/contracts/product/PolicyService.sol index d3db85340..381ec5a23 100644 --- a/contracts/product/PolicyService.sol +++ b/contracts/product/PolicyService.sol @@ -33,11 +33,16 @@ contract PolicyService is Service, IPolicyService { - IAccountingService private _accountingService; - IComponentService internal _componentService; - IDistributionService internal _distributionService; - IPoolService internal _poolService; - IPricingService internal _pricingService; + // keccak256(abi.encode(uint256(keccak256("etherisc.gif.PolicyService@3.0.0")) - 1)) & ~bytes32(uint256(0xff)); + bytes32 private constant POLICY_SERVICE_STORAGE_LOCATION_V3_0 = 0x862c7110061e72e577b74a9231a4d5d6d7c0698598767dc8092f941bca614500; + + struct PolicyServiceStorage { + IAccountingService _accountingService; + IComponentService _componentService; + IDistributionService _distributionService; + IPoolService _poolService; + IPricingService _pricingService; + } function _initialize( address owner, @@ -55,11 +60,12 @@ contract PolicyService is __Service_init(authority, registry, 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)); - _pricingService = IPricingService(getRegistry().getServiceAddress(PRICE(), majorVersion)); + PolicyServiceStorage storage $ = _getPolicyServiceStorage(); + $._accountingService = IAccountingService(getRegistry().getServiceAddress(ACCOUNTING(), majorVersion)); + $._componentService = IComponentService(getRegistry().getServiceAddress(COMPONENT(), majorVersion)); + $._poolService = IPoolService(getRegistry().getServiceAddress(POOL(), majorVersion)); + $._distributionService = IDistributionService(getRegistry().getServiceAddress(DISTRIBUTION(), majorVersion)); + $._pricingService = IPricingService(getRegistry().getServiceAddress(PRICE(), majorVersion)); _registerInterface(type(IPolicyService).interfaceId); } @@ -116,69 +122,83 @@ contract PolicyService is revert ErrorPolicyServicePolicyStateNotApplied(applicationNftId); } - // effects - // actual collateralizaion - _poolService.lockCollateral( - instance, - address(instanceReader.getToken(productNftId)), - productNftId, - applicationNftId, - applicationInfo.bundleNftId, - applicationInfo.sumInsuredAmount); + PolicyServiceStorage storage $ = _getPolicyServiceStorage(); - // optional activation of policy - if(activateAt.gtz()) { - applicationInfo = PolicyServiceLib.activate(applicationNftId, applicationInfo, activateAt); - } + { + RiskId riskId = applicationInfo.riskId; + NftId bundleNftId = applicationInfo.bundleNftId; + + { + // calculate premium + IPolicy.PremiumInfo memory premium = $._pricingService.calculatePremium( + productNftId, + riskId, + applicationInfo.sumInsuredAmount, + applicationInfo.lifetime, + applicationInfo.applicationData, + bundleNftId, + applicationInfo.referralId); - // update policy and set state to collateralized - instance.getProductStore().updatePolicy( - applicationNftId, - applicationInfo, - COLLATERALIZED()); - - // calculate and store premium - RiskId riskId = applicationInfo.riskId; - NftId bundleNftId = applicationInfo.bundleNftId; - - IPolicy.PremiumInfo memory premium = _pricingService.calculatePremium( - productNftId, - riskId, - applicationInfo.sumInsuredAmount, - applicationInfo.lifetime, - applicationInfo.applicationData, - bundleNftId, - applicationInfo.referralId); - - if (premium.premiumAmount > maxPremiumAmount) { - revert LogPolicyServiceMaxPremiumAmountExceeded( + premiumAmount = premium.premiumAmount; + + // check calculated premium does not exceed max premium amount + if (premiumAmount > maxPremiumAmount) { + revert LogPolicyServiceMaxPremiumAmountExceeded( + applicationNftId, + maxPremiumAmount, + premiumAmount); + } + + // create premium in instance + instance.getProductStore().createPremium( + applicationNftId, + premium); + } + + // optional activation of policy + if(activateAt.gtz()) { + applicationInfo = PolicyServiceLib.activate(applicationNftId, applicationInfo, activateAt); + } + + // TODO do not write applicationInfo if no changes where made -> add/use updatePolicyState() + // update policy and set state to collateralized + instance.getProductStore().updatePolicy( applicationNftId, - maxPremiumAmount, - premium.premiumAmount); - } + applicationInfo, + COLLATERALIZED()); + + // link policy to risk and bundle + { + NftId poolNftId = getRegistry().getParentNftId(bundleNftId); + instance.getRiskSet().linkPolicy(productNftId, riskId, applicationNftId); + instance.getBundleSet().linkPolicy( + poolNftId,//getRegistry().getParentNftId(bundleNftId), + bundleNftId, + applicationNftId); + } - premiumAmount = premium.premiumAmount; - instance.getProductStore().createPremium( - applicationNftId, - premium); + // actual collateralizaion + $._poolService.lockCollateral( + instance, + address(instanceReader.getToken(productNftId)), + productNftId, + applicationNftId, + bundleNftId, + applicationInfo.sumInsuredAmount); + } - // update referral counter if product has linked distributino component + // update referral counter if product has linked distribution component { IComponents.ProductInfo memory productInfo = instanceReader.getProductInfo(productNftId); if (productInfo.hasDistribution) { - _distributionService.processReferral( + $._distributionService.processReferral( productInfo.distributionNftId, applicationInfo.referralId); } } - // link policy to risk and bundle - NftId poolNftId = getRegistry().getParentNftId(bundleNftId); - instance.getRiskSet().linkPolicy(productNftId, riskId, applicationNftId); - instance.getBundleSet().linkPolicy(poolNftId, bundleNftId, applicationNftId); - // log policy creation before interactions with token and policy holder - emit LogPolicyServicePolicyCreated(applicationNftId, premium.premiumAmount, activateAt); + emit LogPolicyServicePolicyCreated(applicationNftId, premiumAmount, activateAt); // interactions // callback to policy holder if applicable @@ -404,9 +424,11 @@ contract PolicyService is revert ErrorPolicyServicePremiumNotPaid(policyNftId, policyInfo.premiumAmount); } + PolicyServiceStorage storage $ = _getPolicyServiceStorage(); + // effects // release (remaining) collateral that was blocked by policy - _poolService.releaseCollateral( + $._poolService.releaseCollateral( instance, policyNftId, policyInfo); @@ -469,22 +491,24 @@ contract PolicyService is instanceReader, productNftId); + PolicyServiceStorage storage $ = _getPolicyServiceStorage(); + // update product fees, distribution and pool fees - _accountingService.increaseProductFees( + $._accountingService.increaseProductFees( instanceStore, productNftId, premium.productFeeVarAmount + premium.productFeeFixAmount); // update distribution fees and distributor commission and pool fees if (!distributionNftId.eqz()) { // only call distribution service if a distribution component is connected to the product - _distributionService.processSale( + $._distributionService.processSale( distributionNftId, referralId, premium); } // update pool and bundle fees - _poolService.processSale( + $._poolService.processSale( bundleNftId, premium); } @@ -631,6 +655,8 @@ contract PolicyService is policyInfo = instance.getInstanceReader().getPolicyInfo(policyNftId); if (policyInfo.productNftId != productNftId) { + // TODO rename: ...or productNftId does not exist + // check with registry instead of store revert ErrorPolicyServicePolicyProductMismatch( policyNftId, productNftId, @@ -638,6 +664,13 @@ contract PolicyService is } } + function _getPolicyServiceStorage() private pure returns (PolicyServiceStorage storage $) { + // solhint-disable-next-line no-inline-assembly + assembly { + $.slot := POLICY_SERVICE_STORAGE_LOCATION_V3_0 + } + } + function _getDomain() internal pure override returns(ObjectType) { return POLICY(); diff --git a/contracts/product/PricingService.sol b/contracts/product/PricingService.sol index fc0f7dfd4..b887202b6 100644 --- a/contracts/product/PricingService.sol +++ b/contracts/product/PricingService.sol @@ -28,7 +28,12 @@ contract PricingService is Service, IPricingService { - IDistributionService internal _distributionService; + // keccak256(abi.encode(uint256(keccak256("etherisc.gif.PricingService@3.0.0")) - 1)) & ~bytes32(uint256(0xff)); + bytes32 public constant PRICING_SERVICE_STORAGE_LOCATION_V3_0 = 0x69339118ac24f243948ace3f64dcbea8699f7f0061492066c280ac597f7bd500; + + struct PricingServiceStorage { + IDistributionService _distributionService; + } function _initialize( address owner, @@ -45,7 +50,8 @@ contract PricingService is __Service_init(authority, registry, owner); - _distributionService = IDistributionService(_getServiceAddress(DISTRIBUTION())); + PricingServiceStorage storage $ = _getPricingServiceStorage(); + $._distributionService = IDistributionService(_getServiceAddress(DISTRIBUTION())); _registerInterface(type(IPricingService).interfaceId); } @@ -243,9 +249,9 @@ contract PricingService is view returns (IPolicy.PremiumInfo memory finalPremium) { - + PricingServiceStorage storage $ = _getPricingServiceStorage(); // if the referral is not valid, then the distribution owner gets everything - if (distributionNftId.eqz() || ! _distributionService.referralIsValid(distributionNftId, referralId)) { + if (distributionNftId.eqz() || ! $._distributionService.referralIsValid(distributionNftId, referralId)) { premium.distributionOwnerFeeFixAmount = premium.distributionFeeFixAmount; premium.distributionOwnerFeeVarAmount = premium.distributionFeeVarAmount; premium.premiumAmount = premium.fullPremiumAmount; @@ -254,7 +260,7 @@ contract PricingService is Fee memory minDistributionOwnerFee = feeInfo.minDistributionOwnerFee; - // if the referral is valid, the the commission and discount are calculated based in the full premium + // if the referral is valid, then the commission and discount are calculated based in the full premium // the remaing amount goes to the distribution owner { IDistribution.ReferralInfo memory referralInfo = reader.getReferralInfo(referralId); @@ -299,6 +305,12 @@ contract PricingService is premiumWithTargetWalletAmounts = premium; } + function _getPricingServiceStorage() private pure returns (PricingServiceStorage storage $) { + // solhint-disable-next-line no-inline-assembly + assembly { + $.slot := PRICING_SERVICE_STORAGE_LOCATION_V3_0 + } + } function _getDomain() internal pure override returns(ObjectType) { return PRICE(); diff --git a/contracts/product/Product.sol b/contracts/product/Product.sol index 4a7382e07..575ba95b3 100644 --- a/contracts/product/Product.sol +++ b/contracts/product/Product.sol @@ -30,8 +30,8 @@ abstract contract Product is InstanceLinkedComponent, IProductComponent { - // keccak256(abi.encode(uint256(keccak256("etherisc.storage.Product")) - 1)) & ~bytes32(uint256(0xff)); - bytes32 public constant PRODUCT_STORAGE_LOCATION_V1 = 0x0bb7aafdb8e380f81267337bc5b5dfdf76e6d3a380ecadb51ec665246d9d6800; + // keccak256(abi.encode(uint256(keccak256("etherisc.gif.Product@3.0.0")) - 1)) & ~bytes32(uint256(0xff)); + bytes32 public constant PRODUCT_STORAGE_LOCATION_V3_0 = 0x3261127dd9d5619cc84045fb5cff6ef7871eb2df664d756420362d0f872a4600; struct ProductStorage { IComponents.ProductInfo _productInfo; @@ -485,7 +485,7 @@ abstract contract Product is function _getProductStorage() internal virtual pure returns (ProductStorage storage $) { assembly { - $.slot := PRODUCT_STORAGE_LOCATION_V1 + $.slot := PRODUCT_STORAGE_LOCATION_V3_0 } } } \ No newline at end of file diff --git a/contracts/shared/Component.sol b/contracts/shared/Component.sol index 6d65024c6..c4474dcb2 100644 --- a/contracts/shared/Component.sol +++ b/contracts/shared/Component.sol @@ -19,8 +19,8 @@ abstract contract Component is Registerable, IComponent { - // keccak256(abi.encode(uint256(keccak256("gif-next.contracts.component.Component.sol")) - 1)) & ~bytes32(uint256(0xff)); - bytes32 public constant COMPONENT_LOCATION_V1 = 0xffe8d4462baed26a47154f4b8f6db497d2f772496965791d25bd456e342b7f00; + // keccak256(abi.encode(uint256(keccak256("etherisc.gif.Component@3.0.0")) - 1)) & ~bytes32(uint256(0xff)); + bytes32 public constant COMPONENT_STORAGE_LOCATION_V3_0 = 0xfc96c0569508c55c4a252d7e9dd1f9d0347413a3bb20065fd534643ada195b00; struct ComponentStorage { string _name; // unique (per instance) component name @@ -40,7 +40,7 @@ abstract contract Component is function _getComponentStorage() private pure returns (ComponentStorage storage $) { // solhint-disable-next-line no-inline-assembly assembly { - $.slot := COMPONENT_LOCATION_V1 + $.slot := COMPONENT_STORAGE_LOCATION_V3_0 } } diff --git a/contracts/shared/ComponentService.sol b/contracts/shared/ComponentService.sol index ef405f3cf..da63f3a6c 100644 --- a/contracts/shared/ComponentService.sol +++ b/contracts/shared/ComponentService.sol @@ -39,9 +39,14 @@ contract ComponentService is bool private constant INCREASE = true; bool private constant DECREASE = false; - IAccountingService private _accountingService; - IRegistryService private _registryService; - IStaking private _staking; + // keccak256(abi.encode(uint256(keccak256("etherisc.gif.ComponentService@3.0.0")) - 1)) & ~bytes32(uint256(0xff)); + bytes32 public constant COMPONENT_SERVICE_STORAGE_LOCATION_V3_0 = 0xc5533ae48eb96dccabcb7c228b271a453799a15cdbe5e61ead04b4ec1b7d9c00; + + struct ComponentServiceStorage { + IAccountingService _accountingService; + IRegistryService _registryService; + IStaking _staking; + } function _initialize( address owner, @@ -58,9 +63,10 @@ contract ComponentService is __Service_init(authority, registry, owner); - _accountingService = IAccountingService(_getServiceAddress(ACCOUNTING())); - _registryService = IRegistryService(_getServiceAddress(REGISTRY())); - _staking = IStakingService(_getServiceAddress(STAKING())).getStaking(); + ComponentServiceStorage storage $ = _getComponentServiceStorage(); + $._accountingService = IAccountingService(_getServiceAddress(ACCOUNTING())); + $._registryService = IRegistryService(_getServiceAddress(REGISTRY())); + $._staking = IStakingService(_getServiceAddress(STAKING())).getStaking(); _registerInterface(type(IComponentService).interfaceId); } @@ -178,7 +184,8 @@ contract ComponentService is // effects // decrease fee counters by withdrawnAmount - _accountingService.decreaseComponentFees( + ComponentServiceStorage storage $ = _getComponentServiceStorage(); + $._accountingService.decreaseComponentFees( instance.getInstanceStore(), componentNftId, withdrawnAmount); @@ -231,7 +238,8 @@ contract ComponentService is token); // add product specific token for product to staking - _staking.addTargetToken( + ComponentServiceStorage storage $ = _getComponentServiceStorage(); + $._staking.addTargetToken( instanceNftId, token); } @@ -495,12 +503,12 @@ contract ComponentService is componentAddress, parentNftId); - InstanceStore instanceStore = instance.getInstanceStore(); ObjectType componentType = objectInfo.objectType; + ComponentServiceStorage storage $ = _getComponentServiceStorage(); if(componentType == PRODUCT()) { // register product with registry - componentNftId = _registryService.registerProduct( + componentNftId = $._registryService.registerProduct( component, objectInfo.initialOwner).nftId; @@ -508,7 +516,7 @@ contract ComponentService is _createProduct(instance.getProductStore(), componentNftId, componentAddress); } else { // register non product component with registry - componentNftId = _registryService.registerProductLinkedComponent( + componentNftId = $._registryService.registerProductLinkedComponent( component, objectInfo.objectType, objectInfo.initialOwner).nftId; @@ -519,7 +527,7 @@ contract ComponentService is NftId productNftId = parentNftId; IComponents.ProductInfo memory productInfo = instanceReader.getProductInfo(productNftId); if(componentType == POOL()) { - _createPool(instanceStore, instance.getProductStore(), productNftId, componentNftId, componentAddress, productInfo); + _createPool(instance.getInstanceStore(), instance.getProductStore(), productNftId, componentNftId, componentAddress, productInfo); } else if(componentType == DISTRIBUTION()) { _createDistribution(instance.getProductStore(), productNftId, componentNftId, productInfo); } else if(componentType == ORACLE()) { @@ -545,7 +553,7 @@ contract ComponentService is instanceAdmin.authority()); // register component with instance - instanceStore.createComponent( + instance.getInstanceStore().createComponent( componentNftId, componentInfo); @@ -559,7 +567,7 @@ contract ComponentService is instance.getNftId(), componentNftId, componentType, - address(component), + componentAddress, token, objectInfo.initialOwner); } @@ -684,6 +692,12 @@ contract ComponentService is instance = IInstance(instanceAddress); } + function _getComponentServiceStorage() private pure returns (ComponentServiceStorage storage $) { + // solhint-disable-next-line no-inline-assembly + assembly { + $.slot := COMPONENT_SERVICE_STORAGE_LOCATION_V3_0 + } + } function _getDomain() internal pure virtual override returns(ObjectType) { return COMPONENT(); diff --git a/contracts/shared/InitializableERC165.sol b/contracts/shared/InitializableERC165.sol index 11750040a..1f1dee4c3 100644 --- a/contracts/shared/InitializableERC165.sol +++ b/contracts/shared/InitializableERC165.sol @@ -8,7 +8,12 @@ contract InitializableERC165 is Initializable, IERC165 { - mapping(bytes4 => bool) private _isSupported; + // keccak256(abi.encode(uint256(keccak256("etherisc.gif.InitializableERC165@3.0.0")) - 1)) & ~bytes32(uint256(0xff)); + bytes32 public constant INITIALIZABLE_ERC165_STORAGE_LOCATION_V3_0 = 0x2c5de3b4947d23a15785cdf804e1e581a3c5f54c0357f66e3f442def0f390100; + + struct InitializableERC165Storage { + mapping(bytes4 => bool) _isSupported; + } // @dev initializes with support for ERC165 function __ERC165_init() internal onlyInitializing() { @@ -16,7 +21,8 @@ contract InitializableERC165 is } function _initializeERC165() internal { - _isSupported[type(IERC165).interfaceId] = true; + InitializableERC165Storage storage $ = _getInitializableERC165Storage(); + $._isSupported[type(IERC165).interfaceId] = true; } // @dev register support for provided interfaceId @@ -26,10 +32,18 @@ contract InitializableERC165 is } function _registerInterfaceNotInitializing(bytes4 interfaceId) internal{ - _isSupported[interfaceId] = true; + InitializableERC165Storage storage $ = _getInitializableERC165Storage(); + $._isSupported[interfaceId] = true; } function supportsInterface(bytes4 interfaceId) external view override returns (bool) { - return _isSupported[interfaceId]; + InitializableERC165Storage storage $ = _getInitializableERC165Storage(); + return $._isSupported[interfaceId]; + } + + function _getInitializableERC165Storage() private pure returns (InitializableERC165Storage storage $) { + assembly { + $.slot := INITIALIZABLE_ERC165_STORAGE_LOCATION_V3_0 + } } } \ No newline at end of file diff --git a/contracts/shared/InstanceLinkedComponent.sol b/contracts/shared/InstanceLinkedComponent.sol index f0e02d879..b76c6e6ac 100644 --- a/contracts/shared/InstanceLinkedComponent.sol +++ b/contracts/shared/InstanceLinkedComponent.sol @@ -30,8 +30,8 @@ abstract contract InstanceLinkedComponent is Component, IInstanceLinkedComponent { - // keccak256(abi.encode(uint256(keccak256("gif-next.contracts.component.Component.sol")) - 1)) & ~bytes32(uint256(0xff)); - bytes32 public constant INSTANCE_LINKED_COMPONENT_LOCATION_V1 = 0xffe3d4462bded26a47154f4b8f6db494d2f772496965791d25bd456e342b7f00; + // keccak256(abi.encode(uint256(keccak256("etherisc.gif.InstanceLinkedComponent@3.0.0")) - 1)) & ~bytes32(uint256(0xff)); + bytes32 public constant INSTANCE_LINKED_COMPONENT_STORAGE_LOCATION_V3_0 = 0xdc3a75afc3e621184afdbd532600dd89ad5b8ed15ddda6ef096bf0aad1761400; struct InstanceLinkedComponentStorage { IInstance _instance; // instance for this component @@ -63,7 +63,7 @@ abstract contract InstanceLinkedComponent is function _getInstanceLinkedComponentStorage() private pure returns (InstanceLinkedComponentStorage storage $) { assembly { - $.slot := INSTANCE_LINKED_COMPONENT_LOCATION_V1 + $.slot := INSTANCE_LINKED_COMPONENT_STORAGE_LOCATION_V3_0 } } diff --git a/contracts/shared/NftOwnable.sol b/contracts/shared/NftOwnable.sol index 0caf8dc05..74290d5ce 100644 --- a/contracts/shared/NftOwnable.sol +++ b/contracts/shared/NftOwnable.sol @@ -12,8 +12,8 @@ contract NftOwnable is RegistryLinked, INftOwnable { - // keccak256(abi.encode(uint256(keccak256("etherisc.storage.NftOwnable")) - 1)) & ~bytes32(uint256(0xff)); - bytes32 public constant NFT_OWNABLE_STORAGE_LOCATION_V1 = 0x07ebcf49758b6ed3af50fa146bec0abe157c0218fe65dc0874c286e9d5da4f00; + // keccak256(abi.encode(uint256(keccak256("etherisc.storage.NftOwnable@3.0.0")) - 1)) & ~bytes32(uint256(0xff)); + bytes32 public constant NFT_OWNABLE_STORAGE_LOCATION_V3_0 = 0x75a854a54bf614cac5ba72c2235143229b43a26f8eb448270fb97a467532e300; struct NftOwnableStorage { NftId _nftId; @@ -123,7 +123,7 @@ contract NftOwnable is function _getNftOwnableStorage() private pure returns (NftOwnableStorage storage $) { // solhint-disable-next-line no-inline-assembly assembly { - $.slot := NFT_OWNABLE_STORAGE_LOCATION_V1 + $.slot := NFT_OWNABLE_STORAGE_LOCATION_V3_0 } } } \ No newline at end of file diff --git a/contracts/shared/Registerable.sol b/contracts/shared/Registerable.sol index 870c00471..92de20983 100644 --- a/contracts/shared/Registerable.sol +++ b/contracts/shared/Registerable.sol @@ -20,8 +20,8 @@ abstract contract Registerable is NftOwnable, IRegisterable { - // keccak256(abi.encode(uint256(keccak256("gif-next.contracts.shared.Registerable.sol")) - 1)) & ~bytes32(uint256(0xff)); - bytes32 public constant REGISTERABLE_LOCATION_V1 = 0x6548007c3f4340f82f348c576c0ff69f4f529cadd5ad41f96aae61abceeaa300; + // keccak256(abi.encode(uint256(keccak256("etherisc.gif.Registerable@3.0.0")) - 1)) & ~bytes32(uint256(0xff)); + bytes32 public constant REGISTERABLE_STORAGE_LOCATION_V3_0 = 0x66ed5788a8bec6ff1e7b389e8e939876404dd6c85eb5e16ebccc687f21cd6200; struct RegisterableStorage { NftId _parentNftId; @@ -100,7 +100,7 @@ abstract contract Registerable is function _getRegisterableStorage() private pure returns (RegisterableStorage storage $) { assembly { - $.slot := REGISTERABLE_LOCATION_V1 + $.slot := REGISTERABLE_STORAGE_LOCATION_V3_0 } } } \ No newline at end of file diff --git a/contracts/staking/Staking.sol b/contracts/staking/Staking.sol index ea4c91c9f..3a906c115 100644 --- a/contracts/staking/Staking.sol +++ b/contracts/staking/Staking.sol @@ -38,8 +38,8 @@ contract Staking is { string public constant CONTRACT_NAME = "Staking"; - // keccak256(abi.encode(uint256(keccak256("gif-next.contracts.component.Staking.sol")) - 1)) & ~bytes32(uint256(0xff)); - bytes32 public constant STAKING_LOCATION_V1 = 0xafe8d4462b2ed26a47154f4b8f6d1497d2f772496965791d25bd456e342b7f00; + // keccak256(abi.encode(uint256(keccak256("etherisc.gif.Staking@3.0.0")) - 1)) & ~bytes32(uint256(0xff)); + bytes32 public constant STAKING_STORAGE_LOCATION_V3_0 = 0x17325792392c6ffb9e46de8a113aab40b12e7804b80f435c7c40612bd5653400; struct StakingStorage { TokenRegistry _tokenRegistry; @@ -791,7 +791,7 @@ contract Staking is function _getStakingStorage() private pure returns (StakingStorage storage $) { assembly { - $.slot := STAKING_LOCATION_V1 + $.slot := STAKING_STORAGE_LOCATION_V3_0 } } } diff --git a/contracts/staking/StakingService.sol b/contracts/staking/StakingService.sol index e16f4b3b3..68027e4e9 100644 --- a/contracts/staking/StakingService.sol +++ b/contracts/staking/StakingService.sol @@ -22,9 +22,8 @@ contract StakingService is Service, IStakingService { - // TODO decide and implement string spec for location calculation - // keccak256(abi.encode(uint256(keccak256("gif-next.contracts.shared.StakingService.sol")) - 1)) & ~bytes32(uint256(0xff)); - bytes32 public constant STAKING_SERVICE_LOCATION_V1 = 0x6548005c3f4340f82f348c576c0ff69f7f529cadd5ad41f96aae61abceeaa300; + // keccak256(abi.encode(uint256(keccak256("etherisc.gif.StakingService@3.0.0")) - 1)) & ~bytes32(uint256(0xff)); + bytes32 public constant STAKING_SERVICE_STORAGE_LOCATION_V3_0 = 0x5744a630cd832cc833bbcbacd41a5b85ea7e62a359f2b0312fec3efecead1700; struct StakingServiceStorage { RegistryService _registryService; @@ -310,7 +309,7 @@ contract StakingService is function _getStakingServiceStorage() private pure returns (StakingServiceStorage storage $) { assembly { - $.slot := STAKING_SERVICE_LOCATION_V1 + $.slot := STAKING_SERVICE_STORAGE_LOCATION_V3_0 } } diff --git a/test/component/product/ProductPayout.t.sol b/test/component/product/ProductPayout.t.sol index 5d78f27e5..c1ebd8011 100644 --- a/test/component/product/ProductPayout.t.sol +++ b/test/component/product/ProductPayout.t.sol @@ -800,7 +800,7 @@ contract TestProductClaim is GifTest { 20000, // sum insured 365 * 24 * 3600, // lifetime 1000, // claim amount 1 - 100, // payout mount 1 + 100, // payout amount 1 true); // process payout // add 2nd payout to 1st claim (filling up to full claim amount) @@ -1201,7 +1201,7 @@ contract TestProductClaim is GifTest { vm.stopPrank(); vm.startPrank(policyHolder); - address tokenHandlerAddress = address(instanceReader.getComponentInfo(productNftId).tokenHandler); + address tokenHandlerAddress = address(instanceReader.getTokenHandler(productNftId)); token.approve(tokenHandlerAddress, premiumAmountInt); vm.stopPrank(); diff --git a/test/component/product/ProductPolicy.t.sol b/test/component/product/ProductPolicy.t.sol index 248a79063..32a642b1d 100644 --- a/test/component/product/ProductPolicy.t.sol +++ b/test/component/product/ProductPolicy.t.sol @@ -6,7 +6,7 @@ import {console} from "../../../lib/forge-std/src/Test.sol"; import {IBundleService} from "../../../contracts/pool/IBundleService.sol"; import {BundleSet} from "../../../contracts/instance/BundleSet.sol"; import {GifTest} from "../../base/GifTest.sol"; -import {NftId} from "../../../contracts/type/NftId.sol"; +import {NftIdLib, NftId} from "../../../contracts/type/NftId.sol"; import {IComponents} from "../../../contracts/instance/module/IComponents.sol"; import {IDistribution} from "../../../contracts/instance/module/IDistribution.sol"; import {IPolicy} from "../../../contracts/instance/module/IPolicy.sol"; @@ -18,10 +18,12 @@ import {Timestamp, TimestampLib, zeroTimestamp} from "../../../contracts/type/Ti import {RiskId} from "../../../contracts/type/RiskId.sol"; import {ReferralId, ReferralLib} from "../../../contracts/type/Referral.sol"; import {ReferralId, ReferralLib} from "../../../contracts/type/Referral.sol"; -import {APPLIED, COLLATERALIZED, CLOSED, DECLINED, PAID, EXPECTED} from "../../../contracts/type/StateId.sol"; +import {StateIdLib, APPLIED, COLLATERALIZED, CLOSED, DECLINED, REVOKED, PAID, EXPECTED} from "../../../contracts/type/StateId.sol"; import {POLICY} from "../../../contracts/type/ObjectType.sol"; import {DistributorType} from "../../../contracts/type/DistributorType.sol"; import {IPolicyService} from "../../../contracts/product/IPolicyService.sol"; +import {ILifecycle} from "../../../contracts/shared/ILifecycle.sol"; + // solhint-disable func-name-mixedcase contract ProductPolicyTest is GifTest { @@ -375,7 +377,29 @@ contract ProductPolicyTest is GifTest { } - function test_productCreatePolicy_notApplied() public { + function test_productCreatePolicy_whenApplicationNotExist() public { + // GIVEN + + vm.startPrank(productOwner); + + NftId applicationNftId = NftIdLib.toNftId(1234567890); + + assertTrue(instance.getProductStore().getState(applicationNftId.toKey32(POLICY())) == StateIdLib.zero(), "state not zero"); + + Amount maxPremiumAmount = AmountLib.toAmount(1000); + + // THEN + vm.expectRevert(abi.encodeWithSelector( + IPolicyService.ErrorPolicyServicePolicyProductMismatch.selector, + applicationNftId, + productNftId, + NftIdLib.zero())); + + // WHEN - policyNftId not exist + product.createPolicy2(applicationNftId, false, zeroTimestamp(), maxPremiumAmount); + } + + function test_productCreatePolicy_whenApplicationDeclined() public { // GIVEN vm.startPrank(productOwner); @@ -387,7 +411,7 @@ contract ProductPolicyTest is GifTest { RiskId riskId = product.createRisk("42x4711", data); uint sumInsuredAmount = 1000; - NftId policyNftId = product.createApplication( + NftId applicationNftId = product.createApplication( customer, riskId, sumInsuredAmount, @@ -396,21 +420,26 @@ contract ProductPolicyTest is GifTest { bundleNftId, ReferralLib.zero() ); - assertTrue(policyNftId.gtz(), "policyNftId was zero"); - assertEq(chainNft.ownerOf(policyNftId.toInt()), customer, "customer not owner of policyNftId"); + assertTrue(applicationNftId.gtz(), "applicationNftId was zero"); + assertEq(chainNft.ownerOf(applicationNftId.toInt()), customer, "customer not owner of policyNftId"); - assertTrue(instance.getProductStore().getState(policyNftId.toKey32(POLICY())) == APPLIED(), "state not APPLIED"); + assertTrue(instance.getProductStore().getState(applicationNftId.toKey32(POLICY())) == APPLIED(), "state not APPLIED"); Amount maxPremiumAmount = AmountLib.toAmount(1000); - product.decline(policyNftId); + product.decline(applicationNftId); + + assertTrue(instance.getProductStore().getState(applicationNftId.toKey32(POLICY())) == DECLINED(), "state not DECLINED"); // THEN vm.expectRevert(abi.encodeWithSelector( - IPolicyService.ErrorPolicyServicePolicyStateNotApplied.selector, - policyNftId)); - - // WHEN - state not applied - product.createPolicy2(policyNftId, false, zeroTimestamp(), maxPremiumAmount); + ILifecycle.ErrorInvalidStateTransition.selector, + address(product.getInstance().getProductStore()), + POLICY(), + DECLINED(), + COLLATERALIZED())); + + // WHEN - state declined + product.createPolicy2(applicationNftId, false, zeroTimestamp(), maxPremiumAmount); } @@ -1520,7 +1549,29 @@ contract ProductPolicyTest is GifTest { product.expire(policyNftId, expireAtTs); } - function test_productDeclinePolicy_notApplied() public { + function test_productDeclineApplication_whenApplicationNotExist() public { + // GIVEN + // !!! COMPILES WITH DOUBLE START PRANK !!! -> Foundry issue? + vm.startPrank(productOwner); + + NftId applicationNftId = NftIdLib.toNftId(1234567890); + + assertTrue(instance.getProductStore().getState(applicationNftId.toKey32(POLICY())) == StateIdLib.zero(), "state not zero"); + + vm.startPrank(productOwner); + + // THEN + vm.expectRevert(abi.encodeWithSelector( + IPolicyService.ErrorPolicyServicePolicyProductMismatch.selector, + applicationNftId, + productNftId, + NftIdLib.zero())); + + // WHEN - state not applied + product.decline(applicationNftId); + } + + function test_productDeclineApplication_whenApplicationRevoked() public { // GIVEN vm.startPrank(productOwner); @@ -1532,7 +1583,7 @@ contract ProductPolicyTest is GifTest { RiskId riskId = product.createRisk("42x4711", data); uint sumInsuredAmount = 1000; - NftId policyNftId = product.createApplication( + NftId applicationNftId = product.createApplication( customer, riskId, sumInsuredAmount, @@ -1541,23 +1592,27 @@ contract ProductPolicyTest is GifTest { bundleNftId, ReferralLib.zero() ); - assertTrue(policyNftId.gtz(), "policyNftId was zero"); - assertEq(chainNft.ownerOf(policyNftId.toInt()), customer, "customer not owner of policyNftId"); + assertTrue(applicationNftId.gtz(), "policyNftId was zero"); + assertEq(chainNft.ownerOf(applicationNftId.toInt()), customer, "customer not owner of policyNftId"); - assertTrue(instance.getProductStore().getState(policyNftId.toKey32(POLICY())) == APPLIED(), "state not APPLIED"); + assertTrue(instance.getProductStore().getState(applicationNftId.toKey32(POLICY())) == APPLIED(), "state not APPLIED"); - Amount maxPremiumAmount = AmountLib.toAmount(1000); - product.revoke(policyNftId); + product.revoke(applicationNftId); + + assertTrue(instance.getProductStore().getState(applicationNftId.toKey32(POLICY())) == REVOKED(), "state not REVOKED"); vm.startPrank(productOwner); // THEN vm.expectRevert(abi.encodeWithSelector( - IPolicyService.ErrorPolicyServicePolicyStateNotApplied.selector, - policyNftId)); + ILifecycle.ErrorInvalidStateTransition.selector, + address(product.getInstance().getProductStore()), + POLICY(), + REVOKED(), + DECLINED())); // WHEN - state not applied - product.decline(policyNftId); + product.decline(applicationNftId); } function test_productCollectPremium_notCollateralized() public { From 425fea23e11b5082629b6f4df78f6efd8d7a18af Mon Sep 17 00:00:00 2001 From: rapiddenis <41779817+rapidddenis@users.noreply.github.com> Date: Fri, 6 Sep 2024 19:01:53 +0000 Subject: [PATCH 2/3] reduce policy and pool services size --- contracts/pool/IPoolService.sol | 3 --- contracts/pool/PoolService.sol | 6 +++--- contracts/product/PolicyService.sol | 14 ++++++++------ contracts/staking/Staking.sol | 2 +- 4 files changed, 12 insertions(+), 13 deletions(-) diff --git a/contracts/pool/IPoolService.sol b/contracts/pool/IPoolService.sol index 4fcd4fb26..fd0fa372b 100644 --- a/contracts/pool/IPoolService.sol +++ b/contracts/pool/IPoolService.sol @@ -18,7 +18,6 @@ interface IPoolService is IService { event LogPoolServiceWalletFunded(NftId poolNftId, address poolOwner, Amount amount); event LogPoolServiceWalletDefunded(NftId poolNftId, address poolOwner, Amount amount); - event LogPoolServiceBundleCreated(NftId instanceNftId, NftId poolNftId, NftId bundleNftId); event LogPoolServiceBundleClosed(NftId instanceNftId, NftId poolNftId, NftId bundleNftId); event LogPoolServiceBundleStaked(NftId instanceNftId, NftId poolNftId, NftId bundleNftId, Amount amount, Amount netAmount); @@ -36,8 +35,6 @@ interface IPoolService is IService { error ErrorPoolServicePoolNotExternallyManaged(NftId poolNftId); error ErrorPoolServicePolicyPoolMismatch(NftId policyNftId, NftId productNftId, NftId expectedProductNftId); - error ErrorPoolServiceBundleOwnerRoleAlreadySet(NftId poolNftId); - error ErrorPoolServiceInvalidTransferAmount(Amount expectedAmount, Amount actualAmount); error ErrorPoolServiceBundlePoolMismatch(NftId bundleNftId, NftId poolNftId); error ErrorPoolServiceMaxBalanceAmountExceeded(NftId poolNftId, Amount maxBalanceAmount, Amount currentBalanceAmount, Amount transferAmount); error ErrorPoolServiceFeesWithdrawAmountExceedsLimit(Amount amount, Amount limit); diff --git a/contracts/pool/PoolService.sol b/contracts/pool/PoolService.sol index c87f218b4..8cee72312 100644 --- a/contracts/pool/PoolService.sol +++ b/contracts/pool/PoolService.sol @@ -131,13 +131,13 @@ contract PoolService is (NftId poolNftId, IInstance instance) = _getAndVerifyActivePool(); InstanceReader instanceReader = instance.getInstanceReader(); NftId productNftId = getRegistry().getParentNftId(poolNftId); + NftId policyProductNftId = getRegistry().getParentNftId(policyNftId); // check policy matches with calling pool - IPolicy.PolicyInfo memory policyInfo = instanceReader.getPolicyInfo(policyNftId); - if(policyInfo.productNftId != productNftId) { + if(policyProductNftId != productNftId) { revert ErrorPoolServicePolicyPoolMismatch( policyNftId, - policyInfo.productNftId, + policyProductNftId, productNftId); } diff --git a/contracts/product/PolicyService.sol b/contracts/product/PolicyService.sol index 381ec5a23..8b3bfd58d 100644 --- a/contracts/product/PolicyService.sol +++ b/contracts/product/PolicyService.sol @@ -83,9 +83,10 @@ contract PolicyService is (IInstance instance,,) = _getAndVerifyCallerForPolicy(applicationNftId); // check policy is in state applied - if (instance.getInstanceReader().getPolicyState(applicationNftId) != APPLIED()) { - revert ErrorPolicyServicePolicyStateNotApplied(applicationNftId); - } + // commented to reduce contract size, the only "APPLIED -> DECLINED" transition exists + //if (instance.getInstanceReader().getPolicyState(applicationNftId) != APPLIED()) { + // revert ErrorPolicyServicePolicyStateNotApplied(applicationNftId); + //} // effects // store updated policy info @@ -117,10 +118,11 @@ contract PolicyService is ) = _getAndVerifyCallerForPolicy(applicationNftId); // check policy is in state applied + // commented to reduce contract size, the only "APPLIED -> COLLATERALIZED" transition exists InstanceReader instanceReader = instance.getInstanceReader(); - if (instanceReader.getPolicyState(applicationNftId) != APPLIED()) { - revert ErrorPolicyServicePolicyStateNotApplied(applicationNftId); - } + //if (instanceReader.getPolicyState(applicationNftId) != APPLIED()) { + // revert ErrorPolicyServicePolicyStateNotApplied(applicationNftId); + //} PolicyServiceStorage storage $ = _getPolicyServiceStorage(); diff --git a/contracts/staking/Staking.sol b/contracts/staking/Staking.sol index 3a906c115..a2139cd4b 100644 --- a/contracts/staking/Staking.sol +++ b/contracts/staking/Staking.sol @@ -80,7 +80,7 @@ contract Staking is } StakingStorage storage $ = _getStakingStorage(); - address dipToken = _getStakingStorage()._tokenRegistry.getDipTokenAddress(); + address dipToken = $._tokenRegistry.getDipTokenAddress(); $._tokenHandler = TokenHandlerDeployerLib.deployTokenHandler( address(getRegistry()), address(this), From 36d69bae5d16fd05a41451f891480aaed42b35e8 Mon Sep 17 00:00:00 2001 From: rapiddenis <41779817+rapidddenis@users.noreply.github.com> Date: Tue, 24 Sep 2024 12:44:13 -0400 Subject: [PATCH 3/3] make upgradeable proxy stateless move init data to proxy manager --- contracts/shared/RegistryLinked.sol | 4 ++-- contracts/upgradeability/ProxyManager.sol | 18 ++++++++++++++---- .../UpgradableProxyWithAdmin.sol | 14 +++++++++----- scripts/libs/deployment.ts | 2 +- 4 files changed, 26 insertions(+), 12 deletions(-) diff --git a/contracts/shared/RegistryLinked.sol b/contracts/shared/RegistryLinked.sol index c19916461..ec3de09de 100644 --- a/contracts/shared/RegistryLinked.sol +++ b/contracts/shared/RegistryLinked.sol @@ -11,9 +11,9 @@ contract RegistryLinked is Initializable, IRegistryLinked { - // priorize simplicity and size over using standard upgradeability structs - IRegistry private _registry; + // may interfere with proxy storage when used for upgradeable contracts + IRegistry private _registry; /// @dev initialization for upgradable contracts // used in _initializeRegisterable diff --git a/contracts/upgradeability/ProxyManager.sol b/contracts/upgradeability/ProxyManager.sol index 023cd1849..057f01a86 100644 --- a/contracts/upgradeability/ProxyManager.sol +++ b/contracts/upgradeability/ProxyManager.sol @@ -38,6 +38,8 @@ contract ProxyManager is UpgradableProxyWithAdmin internal _proxy; + bytes internal _deployData; + // state to keep version history mapping(Version version => VersionInfo info) internal _versionHistory; Version [] internal _versions; @@ -66,7 +68,7 @@ contract ProxyManager is address initialImplementation, bytes memory initializationData ) - public + internal virtual onlyInitializing() returns (IVersionable versionable) @@ -76,10 +78,12 @@ contract ProxyManager is address initialProxyAdminOwner ) = _preDeployChecksAndSetup(registry); + _deployData = getDeployData(currentProxyOwner, initializationData); + _proxy = new UpgradableProxyWithAdmin( initialImplementation, initialProxyAdminOwner, - getDeployData(currentProxyOwner, initializationData) + _deployData ); versionable = _updateVersionHistory( @@ -95,7 +99,7 @@ contract ProxyManager is bytes memory initializationData, bytes32 salt ) - public + internal virtual onlyInitializing() returns (IVersionable versionable) @@ -105,10 +109,12 @@ contract ProxyManager is address initialProxyAdminOwner ) = _preDeployChecksAndSetup(registry); + _deployData = getDeployData(currentProxyOwner, initializationData); + _proxy = new UpgradableProxyWithAdmin{salt: salt}( initialImplementation, initialProxyAdminOwner, - getDeployData(currentProxyOwner, initializationData) + _deployData ); versionable = _updateVersionHistory( @@ -195,6 +201,10 @@ contract ProxyManager is return _versionHistory[_version]; } + function getDeployData() external view returns (bytes memory) { + return _deployData; + } + function _preDeployChecksAndSetup(address registry) private returns ( diff --git a/contracts/upgradeability/UpgradableProxyWithAdmin.sol b/contracts/upgradeability/UpgradableProxyWithAdmin.sol index 1a356a6cf..19274d7aa 100644 --- a/contracts/upgradeability/UpgradableProxyWithAdmin.sol +++ b/contracts/upgradeability/UpgradableProxyWithAdmin.sol @@ -6,19 +6,23 @@ import {TransparentUpgradeableProxy} from "@openzeppelin/contracts/proxy/transpa contract UpgradableProxyWithAdmin is TransparentUpgradeableProxy { - bytes internal _initializationData; + //bytes private _initializationData; constructor(address implementation, address initialProxyAdminOwner, bytes memory data) TransparentUpgradeableProxy(implementation, initialProxyAdminOwner, data) { - _initializationData = data; + // TODO + // This overwrites implementations storage + // Implementation can not use storage with root at slot 0 -OR- proxy MUST be stateless + // In case of Staking overwrites RegistryLinked._registry + //_initializationData = data; } function getProxyAdmin() external returns (ProxyAdmin) { return ProxyAdmin(_proxyAdmin()); } - function getInitializationData() external view returns (bytes memory) { - return _initializationData; - } + //function getProxyInitializationData() external view returns (bytes memory) { + // return _initializationData; + //} } \ No newline at end of file diff --git a/scripts/libs/deployment.ts b/scripts/libs/deployment.ts index 4994c0ad5..c72d72d92 100644 --- a/scripts/libs/deployment.ts +++ b/scripts/libs/deployment.ts @@ -97,7 +97,7 @@ export async function deployProxyManagerContract( [ serviceImplAddress, deployment.address, - await proxy.getInitializationData(), + await proxyManager.getDeployData(), ], undefined);