Skip to content

Commit

Permalink
Merge pull request #601 from etherisc/feature/refactor-product-info
Browse files Browse the repository at this point in the history
add new ObjectType FeeInfo (#599)
  • Loading branch information
matthiaszimmermann authored Aug 19, 2024
2 parents d43ca2d + 5b8b4a3 commit 6293645
Show file tree
Hide file tree
Showing 28 changed files with 207 additions and 106 deletions.
6 changes: 3 additions & 3 deletions contracts/distribution/DistributionService.sol
Original file line number Diff line number Diff line change
Expand Up @@ -77,10 +77,10 @@ contract DistributionService is

{
NftId productNftId = _getProductNftId(distributionNftId);
IComponents.ProductInfo memory productInfo = instance.getInstanceReader().getProductInfo(productNftId);
IComponents.FeeInfo memory feeInfo = instance.getInstanceReader().getFeeInfo(productNftId);

UFixed variableDistributionFees = productInfo.distributionFee.fractionalFee;
UFixed variableFeesPartsTotal = productInfo.minDistributionOwnerFee.fractionalFee + commissionPercentage;
UFixed variableDistributionFees = feeInfo.distributionFee.fractionalFee;
UFixed variableFeesPartsTotal = feeInfo.minDistributionOwnerFee.fractionalFee + commissionPercentage;

if (variableFeesPartsTotal > variableDistributionFees) {
revert ErrorDistributionServiceVariableFeesTooHight(variableDistributionFees.toInt1000(), variableFeesPartsTotal.toInt1000());
Expand Down
4 changes: 3 additions & 1 deletion contracts/examples/fire/FireProduct.sol
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,9 @@ contract FireProduct is
numberOfOracles: 0,
poolNftId: NftIdLib.zero(),
distributionNftId: NftIdLib.zero(),
oracleNftId: new NftId[](0),
oracleNftId: new NftId[](0)
}),
IComponents.FeeInfo({
productFee: FeeLib.zero(),
processingFee: FeeLib.zero(),
distributionFee: FeeLib.zero(),
Expand Down
4 changes: 4 additions & 0 deletions contracts/examples/unpermissioned/SimpleProduct.sol
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ contract SimpleProduct is
string memory name,
address token,
IComponents.ProductInfo memory productInfo,
IComponents.FeeInfo memory feeInfo,
IAuthorization authorization,
address initialOwner
)
Expand All @@ -49,6 +50,7 @@ contract SimpleProduct is
name,
token,
productInfo,
feeInfo,
authorization,
initialOwner);
}
Expand All @@ -60,6 +62,7 @@ contract SimpleProduct is
string memory name,
address token,
IComponents.ProductInfo memory productInfo,
IComponents.FeeInfo memory feeInfo,
IAuthorization authorization,
address initialOwner
)
Expand All @@ -73,6 +76,7 @@ contract SimpleProduct is
name,
token,
productInfo,
feeInfo,
authorization,
initialOwner);

Expand Down
2 changes: 2 additions & 0 deletions contracts/instance/InstanceAuthorizationV3.sol
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,8 @@ contract InstanceAuthorizationV3
_authorize(functions, InstanceStore.createPool.selector, "createPool");
_authorize(functions, InstanceStore.createProduct.selector, "createProduct");
_authorize(functions, InstanceStore.updateProduct.selector, "updateProduct");
_authorize(functions, InstanceStore.createFee.selector, "createFee");
_authorize(functions, InstanceStore.updateFee.selector, "updateFee");

// authorize distribution service role
functions = _authorizeForTarget(INSTANCE_STORE_TARGET_NAME, getServiceRole(DISTRIBUTION()));
Expand Down
17 changes: 16 additions & 1 deletion contracts/instance/InstanceReader.sol
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import {ClaimId, ClaimIdLib} from "../type/ClaimId.sol";
import {DistributorType} from "../type/DistributorType.sol";
import {Key32} from "../type/Key32.sol";
import {NftId} from "../type/NftId.sol";
import {COMPONENT, DISTRIBUTOR, DISTRIBUTION, PREMIUM, PRODUCT, POLICY, POOL, BUNDLE} from "../type/ObjectType.sol";
import {COMPONENT, DISTRIBUTOR, DISTRIBUTION, FEE, PREMIUM, PRODUCT, POLICY, POOL, BUNDLE} from "../type/ObjectType.sol";
import {PayoutId, PayoutIdLib} from "../type/PayoutId.sol";
import {ReferralId, ReferralStatus, ReferralLib, REFERRAL_OK, REFERRAL_ERROR_UNKNOWN, REFERRAL_ERROR_EXPIRED, REFERRAL_ERROR_EXHAUSTED} from "../type/Referral.sol";
import {RequestId} from "../type/RequestId.sol";
Expand Down Expand Up @@ -408,6 +408,17 @@ contract InstanceReader {
}
}

function getFeeInfo(NftId productNftId)
public
view
returns (IComponents.FeeInfo memory feeInfo)
{
bytes memory data = _store.getData(toFeeKey(productNftId));
if (data.length > 0) {
return abi.decode(data, (IComponents.FeeInfo));
}
}

function getPoolInfo(NftId poolNftId)
public
view
Expand Down Expand Up @@ -554,6 +565,10 @@ contract InstanceReader {
return productNftId.toKey32(PRODUCT());
}

function toFeeKey(NftId productNftId) public pure returns (Key32) {
return productNftId.toKey32(FEE());
}

// low level function
function getInstance() external view returns (IInstance instance) {
return _instance;
Expand Down
15 changes: 13 additions & 2 deletions contracts/instance/InstanceStore.sol
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,10 @@ import {Amount} from "../type/Amount.sol";
import {Key32} from "../type/Key32.sol";
import {NftId} from "../type/NftId.sol";
import {ClaimId} from "../type/ClaimId.sol";
import {ObjectType, BUNDLE, POLICY, POOL, PREMIUM, PRODUCT, COMPONENT, DISTRIBUTOR} from "../type/ObjectType.sol";
import {ObjectType, BUNDLE, POLICY, POOL, PREMIUM, PRODUCT, COMPONENT, DISTRIBUTOR, FEE} from "../type/ObjectType.sol";
import {RequestId} from "../type/RequestId.sol";
import {RiskId} from "../type/RiskId.sol";
import {StateId} from "../type/StateId.sol";
import {StateId, KEEP_STATE} from "../type/StateId.sol";
import {ReferralId} from "../type/Referral.sol";
import {DistributorType} from "../type/DistributorType.sol";
import {PayoutId} from "../type/PayoutId.sol";
Expand Down Expand Up @@ -85,6 +85,17 @@ contract InstanceStore is
_update(_toNftKey32(productNftId, PRODUCT()), abi.encode(info), newState);
}


//--- Fee -----------------------------------------------------------//
function createFee(NftId productNftId, IComponents.FeeInfo memory info) external restricted() {
_create(_toNftKey32(productNftId, FEE()), abi.encode(info));
}

// Fee only has one state, so no change change possible
function updateFee(NftId productNftId, IComponents.FeeInfo memory info) external restricted() {
_update(_toNftKey32(productNftId, FEE()), abi.encode(info), KEEP_STATE());
}

//--- Pool --------------------------------------------------------------//

function createPool(
Expand Down
3 changes: 2 additions & 1 deletion contracts/instance/base/ObjectLifecycle.sol
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ pragma solidity ^0.8.20;

import {Initializable} from "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";

import {COMPONENT, BUNDLE, POLICY, REQUEST, RISK, CLAIM, PAYOUT, POOL, PREMIUM, PRODUCT, DISTRIBUTION, DISTRIBUTOR, DISTRIBUTOR_TYPE, REFERRAL} from "../../type/ObjectType.sol";
import {COMPONENT, BUNDLE, POLICY, REQUEST, RISK, CLAIM, PAYOUT, POOL, PREMIUM, PRODUCT, DISTRIBUTION, DISTRIBUTOR, DISTRIBUTOR_TYPE, REFERRAL, FEE} from "../../type/ObjectType.sol";
import {ACTIVE, PAUSED, ARCHIVED, CLOSED, APPLIED, COLLATERALIZED, REVOKED, SUBMITTED, CONFIRMED, DECLINED, EXPECTED, PAID, FULFILLED, FAILED, CANCELLED} from "../../type/StateId.sol";
import {Lifecycle} from "../../shared/Lifecycle.sol";

Expand Down Expand Up @@ -100,6 +100,7 @@ contract ObjectLifecycle is
// dummy lifecycle only
function _setUpProductLifecycle() private {
setInitialState(PRODUCT(), ACTIVE());
setInitialState(FEE(), ACTIVE());
}

// dummy lifecycles only
Expand Down
5 changes: 3 additions & 2 deletions contracts/instance/module/IComponents.sol
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import {IERC20Metadata} from "@openzeppelin/contracts/token/ERC20/extensions/IER
import {Amount} from "../../type/Amount.sol";
import {Fee} from "../../type/Fee.sol";
import {NftId} from "../../type/NftId.sol";
import {RoleId} from "../../type/RoleId.sol";
import {TokenHandler} from "../../shared/TokenHandler.sol";
import {UFixed} from "../../type/UFixed.sol";

Expand All @@ -28,6 +27,9 @@ interface IComponents {
NftId poolNftId; // mandatory
NftId distributionNftId; // 0..1 (optional)
NftId [] oracleNftId; // 0..n (optional)
}

struct FeeInfo {
Fee productFee; // product fee on net premium
Fee processingFee; // product fee on payout amounts
Fee distributionFee; // distribution fee for sales that do not include commissions
Expand All @@ -37,7 +39,6 @@ interface IComponents {
Fee performanceFee; // pool fee on profits from capital investors
}


struct PoolInfo {
Amount maxBalanceAmount; // max balance amount allowed for pool
bool isInterceptingBundleTransfers; // custom logic for bundle nft transfers
Expand Down
2 changes: 1 addition & 1 deletion contracts/pool/PoolService.sol
Original file line number Diff line number Diff line change
Expand Up @@ -175,7 +175,7 @@ contract PoolService is

{
NftId productNftId = registry.getObjectInfo(poolNftId).parentNftId;
Fee memory stakingFee = instanceReader.getProductInfo(productNftId).stakingFee;
Fee memory stakingFee = instanceReader.getFeeInfo(productNftId).stakingFee;
(
feeAmount,
netAmount
Expand Down
2 changes: 2 additions & 0 deletions contracts/product/BasicProduct.sol
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ abstract contract BasicProduct is
string memory name,
address token,
IComponents.ProductInfo memory productInfo,
IComponents.FeeInfo memory feeInfo,
IAuthorization authorization,
address initialOwner
)
Expand All @@ -42,6 +43,7 @@ abstract contract BasicProduct is
name,
token,
productInfo,
feeInfo,
authorization,
initialOwner,
""); // component data
Expand Down
4 changes: 2 additions & 2 deletions contracts/product/ClaimService.sol
Original file line number Diff line number Diff line change
Expand Up @@ -562,8 +562,8 @@ contract ClaimService is
beneficiary = payoutInfo.beneficiary;
}

IComponents.ProductInfo memory productInfo = instanceReader.getProductInfo(productNftId);
if(FeeLib.gtz(productInfo.processingFee)) {
IComponents.FeeInfo memory feeInfo = instanceReader.getFeeInfo(productNftId);
if(FeeLib.gtz(feeInfo.processingFee)) {
// TODO calculate and set net payout and processing fees
}
}
Expand Down
5 changes: 4 additions & 1 deletion contracts/product/IProductComponent.sol
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,10 @@ interface IProductComponent is
) external view returns (Amount netPremiumAmount);


/// @dev returns initial pool specific infos for this pool
/// @dev returns initial product specific infos
function getInitialProductInfo() external view returns (IComponents.ProductInfo memory info);

/// @dev returns initial fee infos
function getInitialFeeInfo() external view returns (IComponents.FeeInfo memory info);

}
33 changes: 18 additions & 15 deletions contracts/product/PricingService.sol
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,7 @@ contract PricingService is
{
// get configurations for all involed objects
IComponents.ProductInfo memory productInfo = reader.getProductInfo(productNftId);
IComponents.FeeInfo memory feeInfo = reader.getFeeInfo(productNftId);

IBundle.BundleInfo memory bundleInfo = reader.getBundleInfo(bundleNftId);
if(bundleInfo.poolNftId != productInfo.poolNftId) {
Expand All @@ -108,21 +109,22 @@ contract PricingService is
// calculate fixed fees for product, pool, bundle
premium = _getFixedFeeAmounts(
netPremiumAmount,
productInfo,
feeInfo,
bundleInfo
);

// calculate variable fees for product, pool, bundle
premium = _calculateVariableFeeAmounts(
premium,
productInfo,
feeInfo,
bundleInfo
);

// calculate distribution fee and (if applicable) commission
premium = _calculateDistributionOwnerFeeAmount(
premium,
productInfo,
feeInfo,
productInfo.distributionNftId,
referralId,
reader
);
Expand All @@ -138,7 +140,7 @@ contract PricingService is
revert ErrorPricingServiceTargetWalletAmountsMismatch();
}

if (premium.distributionOwnerFeeFixAmount.toInt() < productInfo.minDistributionOwnerFee.fixedFee) {
if (premium.distributionOwnerFeeFixAmount.toInt() < feeInfo.minDistributionOwnerFee.fixedFee) {
revert ErrorPricingServiceFeeCalculationMismatch(
premium.distributionFeeFixAmount,
premium.distributionFeeVarAmount,
Expand All @@ -165,7 +167,7 @@ contract PricingService is
// internal functions
function _getFixedFeeAmounts(
Amount netPremiumAmount,
IComponents.ProductInfo memory productInfo,
IComponents.FeeInfo memory feeInfo,
IBundle.BundleInfo memory bundleInfo
)
internal
Expand All @@ -178,26 +180,26 @@ contract PricingService is
premium.netPremiumAmount = netPremiumAmount;
premium.fullPremiumAmount = netPremiumAmount;

Amount t = AmountLib.toAmount(productInfo.productFee.fixedFee);
Amount t = AmountLib.toAmount(feeInfo.productFee.fixedFee);
premium.productFeeFixAmount = t;
premium.fullPremiumAmount = premium.fullPremiumAmount + t;

t = AmountLib.toAmount(productInfo.poolFee.fixedFee);
t = AmountLib.toAmount(feeInfo.poolFee.fixedFee);
premium.poolFeeFixAmount = t;
premium.fullPremiumAmount = premium.fullPremiumAmount + t;

t = AmountLib.toAmount(bundleInfo.fee.fixedFee);
premium.bundleFeeFixAmount = t;
premium.fullPremiumAmount = premium.fullPremiumAmount + t;

t = AmountLib.toAmount(productInfo.distributionFee.fixedFee);
t = AmountLib.toAmount(feeInfo.distributionFee.fixedFee);
premium.distributionFeeFixAmount = t;
premium.fullPremiumAmount = premium.fullPremiumAmount + t;
}

function _calculateVariableFeeAmounts(
IPolicy.PremiumInfo memory premium,
IComponents.ProductInfo memory productInfo,
IComponents.FeeInfo memory feeInfo,
IBundle.BundleInfo memory bundleInfo
)
internal
Expand All @@ -208,19 +210,19 @@ contract PricingService is
{
Amount netPremiumAmount = premium.netPremiumAmount;

Amount t = netPremiumAmount.multiplyWith(productInfo.productFee.fractionalFee);
Amount t = netPremiumAmount.multiplyWith(feeInfo.productFee.fractionalFee);
premium.productFeeVarAmount = t;
premium.fullPremiumAmount = premium.fullPremiumAmount + t;

t = netPremiumAmount.multiplyWith(productInfo.poolFee.fractionalFee);
t = netPremiumAmount.multiplyWith(feeInfo.poolFee.fractionalFee);
premium.poolFeeVarAmount = t;
premium.fullPremiumAmount = premium.fullPremiumAmount + t;

t = netPremiumAmount.multiplyWith(bundleInfo.fee.fractionalFee);
premium.bundleFeeVarAmount = t;
premium.fullPremiumAmount = premium.fullPremiumAmount + t;

t = netPremiumAmount.multiplyWith(productInfo.distributionFee.fractionalFee);
t = netPremiumAmount.multiplyWith(feeInfo.distributionFee.fractionalFee);
premium.distributionFeeVarAmount = t;
premium.fullPremiumAmount = premium.fullPremiumAmount + t;

Expand All @@ -229,7 +231,8 @@ contract PricingService is

function _calculateDistributionOwnerFeeAmount(
IPolicy.PremiumInfo memory premium,
IComponents.ProductInfo memory productInfo,
IComponents.FeeInfo memory feeInfo,
NftId distributionNftId,
// ISetup.DistributionSetupInfo memory distInfo,
ReferralId referralId,
InstanceReader reader
Expand All @@ -240,14 +243,14 @@ contract PricingService is
{

// if the referral is not valid, then the distribution owner gets everything
if (productInfo.distributionNftId.eqz() || ! _distributionService.referralIsValid(productInfo.distributionNftId, referralId)) {
if (distributionNftId.eqz() || ! _distributionService.referralIsValid(distributionNftId, referralId)) {
premium.distributionOwnerFeeFixAmount = premium.distributionFeeFixAmount;
premium.distributionOwnerFeeVarAmount = premium.distributionFeeVarAmount;
premium.premiumAmount = premium.fullPremiumAmount;
return premium;
}

Fee memory minDistributionOwnerFee = productInfo.minDistributionOwnerFee;
Fee memory minDistributionOwnerFee = feeInfo.minDistributionOwnerFee;

// if the referral is valid, the the commission and discount are calculated based in the full premium
// the remaing amount goes to the distribution owner
Expand Down
12 changes: 12 additions & 0 deletions contracts/product/Product.sol
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ abstract contract Product is

struct ProductStorage {
IComponents.ProductInfo _productInfo;
IComponents.FeeInfo _feeInfo;
IComponentService _componentService;
IRiskService _riskService;
IApplicationService _applicationService;
Expand Down Expand Up @@ -126,13 +127,23 @@ abstract contract Product is
return _getProductStorage()._productInfo;
}

function getInitialFeeInfo()
public
virtual
view
returns (IComponents.FeeInfo memory feeInfo)
{
return _getProductStorage()._feeInfo;
}


function _initializeProduct(
address registry,
NftId instanceNftId,
string memory name,
address token,
IComponents.ProductInfo memory productInfo,
IComponents.FeeInfo memory feeInfo,
IAuthorization authorization,
address initialOwner,
bytes memory componentData // writeonly data that will saved in the object info record of the registry
Expand All @@ -154,6 +165,7 @@ abstract contract Product is

ProductStorage storage $ = _getProductStorage();
$._productInfo = productInfo;
$._feeInfo = feeInfo;
$._riskService = IRiskService(_getServiceAddress(RISK()));
$._applicationService = IApplicationService(_getServiceAddress(APPLICATION()));
$._policyService = IPolicyService(_getServiceAddress(POLICY()));
Expand Down
Loading

0 comments on commit 6293645

Please sign in to comment.