From 0ee85c97a43eb2eb5551f465aabc45b5bc75c48e Mon Sep 17 00:00:00 2001 From: jo-es Date: Fri, 4 Sep 2020 18:24:46 +0200 Subject: [PATCH] Push progress --- .../contracts/Core/ANN/ANNActor.sol | 121 ++++++++++--- .../contracts/Core/ANN/ANNEncoder.sol | 138 ++++++++++++++- .../contracts/Core/ANN/ANNRegistry.sol | 109 ++++++++++-- .../contracts/Core/ANN/IANNRegistry.sol | 18 +- .../Core/Base/AssetActor/BaseActor.sol | 53 ++---- .../Core/Base/AssetRegistry/BaseRegistry.sol | 7 +- .../AssetRegistry/BaseRegistryStorage.sol | 2 - .../AssetRegistry/State/IStateRegistry.sol | 16 -- .../Base/AssetRegistry/State/StateEncoder.sol | 162 ------------------ .../AssetRegistry/State/StateRegistry.sol | 85 +-------- .../Core/Base/Custodian/Custodian.sol | 2 +- .../contracts/Core/CEC/CECActor.sol | 60 ++++++- .../contracts/Core/CEC/CECEncoder.sol | 122 ++++++++++++- .../contracts/Core/CEC/CECRegistry.sol | 107 ++++++++++-- .../contracts/Core/CEC/ICECRegistry.sol | 18 +- .../contracts/Core/CEG/CEGActor.sol | 60 ++++++- .../contracts/Core/CEG/CEGEncoder.sol | 126 +++++++++++++- .../contracts/Core/CEG/CEGRegistry.sol | 107 ++++++++++-- .../contracts/Core/CEG/ICEGRegistry.sol | 18 +- .../contracts/Core/CERTF/CERTFActor.sol | 60 ++++++- .../contracts/Core/CERTF/CERTFEncoder.sol | 140 ++++++++++++++- .../contracts/Core/CERTF/CERTFRegistry.sol | 107 ++++++++++-- .../contracts/Core/CERTF/ICERTFRegistry.sol | 18 +- .../contracts/Core/PAM/IPAMRegistry.sol | 18 +- .../contracts/Core/PAM/PAMActor.sol | 60 ++++++- .../contracts/Core/PAM/PAMEncoder.sol | 134 ++++++++++++++- .../contracts/Core/PAM/PAMRegistry.sol | 107 ++++++++++-- 27 files changed, 1527 insertions(+), 448 deletions(-) delete mode 100644 packages/ap-contracts/contracts/Core/Base/AssetRegistry/State/StateEncoder.sol diff --git a/packages/ap-contracts/contracts/Core/ANN/ANNActor.sol b/packages/ap-contracts/contracts/Core/ANN/ANNActor.sol index 8b039f96..75338e0a 100644 --- a/packages/ap-contracts/contracts/Core/ANN/ANNActor.sol +++ b/packages/ap-contracts/contracts/Core/ANN/ANNActor.sol @@ -46,7 +46,7 @@ contract ANNActor is BaseActor { bytes32 assetId = keccak256(abi.encode(terms, block.timestamp)); // compute the initial state of the asset - State memory initialState = IANNEngine(engine).computeInitialState(terms); + ANNState memory initialState = IANNEngine(engine).computeInitialState(terms); // register the asset in the AssetRegistry IANNRegistry(address(assetRegistry)).registerAsset( @@ -63,30 +63,30 @@ contract ANNActor is BaseActor { emit InitializedAsset(assetId, ContractType.ANN, ownership.creatorObligor, ownership.counterpartyObligor); } - function computeStateAndPayoffForEvent(bytes32 assetId, State memory state, bytes32 _event) - internal - view - override - returns (State memory, int256) - { - // ContractType contractType = ContractType(assetRegistry.getEnumValueForTermsAttribute(assetId, "contractType")); - // revert("ANNActor.computePayoffAndStateForEvent: UNSUPPORTED_CONTRACT_TYPE"); - - address engine = assetRegistry.getEngine(assetId); - ANNTerms memory terms = IANNRegistry(address(assetRegistry)).getTerms(assetId); + function computePayoffForEvent(bytes32 assetId, address engine, ANNTerms memory terms, ANNState memory state, bytes32 _event) internal view returns (int256) { (EventType eventType, uint256 scheduleTime) = decodeEvent(_event); - int256 payoff = IANNEngine(engine).computePayoffForEvent( - terms, - state, - _event, - getExternalDataForPOF( + bytes32 externalData; + { + externalData = getExternalDataForPOF( assetId, eventType, shiftCalcTime(scheduleTime, terms.businessDayConvention, terms.calendar, terms.maturityDate) - ) + ); + } + + return IANNEngine(engine).computePayoffForEvent( + terms, + state, + _event, + externalData ); - state = IANNEngine(engine).computeStateForEvent( + } + + function computeStateForEvent(bytes32 assetId, address engine, ANNTerms memory terms, ANNState memory state, bytes32 _event) internal view returns (ANNState memory) { + (EventType eventType, uint256 scheduleTime) = decodeEvent(_event); + + return IANNEngine(engine).computeStateForEvent( terms, state, _event, @@ -96,7 +96,88 @@ contract ANNActor is BaseActor { shiftCalcTime(scheduleTime, terms.businessDayConvention, terms.calendar, terms.maturityDate) ) ); + } + + /** + * @notice Contract-type specific logic for processing an event required by the use of + * contract-type specific Terms and State. + */ + function settleEventAndUpdateState(bytes32 assetId, bytes32 _event) + internal + override + returns (bool, int256) + { + ANNTerms memory terms = IANNRegistry(address(assetRegistry)).getTerms(assetId); + ANNState memory state = IANNRegistry(address(assetRegistry)).getState(assetId); + address engine = assetRegistry.getEngine(assetId); + + // get finalized state if asset is not performant + if (state.contractPerformance != ContractPerformance.PF) { + state = IANNRegistry(address(assetRegistry)).getFinalizedState(assetId); + } + + (EventType eventType, uint256 scheduleTime) = decodeEvent(_event); + + // get external data for the next event + // compute payoff and the next state by applying the event to the current state + int256 payoff = computePayoffForEvent(assetId, engine, terms, state, _event); + // IANNEngine(engine).computePayoffForEvent( + // terms, + // state, + // _event, + // getExternalDataForPOF( + // assetId, + // eventType, + // shiftCalcTime(scheduleTime, terms.businessDayConvention, terms.calendar, terms.maturityDate) + // ) + // ); + ANNState memory nextState = computeStateForEvent(assetId, engine, terms, state, _event); + // IANNEngine(engine).computeStateForEvent( + // terms, + // state, + // _event, + // getExternalDataForSTF( + // assetId, + // eventType, + // shiftCalcTime(scheduleTime, terms.businessDayConvention, terms.calendar, terms.maturityDate) + // ) + // ); + + // try to settle payoff of event + bool settledPayoff = settlePayoffForEvent(assetId, _event, payoff); + + if (settledPayoff == false) { + // if the obligation can't be fulfilled and the performance changed from performant to DL, DQ or DF, + // store the last performant state of the asset + // (if the obligation is later fulfilled before the asset reaches default, + // the last performant state is used to derive subsequent states of the asset) + if (state.contractPerformance == ContractPerformance.PF) { + IANNRegistry(address(assetRegistry)).setFinalizedState(assetId, state); + } + + // store event as pending event for future settlement + assetRegistry.pushPendingEvent(assetId, _event); + + // create CreditEvent + bytes32 ceEvent = encodeEvent(EventType.CE, scheduleTime); + + // derive the actual state of the asset by applying the CreditEvent (updates performance of asset) + nextState = computeStateForEvent(assetId, engine, terms, state, ceEvent); + // IANNEngine(engine).computeStateForEvent( + // terms, + // state, + // ceEvent, + // getExternalDataForSTF( + // assetId, + // EventType.CE, + // shiftCalcTime(scheduleTime, terms.businessDayConvention, terms.calendar, terms.maturityDate) + // ) + // ); + } + + // store the resulting state + IANNRegistry(address(assetRegistry)).setState(assetId, nextState); - return (state, payoff); + return (settledPayoff, payoff); } } \ No newline at end of file diff --git a/packages/ap-contracts/contracts/Core/ANN/ANNEncoder.sol b/packages/ap-contracts/contracts/Core/ANN/ANNEncoder.sol index 34df4087..df41b5eb 100644 --- a/packages/ap-contracts/contracts/Core/ANN/ANNEncoder.sol +++ b/packages/ap-contracts/contracts/Core/ANN/ANNEncoder.sol @@ -13,6 +13,12 @@ library ANNEncoder { if (asset.packedTerms[attributeKey] == value) return; asset.packedTerms[attributeKey] = value; } + + function storeInPackedState(Asset storage asset, bytes32 attributeKey, bytes32 value) private { + // skip if value did not change + if (asset.packedState[attributeKey] == value) return; + asset.packedState[attributeKey] = value; + } /** * @dev Tightly pack and store only non-zero overwritten terms (LifecycleTerms) @@ -220,7 +226,7 @@ library ANNEncoder { ); } - function decodeAndGetEnumValueForANNAttribute(Asset storage asset, bytes32 attributeKey) + function decodeAndGetEnumValueForANNTermsAttribute(Asset storage asset, bytes32 attributeKey) external view returns (uint8) @@ -248,7 +254,7 @@ library ANNEncoder { } } - function decodeAndGetAddressValueForForANNAttribute(Asset storage asset, bytes32 attributeKey) + function decodeAndGetAddressValueForANNTermsAttribute(Asset storage asset, bytes32 attributeKey) external view returns (address) @@ -262,7 +268,7 @@ library ANNEncoder { } } - function decodeAndGetBytes32ValueForForANNAttribute(Asset storage asset, bytes32 attributeKey) + function decodeAndGetBytes32ValueForANNTermsAttribute(Asset storage asset, bytes32 attributeKey) external view returns (bytes32) @@ -270,7 +276,7 @@ library ANNEncoder { return asset.packedTerms[attributeKey]; } - function decodeAndGetUIntValueForForANNAttribute(Asset storage asset, bytes32 attributeKey) + function decodeAndGetUIntValueForANNTermsAttribute(Asset storage asset, bytes32 attributeKey) external view returns (uint256) @@ -278,7 +284,7 @@ library ANNEncoder { return uint256(asset.packedTerms[attributeKey]); } - function decodeAndGetIntValueForForANNAttribute(Asset storage asset, bytes32 attributeKey) + function decodeAndGetIntValueForANNTermsAttribute(Asset storage asset, bytes32 attributeKey) external view returns (int256) @@ -286,7 +292,7 @@ library ANNEncoder { return int256(asset.packedTerms[attributeKey]); } - function decodeAndGetPeriodValueForForANNAttribute(Asset storage asset, bytes32 attributeKey) + function decodeAndGetPeriodValueForANNTermsAttribute(Asset storage asset, bytes32 attributeKey) external view returns (IP memory) @@ -305,7 +311,7 @@ library ANNEncoder { } } - function decodeAndGetCycleValueForForANNAttribute(Asset storage asset, bytes32 attributeKey) + function decodeAndGetCycleValueForANNTermsAttribute(Asset storage asset, bytes32 attributeKey) external view returns (IPS memory) @@ -328,7 +334,7 @@ library ANNEncoder { } } - function decodeAndGetContractReferenceValueForANNAttribute(Asset storage /* asset */, bytes32 /* attributeKey */) + function decodeAndGetContractReferenceValueForANNTermsAttribute(Asset storage /* asset */, bytes32 /* attributeKey */) external pure returns (ContractReference memory) @@ -340,4 +346,120 @@ library ANNEncoder { ContractReferenceRole(0) ); } + + /** + * @dev Tightly pack and store ANNState + */ + function encodeAndSetANNState(Asset storage asset, ANNState memory state) external { + storeInPackedState(asset, "contractPerformance", bytes32(uint256(uint8(state.contractPerformance))) << 248); + storeInPackedState(asset, "statusDate", bytes32(state.statusDate)); + storeInPackedState(asset, "nonPerformingDate", bytes32(state.nonPerformingDate)); + storeInPackedState(asset, "maturityDate", bytes32(state.maturityDate)); + storeInPackedState(asset, "terminationDate", bytes32(state.terminationDate)); + storeInPackedState(asset, "notionalPrincipal", bytes32(state.notionalPrincipal)); + storeInPackedState(asset, "accruedInterest", bytes32(state.accruedInterest)); + storeInPackedState(asset, "feeAccrued", bytes32(state.feeAccrued)); + storeInPackedState(asset, "nominalInterestRate", bytes32(state.nominalInterestRate)); + storeInPackedState(asset, "interestScalingMultiplier", bytes32(state.interestScalingMultiplier)); + storeInPackedState(asset, "notionalScalingMultiplier", bytes32(state.notionalScalingMultiplier)); + storeInPackedState(asset, "nextPrincipalRedemptionPayment", bytes32(state.nextPrincipalRedemptionPayment)); + } + + /** + * @dev Tightly pack and store finalized ANNState + */ + function encodeAndSetFinalizedANNState(Asset storage asset, ANNState memory state) external { + storeInPackedState(asset, "F_contractPerformance", bytes32(uint256(uint8(state.contractPerformance))) << 248); + storeInPackedState(asset, "F_statusDate", bytes32(state.statusDate)); + storeInPackedState(asset, "F_nonPerformingDate", bytes32(state.nonPerformingDate)); + storeInPackedState(asset, "F_maturityDate", bytes32(state.maturityDate)); + storeInPackedState(asset, "F_terminationDate", bytes32(state.terminationDate)); + storeInPackedState(asset, "F_notionalPrincipal", bytes32(state.notionalPrincipal)); + storeInPackedState(asset, "F_accruedInterest", bytes32(state.accruedInterest)); + storeInPackedState(asset, "F_feeAccrued", bytes32(state.feeAccrued)); + storeInPackedState(asset, "F_nominalInterestRate", bytes32(state.nominalInterestRate)); + storeInPackedState(asset, "F_interestScalingMultiplier", bytes32(state.interestScalingMultiplier)); + storeInPackedState(asset, "F_notionalScalingMultiplier", bytes32(state.notionalScalingMultiplier)); + storeInPackedState(asset, "F_nextPrincipalRedemptionPayment", bytes32(state.nextPrincipalRedemptionPayment)); + } + + /** + * @dev Decode and load the ANNState of the asset + */ + function decodeAndGetANNState(Asset storage asset) + external + view + returns (ANNState memory) + { + return ANNState( + ContractPerformance(uint8(uint256(asset.packedState["contractPerformance"] >> 248))), + uint256(asset.packedState["statusDate"]), + uint256(asset.packedState["nonPerformingDate"]), + uint256(asset.packedState["maturityDate"]), + uint256(asset.packedState["terminationDate"]), + + int256(asset.packedState["notionalPrincipal"]), + int256(asset.packedState["accruedInterest"]), + int256(asset.packedState["feeAccrued"]), + int256(asset.packedState["nominalInterestRate"]), + int256(asset.packedState["interestScalingMultiplier"]), + int256(asset.packedState["notionalScalingMultiplier"]), + int256(asset.packedState["nextPrincipalRedemptionPayment"]) + ); + } + + /** + * @dev Decode and load the finalized ANNState of the asset + */ + function decodeAndGetFinalizedANNState(Asset storage asset) + external + view + returns (ANNState memory) + { + return ANNState( + ContractPerformance(uint8(uint256(asset.packedState["F_contractPerformance"] >> 248))), + uint256(asset.packedState["F_statusDate"]), + uint256(asset.packedState["F_nonPerformingDate"]), + uint256(asset.packedState["F_maturityDate"]), + uint256(asset.packedState["F_terminationDate"]), + + int256(asset.packedState["F_notionalPrincipal"]), + int256(asset.packedState["F_accruedInterest"]), + int256(asset.packedState["F_feeAccrued"]), + int256(asset.packedState["F_nominalInterestRate"]), + int256(asset.packedState["F_interestScalingMultiplier"]), + int256(asset.packedState["F_notionalScalingMultiplier"]), + int256(asset.packedState["F_nextPrincipalRedemptionPayment"]) + ); + } + + function decodeAndGetEnumValueForANNStateAttribute(Asset storage asset, bytes32 attributeKey) + external + view + returns (uint8) + { + if (attributeKey == bytes32("contractPerformance")) { + return uint8(uint256(asset.packedState["contractPerformance"] >> 248)); + } else if (attributeKey == bytes32("F_contractPerformance")) { + return uint8(uint256(asset.packedState["F_contractPerformance"] >> 248)); + } else { + return uint8(0); + } + } + + function decodeAndGetUIntValueForANNStateAttribute(Asset storage asset, bytes32 attributeKey) + external + view + returns (uint256) + { + return uint256(asset.packedState[attributeKey]); + } + + function decodeAndGetIntValueForANNStateAttribute(Asset storage asset, bytes32 attributeKey) + external + view + returns (int256) + { + return int256(asset.packedState[attributeKey]); + } } \ No newline at end of file diff --git a/packages/ap-contracts/contracts/Core/ANN/ANNRegistry.sol b/packages/ap-contracts/contracts/Core/ANN/ANNRegistry.sol index 453ca4ea..e2dcf6f5 100644 --- a/packages/ap-contracts/contracts/Core/ANN/ANNRegistry.sol +++ b/packages/ap-contracts/contracts/Core/ANN/ANNRegistry.sol @@ -38,7 +38,7 @@ contract ANNRegistry is BaseRegistry, IANNRegistry { function registerAsset( bytes32 assetId, ANNTerms calldata terms, - State calldata state, + ANNState calldata state, bytes32[] calldata schedule, AssetOwnership calldata ownership, address engine, @@ -49,8 +49,10 @@ contract ANNRegistry is BaseRegistry, IANNRegistry { override onlyApprovedActors { - setAsset(assetId, state, schedule, ownership, engine, actor, admin); + setAsset(assetId, schedule, ownership, engine, actor, admin); assets[assetId].encodeAndSetANNTerms(terms); + assets[assetId].encodeAndSetANNState(state); + assets[assetId].encodeAndSetFinalizedANNState(state); } /** @@ -88,7 +90,7 @@ contract ANNRegistry is BaseRegistry, IANNRegistry { override(ITermsRegistry, TermsRegistry) returns (uint8) { - return assets[assetId].decodeAndGetEnumValueForANNAttribute(attribute); + return assets[assetId].decodeAndGetEnumValueForANNTermsAttribute(attribute); } function getAddressValueForTermsAttribute(bytes32 assetId, bytes32 attribute) @@ -97,7 +99,7 @@ contract ANNRegistry is BaseRegistry, IANNRegistry { override(ITermsRegistry, TermsRegistry) returns (address) { - return assets[assetId].decodeAndGetAddressValueForForANNAttribute(attribute); + return assets[assetId].decodeAndGetAddressValueForANNTermsAttribute(attribute); } function getBytes32ValueForTermsAttribute(bytes32 assetId, bytes32 attribute) @@ -106,7 +108,7 @@ contract ANNRegistry is BaseRegistry, IANNRegistry { override(ITermsRegistry, TermsRegistry) returns (bytes32) { - return assets[assetId].decodeAndGetBytes32ValueForForANNAttribute(attribute); + return assets[assetId].decodeAndGetBytes32ValueForANNTermsAttribute(attribute); } function getUIntValueForTermsAttribute(bytes32 assetId, bytes32 attribute) @@ -115,7 +117,7 @@ contract ANNRegistry is BaseRegistry, IANNRegistry { override(ITermsRegistry, TermsRegistry) returns (uint256) { - return assets[assetId].decodeAndGetUIntValueForForANNAttribute(attribute); + return assets[assetId].decodeAndGetUIntValueForANNTermsAttribute(attribute); } function getIntValueForTermsAttribute(bytes32 assetId, bytes32 attribute) @@ -124,7 +126,7 @@ contract ANNRegistry is BaseRegistry, IANNRegistry { override(ITermsRegistry, TermsRegistry) returns (int256) { - return assets[assetId].decodeAndGetIntValueForForANNAttribute(attribute); + return assets[assetId].decodeAndGetIntValueForANNTermsAttribute(attribute); } function getPeriodValueForTermsAttribute(bytes32 assetId, bytes32 attribute) @@ -133,7 +135,7 @@ contract ANNRegistry is BaseRegistry, IANNRegistry { override(ITermsRegistry, TermsRegistry) returns (IP memory) { - return assets[assetId].decodeAndGetPeriodValueForForANNAttribute(attribute); + return assets[assetId].decodeAndGetPeriodValueForANNTermsAttribute(attribute); } function getCycleValueForTermsAttribute(bytes32 assetId, bytes32 attribute) @@ -142,7 +144,7 @@ contract ANNRegistry is BaseRegistry, IANNRegistry { override(ITermsRegistry, TermsRegistry) returns (IPS memory) { - return assets[assetId].decodeAndGetCycleValueForForANNAttribute(attribute); + return assets[assetId].decodeAndGetCycleValueForANNTermsAttribute(attribute); } function getContractReferenceValueForTermsAttribute(bytes32 assetId, bytes32 attribute) @@ -151,8 +153,93 @@ contract ANNRegistry is BaseRegistry, IANNRegistry { override(ITermsRegistry, TermsRegistry) returns (ContractReference memory) { - return assets[assetId].decodeAndGetContractReferenceValueForANNAttribute(attribute); - } + return assets[assetId].decodeAndGetContractReferenceValueForANNTermsAttribute(attribute); + } + + /** + * @notice Returns the state of an asset. + * @param assetId id of the asset + * @return state of the asset + */ + function getState(bytes32 assetId) + external + view + override + returns (ANNState memory) + { + return assets[assetId].decodeAndGetANNState(); + } + + /** + * @notice Returns the state of an asset. + * @param assetId id of the asset + * @return state of the asset + */ + function getFinalizedState(bytes32 assetId) + external + view + override + returns (ANNState memory) + { + return assets[assetId].decodeAndGetFinalizedANNState(); + } + + /** + * @notice Sets next state of an asset. + * @dev Can only be updated by the assets actor or by an authorized account. + * @param assetId id of the asset + * @param state next state of the asset + */ + function setState(bytes32 assetId, ANNState calldata state) + external + override + isAuthorized (assetId) + { + assets[assetId].encodeAndSetANNState(state); + emit UpdatedState(assetId, state.statusDate); + } + + /** + * @notice Sets next finalized state of an asset. + * @dev Can only be updated by the assets actor or by an authorized account. + * @param assetId id of the asset + * @param state next state of the asset + */ + function setFinalizedState(bytes32 assetId, ANNState calldata state) + external + override + isAuthorized (assetId) + { + assets[assetId].encodeAndSetFinalizedANNState(state); + emit UpdatedFinalizedState(assetId, state.statusDate); + } + + function getEnumValueForStateAttribute(bytes32 assetId, bytes32 attribute) + public + view + override(IStateRegistry, StateRegistry) + returns (uint8) + { + return assets[assetId].decodeAndGetEnumValueForANNStateAttribute(attribute); + } + + function getIntValueForStateAttribute(bytes32 assetId, bytes32 attribute) + public + view + override(IStateRegistry, StateRegistry) + returns (int256) + { + return assets[assetId].decodeAndGetIntValueForANNStateAttribute(attribute); + } + + function getUintValueForStateAttribute(bytes32 assetId, bytes32 attribute) + public + view + override(IStateRegistry, StateRegistry) + returns (uint256) + { + return assets[assetId].decodeAndGetUIntValueForANNStateAttribute(attribute); + } function getNextCyclicEvent(bytes32 assetId) internal diff --git a/packages/ap-contracts/contracts/Core/ANN/IANNRegistry.sol b/packages/ap-contracts/contracts/Core/ANN/IANNRegistry.sol index e103d121..bbc88092 100644 --- a/packages/ap-contracts/contracts/Core/ANN/IANNRegistry.sol +++ b/packages/ap-contracts/contracts/Core/ANN/IANNRegistry.sol @@ -11,7 +11,7 @@ interface IANNRegistry is IAssetRegistry { function registerAsset( bytes32 assetId, ANNTerms calldata terms, - State calldata state, + ANNState calldata state, bytes32[] calldata schedule, AssetOwnership calldata ownership, address engine, @@ -27,4 +27,20 @@ interface IANNRegistry is IAssetRegistry { function setTerms(bytes32 assetId, ANNTerms calldata terms) external; + + function getState(bytes32 assetId) + external + view + returns (ANNState memory); + + function setState(bytes32 assetId, ANNState calldata terms) + external; + + function getFinalizedState(bytes32 assetId) + external + view + returns (ANNState memory); + + function setFinalizedState(bytes32 assetId, ANNState calldata terms) + external; } diff --git a/packages/ap-contracts/contracts/Core/Base/AssetActor/BaseActor.sol b/packages/ap-contracts/contracts/Core/Base/AssetActor/BaseActor.sol index 2b3c99d1..8169350c 100644 --- a/packages/ap-contracts/contracts/Core/Base/AssetActor/BaseActor.sol +++ b/packages/ap-contracts/contracts/Core/Base/AssetActor/BaseActor.sol @@ -121,21 +121,18 @@ abstract contract BaseActor is Conversions, EventUtils, BusinessDayConventions, * @notice Return true if event was settled */ function processEvent(bytes32 assetId, bytes32 _event) internal { - State memory state = assetRegistry.getState(assetId); + ContractPerformance contractPerformance = ContractPerformance( + assetRegistry.getEnumValueForStateAttribute(assetId, "contractPerformance") + ); // block progression if asset is has defaulted, terminated or reached maturity require( - state.contractPerformance == ContractPerformance.PF - || state.contractPerformance == ContractPerformance.DL - || state.contractPerformance == ContractPerformance.DQ, + contractPerformance == ContractPerformance.PF + || contractPerformance == ContractPerformance.DL + || contractPerformance == ContractPerformance.DQ, "BaseActor.processEvent: ASSET_REACHED_FINAL_STATE" ); - // get finalized state if asset is not performant - if (state.contractPerformance != ContractPerformance.PF) { - state = assetRegistry.getFinalizedState(assetId); - } - (EventType eventType, uint256 scheduleTime) = decodeEvent(_event); // revert if the event time of the next event is in the future @@ -151,34 +148,7 @@ abstract contract BaseActor is Conversions, EventUtils, BusinessDayConventions, "ANNActor.processEvent: NEXT_EVENT_NOT_YET_SCHEDULED" ); - // get external data for the next event - // compute payoff and the next state by applying the event to the current state - (State memory nextState, int256 payoff) = computeStateAndPayoffForEvent(assetId, state, _event); - - // try to settle payoff of event - bool settledPayoff = settlePayoffForEvent(assetId, _event, payoff); - - if (settledPayoff == false) { - // if the obligation can't be fulfilled and the performance changed from performant to DL, DQ or DF, - // store the last performant state of the asset - // (if the obligation is later fulfilled before the asset reaches default, - // the last performant state is used to derive subsequent states of the asset) - if (state.contractPerformance == ContractPerformance.PF) { - assetRegistry.setFinalizedState(assetId, state); - } - - // store event as pending event for future settlement - assetRegistry.pushPendingEvent(assetId, _event); - - // create CreditEvent - bytes32 ceEvent = encodeEvent(EventType.CE, scheduleTime); - - // derive the actual state of the asset by applying the CreditEvent (updates performance of asset) - (nextState, ) = computeStateAndPayoffForEvent(assetId, nextState, ceEvent); - } - - // store the resulting state - assetRegistry.setState(assetId, nextState); + (bool settledPayoff, int256 payoff) = settleEventAndUpdateState(assetId, _event); // mark event as settled if (settledPayoff == true) { @@ -262,11 +232,14 @@ abstract contract BaseActor is Conversions, EventUtils, BusinessDayConventions, return IERC20(token).transferFrom(payer, payee, amount); } - function computeStateAndPayoffForEvent(bytes32 assetId, State memory state, bytes32 _event) + /** + * @notice Contract-type specific logic for processing an event required by the use of + * contract-type specific Terms and State. Has to be implemented by each contract-type specific Actor. + */ + function settleEventAndUpdateState(bytes32 assetId, bytes32 _event) internal - view virtual - returns (State memory, int256); + returns (bool, int256); /** * @notice Retrieves external data (such as market object data, block time, underlying asset state) diff --git a/packages/ap-contracts/contracts/Core/Base/AssetRegistry/BaseRegistry.sol b/packages/ap-contracts/contracts/Core/Base/AssetRegistry/BaseRegistry.sol index 0c96ffca..14963aa8 100644 --- a/packages/ap-contracts/contracts/Core/Base/AssetRegistry/BaseRegistry.sol +++ b/packages/ap-contracts/contracts/Core/Base/AssetRegistry/BaseRegistry.sol @@ -72,11 +72,11 @@ abstract contract BaseRegistry is /** * @notice Stores the addresses of the owners (owner of creator-side payment obligations, - * owner of creator-side payment claims), the initial state of an asset, the schedule of the asset + * owner of creator-side payment claims), the schedule of the asset * and sets the address of the actor (address of account which is allowed to update the state). + * Terms and State are contract-type specific and have to be handled by the deriving contracts. * @dev The state of the asset can only be updates by a whitelisted actor. * @param assetId id of the asset - * @param state initial state of the asset * @param schedule schedule of the asset * @param ownership ownership of the asset * @param engine ACTUS Engine of the asset @@ -85,7 +85,6 @@ abstract contract BaseRegistry is */ function setAsset( bytes32 assetId, - State memory state, bytes32[] memory schedule, AssetOwnership memory ownership, address engine, @@ -112,8 +111,6 @@ abstract contract BaseRegistry is asset.engine = engine; asset.actor = actor; - asset.encodeAndSetState(state); - asset.encodeAndSetFinalizedState(state); asset.encodeAndSetSchedule(schedule); // set external admin if specified diff --git a/packages/ap-contracts/contracts/Core/Base/AssetRegistry/BaseRegistryStorage.sol b/packages/ap-contracts/contracts/Core/Base/AssetRegistry/BaseRegistryStorage.sol index 35b6c482..d5a9c760 100644 --- a/packages/ap-contracts/contracts/Core/Base/AssetRegistry/BaseRegistryStorage.sol +++ b/packages/ap-contracts/contracts/Core/Base/AssetRegistry/BaseRegistryStorage.sol @@ -4,7 +4,6 @@ pragma experimental ABIEncoderV2; import "../Conversions.sol"; import "../SharedTypes.sol"; -import "./State/StateEncoder.sol"; import "./Schedule/ScheduleEncoder.sol"; @@ -51,7 +50,6 @@ struct Asset { */ abstract contract BaseRegistryStorage { - using StateEncoder for Asset; using ScheduleEncoder for Asset; // AssetId => Asset diff --git a/packages/ap-contracts/contracts/Core/Base/AssetRegistry/State/IStateRegistry.sol b/packages/ap-contracts/contracts/Core/Base/AssetRegistry/State/IStateRegistry.sol index 475cf56a..4816e5ee 100644 --- a/packages/ap-contracts/contracts/Core/Base/AssetRegistry/State/IStateRegistry.sol +++ b/packages/ap-contracts/contracts/Core/Base/AssetRegistry/State/IStateRegistry.sol @@ -7,16 +7,6 @@ import "../../SharedTypes.sol"; interface IStateRegistry { - function getState(bytes32 assetId) - external - view - returns (State memory); - - function getFinalizedState(bytes32 assetId) - external - view - returns (State memory); - function getEnumValueForStateAttribute(bytes32 assetId, bytes32 attribute) external view @@ -31,10 +21,4 @@ interface IStateRegistry { external view returns (uint256); - - function setState(bytes32 assetId, State calldata state) - external; - - function setFinalizedState(bytes32 assetId, State calldata state) - external; } diff --git a/packages/ap-contracts/contracts/Core/Base/AssetRegistry/State/StateEncoder.sol b/packages/ap-contracts/contracts/Core/Base/AssetRegistry/State/StateEncoder.sol deleted file mode 100644 index 7f1b9184..00000000 --- a/packages/ap-contracts/contracts/Core/Base/AssetRegistry/State/StateEncoder.sol +++ /dev/null @@ -1,162 +0,0 @@ -// "SPDX-License-Identifier: Apache-2.0" -pragma solidity ^0.6.11; - -import "../../SharedTypes.sol"; -import "../BaseRegistryStorage.sol"; - - -library StateEncoder { - - function storeInPackedState(Asset storage asset, bytes32 attributeKey, bytes32 value) private { - // skip if value did not change - if (asset.packedState[attributeKey] == value) return; - asset.packedState[attributeKey] = value; - } - - /** - * @dev Tightly pack and store State - */ - function encodeAndSetState(Asset storage asset, State memory state) internal { - storeInPackedState(asset, "contractPerformance", bytes32(uint256(uint8(state.contractPerformance))) << 248); - storeInPackedState(asset, "statusDate", bytes32(state.statusDate)); - storeInPackedState(asset, "nonPerformingDate", bytes32(state.nonPerformingDate)); - storeInPackedState(asset, "maturityDate", bytes32(state.maturityDate)); - storeInPackedState(asset, "exerciseDate", bytes32(state.exerciseDate)); - storeInPackedState(asset, "terminationDate", bytes32(state.terminationDate)); - storeInPackedState(asset, "notionalPrincipal", bytes32(state.notionalPrincipal)); - storeInPackedState(asset, "accruedInterest", bytes32(state.accruedInterest)); - storeInPackedState(asset, "feeAccrued", bytes32(state.feeAccrued)); - storeInPackedState(asset, "nominalInterestRate", bytes32(state.nominalInterestRate)); - storeInPackedState(asset, "interestScalingMultiplier", bytes32(state.interestScalingMultiplier)); - storeInPackedState(asset, "notionalScalingMultiplier", bytes32(state.notionalScalingMultiplier)); - storeInPackedState(asset, "nextPrincipalRedemptionPayment", bytes32(state.nextPrincipalRedemptionPayment)); - storeInPackedState(asset, "exerciseAmount", bytes32(state.exerciseAmount)); - - storeInPackedState(asset, "exerciseQuantity", bytes32(state.exerciseQuantity)); - storeInPackedState(asset, "quantity", bytes32(state.quantity)); - storeInPackedState(asset, "couponAmountFixed", bytes32(state.couponAmountFixed)); - storeInPackedState(asset, "marginFactor", bytes32(state.marginFactor)); - storeInPackedState(asset, "adjustmentFactor", bytes32(state.adjustmentFactor)); - storeInPackedState(asset, "lastCouponDay", bytes32(state.lastCouponDay)); - } - - /** - * @dev Tightly pack and store finalized State - */ - function encodeAndSetFinalizedState(Asset storage asset, State memory state) internal { - storeInPackedState(asset, "F_contractPerformance", bytes32(uint256(uint8(state.contractPerformance))) << 248); - storeInPackedState(asset, "F_statusDate", bytes32(state.statusDate)); - storeInPackedState(asset, "F_nonPerformingDate", bytes32(state.nonPerformingDate)); - storeInPackedState(asset, "F_maturityDate", bytes32(state.maturityDate)); - storeInPackedState(asset, "F_exerciseDate", bytes32(state.exerciseDate)); - storeInPackedState(asset, "F_terminationDate", bytes32(state.terminationDate)); - storeInPackedState(asset, "F_notionalPrincipal", bytes32(state.notionalPrincipal)); - storeInPackedState(asset, "F_accruedInterest", bytes32(state.accruedInterest)); - storeInPackedState(asset, "F_feeAccrued", bytes32(state.feeAccrued)); - storeInPackedState(asset, "F_nominalInterestRate", bytes32(state.nominalInterestRate)); - storeInPackedState(asset, "F_interestScalingMultiplier", bytes32(state.interestScalingMultiplier)); - storeInPackedState(asset, "F_notionalScalingMultiplier", bytes32(state.notionalScalingMultiplier)); - storeInPackedState(asset, "F_nextPrincipalRedemptionPayment", bytes32(state.nextPrincipalRedemptionPayment)); - storeInPackedState(asset, "F_exerciseAmount", bytes32(state.exerciseAmount)); - - storeInPackedState(asset, "F_exerciseQuantity", bytes32(state.exerciseQuantity)); - storeInPackedState(asset, "F_quantity", bytes32(state.quantity)); - storeInPackedState(asset, "F_couponAmountFixed", bytes32(state.couponAmountFixed)); - storeInPackedState(asset, "F_marginFactor", bytes32(state.marginFactor)); - storeInPackedState(asset, "F_adjustmentFactor", bytes32(state.adjustmentFactor)); - storeInPackedState(asset, "F_lastCouponDay", bytes32(state.lastCouponDay)); - } - - /** - * @dev Decode and load the State of the asset - */ - function decodeAndGetState(Asset storage asset) internal view returns (State memory) { - return State( - ContractPerformance(uint8(uint256(asset.packedState["contractPerformance"] >> 248))), - uint256(asset.packedState["statusDate"]), - uint256(asset.packedState["nonPerformingDate"]), - uint256(asset.packedState["maturityDate"]), - uint256(asset.packedState["exerciseDate"]), - uint256(asset.packedState["terminationDate"]), - - uint256(asset.packedState["lastCouponDay"]), - - int256(asset.packedState["notionalPrincipal"]), - int256(asset.packedState["accruedInterest"]), - int256(asset.packedState["feeAccrued"]), - int256(asset.packedState["nominalInterestRate"]), - int256(asset.packedState["interestScalingMultiplier"]), - int256(asset.packedState["notionalScalingMultiplier"]), - int256(asset.packedState["nextPrincipalRedemptionPayment"]), - int256(asset.packedState["exerciseAmount"]), - - int256(asset.packedState["exerciseQuantity"]), - int256(asset.packedState["quantity"]), - int256(asset.packedState["couponAmountFixed"]), - int256(asset.packedState["marginFactor"]), - int256(asset.packedState["adjustmentFactor"]) - ); - } - - /** - * @dev Decode and load the finalized State of the asset - */ - function decodeAndGetFinalizedState(Asset storage asset) internal view returns (State memory) { - return State( - ContractPerformance(uint8(uint256(asset.packedState["F_contractPerformance"] >> 248))), - uint256(asset.packedState["F_statusDate"]), - uint256(asset.packedState["F_nonPerformingDate"]), - uint256(asset.packedState["F_maturityDate"]), - uint256(asset.packedState["F_exerciseDate"]), - uint256(asset.packedState["F_terminationDate"]), - - uint256(asset.packedState["F_lastCouponDay"]), - - int256(asset.packedState["F_notionalPrincipal"]), - int256(asset.packedState["F_accruedInterest"]), - int256(asset.packedState["F_feeAccrued"]), - int256(asset.packedState["F_nominalInterestRate"]), - int256(asset.packedState["F_interestScalingMultiplier"]), - int256(asset.packedState["F_notionalScalingMultiplier"]), - int256(asset.packedState["F_nextPrincipalRedemptionPayment"]), - int256(asset.packedState["F_exerciseAmount"]), - - int256(asset.packedState["F_exerciseQuantity"]), - int256(asset.packedState["F_quantity"]), - int256(asset.packedState["F_couponAmountFixed"]), - int256(asset.packedState["F_marginFactor"]), - int256(asset.packedState["F_adjustmentFactor"]) - ); - } - - - function decodeAndGetEnumValueForStateAttribute(Asset storage asset, bytes32 attributeKey) - internal - view - returns (uint8) - { - if (attributeKey == bytes32("contractPerformance")) { - return uint8(uint256(asset.packedState["contractPerformance"] >> 248)); - } else if (attributeKey == bytes32("F_contractPerformance")) { - return uint8(uint256(asset.packedState["F_contractPerformance"] >> 248)); - } else { - return uint8(0); - } - } - - function decodeAndGetUIntValueForForStateAttribute(Asset storage asset, bytes32 attributeKey) - internal - view - returns (uint256) - { - return uint256(asset.packedState[attributeKey]); - } - - function decodeAndGetIntValueForForStateAttribute(Asset storage asset, bytes32 attributeKey) - internal - view - returns (int256) - { - return int256(asset.packedState[attributeKey]); - } -} \ No newline at end of file diff --git a/packages/ap-contracts/contracts/Core/Base/AssetRegistry/State/StateRegistry.sol b/packages/ap-contracts/contracts/Core/Base/AssetRegistry/State/StateRegistry.sol index 51fd3ddd..976de78f 100644 --- a/packages/ap-contracts/contracts/Core/Base/AssetRegistry/State/StateRegistry.sol +++ b/packages/ap-contracts/contracts/Core/Base/AssetRegistry/State/StateRegistry.sol @@ -2,102 +2,31 @@ pragma solidity ^0.6.11; pragma experimental ABIEncoderV2; -import "../BaseRegistryStorage.sol"; -import "../AccessControl/AccessControl.sol"; -import "./IStateRegistry.sol"; - /** * @title StateRegistry */ -contract StateRegistry is BaseRegistryStorage, AccessControl, IStateRegistry { +abstract contract StateRegistry { event UpdatedState(bytes32 indexed assetId, uint256 statusDate); event UpdatedFinalizedState(bytes32 indexed assetId, uint256 statusDate); - /** - * @notice Returns the state of an asset. - * @param assetId id of the asset - * @return state of the asset - */ - function getState(bytes32 assetId) - external - view - override - returns (State memory) - { - return assets[assetId].decodeAndGetState(); - } - - /** - * @notice Returns the state of an asset. - * @param assetId id of the asset - * @return state of the asset - */ - function getFinalizedState(bytes32 assetId) - external - view - override - returns (State memory) - { - return assets[assetId].decodeAndGetFinalizedState(); - } - function getEnumValueForStateAttribute(bytes32 assetId, bytes32 attribute) public view - override - returns (uint8) - { - return assets[assetId].decodeAndGetEnumValueForStateAttribute(attribute); - } + virtual + returns (uint8); function getIntValueForStateAttribute(bytes32 assetId, bytes32 attribute) public view - override - returns (int256) - { - return assets[assetId].decodeAndGetIntValueForForStateAttribute(attribute); - } + virtual + returns (int256); function getUintValueForStateAttribute(bytes32 assetId, bytes32 attribute) public view - override - returns (uint256) - { - return assets[assetId].decodeAndGetUIntValueForForStateAttribute(attribute); - } - - /** - * @notice Sets next state of an asset. - * @dev Can only be updated by the assets actor or by an authorized account. - * @param assetId id of the asset - * @param state next state of the asset - */ - function setState(bytes32 assetId, State calldata state) - external - override - isAuthorized (assetId) - { - assets[assetId].encodeAndSetState(state); - emit UpdatedState(assetId, state.statusDate); - } - - /** - * @notice Sets next finalized state of an asset. - * @dev Can only be updated by the assets actor or by an authorized account. - * @param assetId id of the asset - * @param state next state of the asset - */ - function setFinalizedState(bytes32 assetId, State calldata state) - external - override - isAuthorized (assetId) - { - assets[assetId].encodeAndSetFinalizedState(state); - emit UpdatedFinalizedState(assetId, state.statusDate); - } + virtual + returns (uint256); } diff --git a/packages/ap-contracts/contracts/Core/Base/Custodian/Custodian.sol b/packages/ap-contracts/contracts/Core/Base/Custodian/Custodian.sol index 3c8b6648..a226a664 100644 --- a/packages/ap-contracts/contracts/Core/Base/Custodian/Custodian.sol +++ b/packages/ap-contracts/contracts/Core/Base/Custodian/Custodian.sol @@ -120,7 +120,7 @@ contract Custodian is ICustodian, ReentrancyGuard, Conversions { ContractRole contractRole = ContractRole(cecRegistry.getEnumValueForTermsAttribute(assetId, "contractRole")); ContractReference memory contractReference_2 = cecRegistry.getContractReferenceValueForTermsAttribute(assetId, "contractReference_2"); - State memory state = cecRegistry.getState(assetId); + CECState memory state = cecRegistry.getState(assetId); AssetOwnership memory ownership = cecRegistry.getOwnership(assetId); // derive address of collateralizer diff --git a/packages/ap-contracts/contracts/Core/CEC/CECActor.sol b/packages/ap-contracts/contracts/Core/CEC/CECActor.sol index 4f512d7d..462ac483 100644 --- a/packages/ap-contracts/contracts/Core/CEC/CECActor.sol +++ b/packages/ap-contracts/contracts/Core/CEC/CECActor.sol @@ -101,7 +101,7 @@ contract CECActor is BaseActor { } // compute the initial state of the asset - State memory initialState = ICECEngine(engine).computeInitialState(terms); + CECState memory initialState = ICECEngine(engine).computeInitialState(terms); // register the asset in the AssetRegistry ICECRegistry(address(assetRegistry)).registerAsset( @@ -118,16 +118,28 @@ contract CECActor is BaseActor { emit InitializedAsset(assetId, ContractType.CEC, ownership.creatorObligor, ownership.counterpartyObligor); } - function computeStateAndPayoffForEvent(bytes32 assetId, State memory state, bytes32 _event) +/** + * @notice Contract-type specific logic for processing an event required by the use of + * contract-type specific Terms and State. + */ + function settleEventAndUpdateState(bytes32 assetId, bytes32 _event) internal - view override - returns (State memory, int256) + returns (bool, int256) { - address engine = assetRegistry.getEngine(assetId); CECTerms memory terms = ICECRegistry(address(assetRegistry)).getTerms(assetId); + CECState memory state = ICECRegistry(address(assetRegistry)).getState(assetId); + address engine = assetRegistry.getEngine(assetId); + + // get finalized state if asset is not performant + if (state.contractPerformance != ContractPerformance.PF) { + state = ICECRegistry(address(assetRegistry)).getFinalizedState(assetId); + } + (EventType eventType, uint256 scheduleTime) = decodeEvent(_event); + // get external data for the next event + // compute payoff and the next state by applying the event to the current state int256 payoff = ICECEngine(engine).computePayoffForEvent( terms, state, @@ -138,7 +150,7 @@ contract CECActor is BaseActor { shiftCalcTime(scheduleTime, terms.businessDayConvention, terms.calendar, terms.maturityDate) ) ); - state = ICECEngine(engine).computeStateForEvent( + CECState memory nextState = ICECEngine(engine).computeStateForEvent( terms, state, _event, @@ -149,6 +161,40 @@ contract CECActor is BaseActor { ) ); - return (state, payoff); + // try to settle payoff of event + bool settledPayoff = settlePayoffForEvent(assetId, _event, payoff); + + if (settledPayoff == false) { + // if the obligation can't be fulfilled and the performance changed from performant to DL, DQ or DF, + // store the last performant state of the asset + // (if the obligation is later fulfilled before the asset reaches default, + // the last performant state is used to derive subsequent states of the asset) + if (state.contractPerformance == ContractPerformance.PF) { + ICECRegistry(address(assetRegistry)).setFinalizedState(assetId, state); + } + + // store event as pending event for future settlement + assetRegistry.pushPendingEvent(assetId, _event); + + // create CreditEvent + bytes32 ceEvent = encodeEvent(EventType.CE, scheduleTime); + + // derive the actual state of the asset by applying the CreditEvent (updates performance of asset) + nextState = ICECEngine(engine).computeStateForEvent( + terms, + state, + ceEvent, + getExternalDataForSTF( + assetId, + EventType.CE, + shiftCalcTime(scheduleTime, terms.businessDayConvention, terms.calendar, terms.maturityDate) + ) + ); + } + + // store the resulting state + ICECRegistry(address(assetRegistry)).setState(assetId, nextState); + + return (settledPayoff, payoff); } } \ No newline at end of file diff --git a/packages/ap-contracts/contracts/Core/CEC/CECEncoder.sol b/packages/ap-contracts/contracts/Core/CEC/CECEncoder.sol index d4db2f6e..29b8c7c9 100644 --- a/packages/ap-contracts/contracts/Core/CEC/CECEncoder.sol +++ b/packages/ap-contracts/contracts/Core/CEC/CECEncoder.sol @@ -13,6 +13,12 @@ library CECEncoder { if (asset.packedTerms[attributeKey] == value) return; asset.packedTerms[attributeKey] = value; } + + function storeInPackedState(Asset storage asset, bytes32 attributeKey, bytes32 value) private { + // skip if value did not change + if (asset.packedState[attributeKey] == value) return; + asset.packedState[attributeKey] = value; + } /** * @dev Tightly pack and store only non-zero overwritten terms (LifecycleTerms) @@ -114,7 +120,7 @@ library CECEncoder { ); } - function decodeAndGetEnumValueForCECAttribute(Asset storage asset, bytes32 attributeKey) + function decodeAndGetEnumValueForCECTermsAttribute(Asset storage asset, bytes32 attributeKey) external view returns (uint8) @@ -140,7 +146,7 @@ library CECEncoder { } } - function decodeAndGetAddressValueForForCECAttribute(Asset storage /* asset */, bytes32 /* attributeKey */) + function decodeAndGetAddressValueForCECTermsAttribute(Asset storage /* asset */, bytes32 /* attributeKey */) external pure returns (address) @@ -148,7 +154,7 @@ library CECEncoder { return address(0); } - function decodeAndGetBytes32ValueForForCECAttribute(Asset storage asset, bytes32 attributeKey) + function decodeAndGetBytes32ValueForCECTermsAttribute(Asset storage asset, bytes32 attributeKey) external view returns (bytes32) @@ -156,7 +162,7 @@ library CECEncoder { return asset.packedTerms[attributeKey]; } - function decodeAndGetUIntValueForForCECAttribute(Asset storage asset, bytes32 attributeKey) + function decodeAndGetUIntValueForCECTermsAttribute(Asset storage asset, bytes32 attributeKey) external view returns (uint256) @@ -164,7 +170,7 @@ library CECEncoder { return uint256(asset.packedTerms[attributeKey]); } - function decodeAndGetIntValueForForCECAttribute(Asset storage asset, bytes32 attributeKey) + function decodeAndGetIntValueForCECTermsAttribute(Asset storage asset, bytes32 attributeKey) external view returns (int256) @@ -172,7 +178,7 @@ library CECEncoder { return int256(asset.packedTerms[attributeKey]); } - function decodeAndGetPeriodValueForForCECAttribute(Asset storage /* asset */, bytes32 /* attributeKey */) + function decodeAndGetPeriodValueForCECTermsAttribute(Asset storage /* asset */, bytes32 /* attributeKey */) external pure returns (IP memory) @@ -180,7 +186,7 @@ library CECEncoder { return IP(0, P(0), false); } - function decodeAndGetCycleValueForForCECAttribute(Asset storage /* asset */, bytes32 /* attributeKey */) + function decodeAndGetCycleValueForCECTermsAttribute(Asset storage /* asset */, bytes32 /* attributeKey */) external pure returns (IPS memory) @@ -188,7 +194,7 @@ library CECEncoder { return IPS(0, P(0), S(0), false); } - function decodeAndGetContractReferenceValueForCECAttribute(Asset storage asset , bytes32 attributeKey ) + function decodeAndGetContractReferenceValueForCECTermsAttribute(Asset storage asset , bytes32 attributeKey ) external view returns (ContractReference memory) @@ -216,4 +222,104 @@ library CECEncoder { ); } } + + /** + * @dev Tightly pack and store CECState + */ + function encodeAndSetCECState(Asset storage asset, CECState memory state) external { + storeInPackedState(asset, "contractPerformance", bytes32(uint256(uint8(state.contractPerformance))) << 248); + storeInPackedState(asset, "statusDate", bytes32(state.statusDate)); + storeInPackedState(asset, "maturityDate", bytes32(state.maturityDate)); + storeInPackedState(asset, "exerciseDate", bytes32(state.exerciseDate)); + storeInPackedState(asset, "terminationDate", bytes32(state.terminationDate)); + storeInPackedState(asset, "notionalPrincipal", bytes32(state.notionalPrincipal)); + storeInPackedState(asset, "feeAccrued", bytes32(state.feeAccrued)); + storeInPackedState(asset, "exerciseAmount", bytes32(state.exerciseAmount)); + } + + /** + * @dev Tightly pack and store finalized CECState + */ + function encodeAndSetFinalizedCECState(Asset storage asset, CECState memory state) external { + storeInPackedState(asset, "F_contractPerformance", bytes32(uint256(uint8(state.contractPerformance))) << 248); + storeInPackedState(asset, "F_statusDate", bytes32(state.statusDate)); + storeInPackedState(asset, "F_maturityDate", bytes32(state.maturityDate)); + storeInPackedState(asset, "F_exerciseDate", bytes32(state.exerciseDate)); + storeInPackedState(asset, "F_terminationDate", bytes32(state.terminationDate)); + storeInPackedState(asset, "F_notionalPrincipal", bytes32(state.notionalPrincipal)); + storeInPackedState(asset, "F_feeAccrued", bytes32(state.feeAccrued)); + storeInPackedState(asset, "F_exerciseAmount", bytes32(state.exerciseAmount)); + } + + /** + * @dev Decode and load the CECState of the asset + */ + function decodeAndGetCECState(Asset storage asset) + external + view + returns (CECState memory) + { + return CECState( + ContractPerformance(uint8(uint256(asset.packedState["contractPerformance"] >> 248))), + uint256(asset.packedState["statusDate"]), + uint256(asset.packedState["maturityDate"]), + uint256(asset.packedState["exerciseDate"]), + uint256(asset.packedState["terminationDate"]), + + int256(asset.packedState["notionalPrincipal"]), + int256(asset.packedState["feeAccrued"]), + int256(asset.packedState["exerciseAmomunt"]) + ); + } + + /** + * @dev Decode and load the finalized CECState of the asset + */ + function decodeAndGetFinalizedCECState(Asset storage asset) + external + view + returns (CECState memory) + { + return CECState( + ContractPerformance(uint8(uint256(asset.packedState["F_contractPerformance"] >> 248))), + uint256(asset.packedState["F_statusDate"]), + uint256(asset.packedState["F_maturityDate"]), + uint256(asset.packedState["F_exerciseDate"]), + uint256(asset.packedState["F_terminationDate"]), + + int256(asset.packedState["F_notionalPrincipal"]), + int256(asset.packedState["F_feeAccrued"]), + int256(asset.packedState["F_exerciseAmomunt"]) + ); + } + + function decodeAndGetEnumValueForCECStateAttribute(Asset storage asset, bytes32 attributeKey) + external + view + returns (uint8) + { + if (attributeKey == bytes32("contractPerformance")) { + return uint8(uint256(asset.packedState["contractPerformance"] >> 248)); + } else if (attributeKey == bytes32("F_contractPerformance")) { + return uint8(uint256(asset.packedState["F_contractPerformance"] >> 248)); + } else { + return uint8(0); + } + } + + function decodeAndGetUIntValueForCECStateAttribute(Asset storage asset, bytes32 attributeKey) + external + view + returns (uint256) + { + return uint256(asset.packedState[attributeKey]); + } + + function decodeAndGetIntValueForCECStateAttribute(Asset storage asset, bytes32 attributeKey) + external + view + returns (int256) + { + return int256(asset.packedState[attributeKey]); + } } \ No newline at end of file diff --git a/packages/ap-contracts/contracts/Core/CEC/CECRegistry.sol b/packages/ap-contracts/contracts/Core/CEC/CECRegistry.sol index 9df4ec25..b75a325a 100644 --- a/packages/ap-contracts/contracts/Core/CEC/CECRegistry.sol +++ b/packages/ap-contracts/contracts/Core/CEC/CECRegistry.sol @@ -36,7 +36,7 @@ contract CECRegistry is BaseRegistry, ICECRegistry { function registerAsset( bytes32 assetId, CECTerms calldata terms, - State calldata state, + CECState calldata state, bytes32[] calldata schedule, AssetOwnership calldata ownership, address engine, @@ -47,8 +47,10 @@ contract CECRegistry is BaseRegistry, ICECRegistry { override onlyApprovedActors { - setAsset(assetId, state, schedule, ownership, engine, actor, admin); + setAsset(assetId, schedule, ownership, engine, actor, admin); assets[assetId].encodeAndSetCECTerms(terms); + assets[assetId].encodeAndSetCECState(state); + assets[assetId].encodeAndSetFinalizedCECState(state); } /** @@ -86,7 +88,7 @@ contract CECRegistry is BaseRegistry, ICECRegistry { override(ITermsRegistry, TermsRegistry) returns (uint8) { - return assets[assetId].decodeAndGetEnumValueForCECAttribute(attribute); + return assets[assetId].decodeAndGetEnumValueForCECTermsAttribute(attribute); } function getAddressValueForTermsAttribute(bytes32 assetId, bytes32 attribute) @@ -95,7 +97,7 @@ contract CECRegistry is BaseRegistry, ICECRegistry { override(ITermsRegistry, TermsRegistry) returns (address) { - return assets[assetId].decodeAndGetAddressValueForForCECAttribute(attribute); + return assets[assetId].decodeAndGetAddressValueForCECTermsAttribute(attribute); } function getBytes32ValueForTermsAttribute(bytes32 assetId, bytes32 attribute) @@ -104,7 +106,7 @@ contract CECRegistry is BaseRegistry, ICECRegistry { override(ITermsRegistry, TermsRegistry) returns (bytes32) { - return assets[assetId].decodeAndGetBytes32ValueForForCECAttribute(attribute); + return assets[assetId].decodeAndGetBytes32ValueForCECTermsAttribute(attribute); } function getUIntValueForTermsAttribute(bytes32 assetId, bytes32 attribute) @@ -113,7 +115,7 @@ contract CECRegistry is BaseRegistry, ICECRegistry { override(ITermsRegistry, TermsRegistry) returns (uint256) { - return assets[assetId].decodeAndGetUIntValueForForCECAttribute(attribute); + return assets[assetId].decodeAndGetUIntValueForCECTermsAttribute(attribute); } function getIntValueForTermsAttribute(bytes32 assetId, bytes32 attribute) @@ -122,7 +124,7 @@ contract CECRegistry is BaseRegistry, ICECRegistry { override(ITermsRegistry, TermsRegistry) returns (int256) { - return assets[assetId].decodeAndGetIntValueForForCECAttribute(attribute); + return assets[assetId].decodeAndGetIntValueForCECTermsAttribute(attribute); } function getPeriodValueForTermsAttribute(bytes32 assetId, bytes32 attribute) @@ -131,7 +133,7 @@ contract CECRegistry is BaseRegistry, ICECRegistry { override(ITermsRegistry, TermsRegistry) returns (IP memory) { - return assets[assetId].decodeAndGetPeriodValueForForCECAttribute(attribute); + return assets[assetId].decodeAndGetPeriodValueForCECTermsAttribute(attribute); } function getCycleValueForTermsAttribute(bytes32 assetId, bytes32 attribute) @@ -140,7 +142,7 @@ contract CECRegistry is BaseRegistry, ICECRegistry { override(ITermsRegistry, TermsRegistry) returns (IPS memory) { - return assets[assetId].decodeAndGetCycleValueForForCECAttribute(attribute); + return assets[assetId].decodeAndGetCycleValueForCECTermsAttribute(attribute); } function getContractReferenceValueForTermsAttribute(bytes32 assetId, bytes32 attribute) @@ -149,7 +151,92 @@ contract CECRegistry is BaseRegistry, ICECRegistry { override(ITermsRegistry, TermsRegistry) returns (ContractReference memory) { - return assets[assetId].decodeAndGetContractReferenceValueForCECAttribute(attribute); + return assets[assetId].decodeAndGetContractReferenceValueForCECTermsAttribute(attribute); + } + + /** + * @notice Returns the state of an asset. + * @param assetId id of the asset + * @return state of the asset + */ + function getState(bytes32 assetId) + external + view + override + returns (CECState memory) + { + return assets[assetId].decodeAndGetCECState(); + } + + /** + * @notice Returns the state of an asset. + * @param assetId id of the asset + * @return state of the asset + */ + function getFinalizedState(bytes32 assetId) + external + view + override + returns (CECState memory) + { + return assets[assetId].decodeAndGetFinalizedCECState(); + } + + /** + * @notice Sets next state of an asset. + * @dev Can only be updated by the assets actor or by an authorized account. + * @param assetId id of the asset + * @param state next state of the asset + */ + function setState(bytes32 assetId, CECState calldata state) + external + override + isAuthorized (assetId) + { + assets[assetId].encodeAndSetCECState(state); + emit UpdatedState(assetId, state.statusDate); + } + + /** + * @notice Sets next finalized state of an asset. + * @dev Can only be updated by the assets actor or by an authorized account. + * @param assetId id of the asset + * @param state next state of the asset + */ + function setFinalizedState(bytes32 assetId, CECState calldata state) + external + override + isAuthorized (assetId) + { + assets[assetId].encodeAndSetFinalizedCECState(state); + emit UpdatedFinalizedState(assetId, state.statusDate); + } + + function getEnumValueForStateAttribute(bytes32 assetId, bytes32 attribute) + public + view + override(IStateRegistry, StateRegistry) + returns (uint8) + { + return assets[assetId].decodeAndGetEnumValueForCECStateAttribute(attribute); + } + + function getIntValueForStateAttribute(bytes32 assetId, bytes32 attribute) + public + view + override(IStateRegistry, StateRegistry) + returns (int256) + { + return assets[assetId].decodeAndGetIntValueForCECStateAttribute(attribute); + } + + function getUintValueForStateAttribute(bytes32 assetId, bytes32 attribute) + public + view + override(IStateRegistry, StateRegistry) + returns (uint256) + { + return assets[assetId].decodeAndGetUIntValueForCECStateAttribute(attribute); } function getNextCyclicEvent(bytes32 /* assetId */) diff --git a/packages/ap-contracts/contracts/Core/CEC/ICECRegistry.sol b/packages/ap-contracts/contracts/Core/CEC/ICECRegistry.sol index 5dce25c2..e922c908 100644 --- a/packages/ap-contracts/contracts/Core/CEC/ICECRegistry.sol +++ b/packages/ap-contracts/contracts/Core/CEC/ICECRegistry.sol @@ -11,7 +11,7 @@ interface ICECRegistry is IAssetRegistry { function registerAsset( bytes32 assetId, CECTerms calldata terms, - State calldata state, + CECState calldata state, bytes32[] calldata schedule, AssetOwnership calldata ownership, address engine, @@ -27,4 +27,20 @@ interface ICECRegistry is IAssetRegistry { function setTerms(bytes32 assetId, CECTerms calldata terms) external; + + function getState(bytes32 assetId) + external + view + returns (CECState memory); + + function setState(bytes32 assetId, CECState calldata terms) + external; + + function getFinalizedState(bytes32 assetId) + external + view + returns (CECState memory); + + function setFinalizedState(bytes32 assetId, CECState calldata terms) + external; } diff --git a/packages/ap-contracts/contracts/Core/CEG/CEGActor.sol b/packages/ap-contracts/contracts/Core/CEG/CEGActor.sol index 1d645147..fb739cfd 100644 --- a/packages/ap-contracts/contracts/Core/CEG/CEGActor.sol +++ b/packages/ap-contracts/contracts/Core/CEG/CEGActor.sol @@ -56,7 +56,7 @@ contract CEGActor is BaseActor { // todo add guarantee validation logic for contract reference 2 // compute the initial state of the asset - State memory initialState = ICEGEngine(engine).computeInitialState(terms); + CEGState memory initialState = ICEGEngine(engine).computeInitialState(terms); // register the asset in the AssetRegistry ICEGRegistry(address(assetRegistry)).registerAsset( @@ -73,16 +73,28 @@ contract CEGActor is BaseActor { emit InitializedAsset(assetId, ContractType.CEG, ownership.creatorObligor, ownership.counterpartyObligor); } - function computeStateAndPayoffForEvent(bytes32 assetId, State memory state, bytes32 _event) + /** + * @notice Contract-type specific logic for processing an event required by the use of + * contract-type specific Terms and State. + */ + function settleEventAndUpdateState(bytes32 assetId, bytes32 _event) internal - view override - returns (State memory, int256) + returns (bool, int256) { - address engine = assetRegistry.getEngine(assetId); CEGTerms memory terms = ICEGRegistry(address(assetRegistry)).getTerms(assetId); + CEGState memory state = ICEGRegistry(address(assetRegistry)).getState(assetId); + address engine = assetRegistry.getEngine(assetId); + + // get finalized state if asset is not performant + if (state.contractPerformance != ContractPerformance.PF) { + state = ICEGRegistry(address(assetRegistry)).getFinalizedState(assetId); + } + (EventType eventType, uint256 scheduleTime) = decodeEvent(_event); + // get external data for the next event + // compute payoff and the next state by applying the event to the current state int256 payoff = ICEGEngine(engine).computePayoffForEvent( terms, state, @@ -93,7 +105,7 @@ contract CEGActor is BaseActor { shiftCalcTime(scheduleTime, terms.businessDayConvention, terms.calendar, terms.maturityDate) ) ); - state = ICEGEngine(engine).computeStateForEvent( + CEGState memory nextState = ICEGEngine(engine).computeStateForEvent( terms, state, _event, @@ -104,6 +116,40 @@ contract CEGActor is BaseActor { ) ); - return (state, payoff); + // try to settle payoff of event + bool settledPayoff = settlePayoffForEvent(assetId, _event, payoff); + + if (settledPayoff == false) { + // if the obligation can't be fulfilled and the performance changed from performant to DL, DQ or DF, + // store the last performant state of the asset + // (if the obligation is later fulfilled before the asset reaches default, + // the last performant state is used to derive subsequent states of the asset) + if (state.contractPerformance == ContractPerformance.PF) { + ICEGRegistry(address(assetRegistry)).setFinalizedState(assetId, state); + } + + // store event as pending event for future settlement + assetRegistry.pushPendingEvent(assetId, _event); + + // create CreditEvent + bytes32 ceEvent = encodeEvent(EventType.CE, scheduleTime); + + // derive the actual state of the asset by applying the CreditEvent (updates performance of asset) + nextState = ICEGEngine(engine).computeStateForEvent( + terms, + state, + ceEvent, + getExternalDataForSTF( + assetId, + EventType.CE, + shiftCalcTime(scheduleTime, terms.businessDayConvention, terms.calendar, terms.maturityDate) + ) + ); + } + + // store the resulting state + ICEGRegistry(address(assetRegistry)).setState(assetId, nextState); + + return (settledPayoff, payoff); } } \ No newline at end of file diff --git a/packages/ap-contracts/contracts/Core/CEG/CEGEncoder.sol b/packages/ap-contracts/contracts/Core/CEG/CEGEncoder.sol index f6b4a8b1..ad593b9d 100644 --- a/packages/ap-contracts/contracts/Core/CEG/CEGEncoder.sol +++ b/packages/ap-contracts/contracts/Core/CEG/CEGEncoder.sol @@ -13,6 +13,12 @@ library CEGEncoder { if (asset.packedTerms[attributeKey] == value) return; asset.packedTerms[attributeKey] = value; } + + function storeInPackedState(Asset storage asset, bytes32 attributeKey, bytes32 value) private { + // skip if value did not change + if (asset.packedState[attributeKey] == value) return; + asset.packedState[attributeKey] = value; + } /** * @dev Tightly pack and store only non-zero overwritten terms (LifecycleTerms) @@ -177,7 +183,7 @@ library CEGEncoder { ); } - function decodeAndGetEnumValueForCEGAttribute(Asset storage asset, bytes32 attributeKey) + function decodeAndGetEnumValueForCEGTermsAttribute(Asset storage asset, bytes32 attributeKey) external view returns (uint8) @@ -203,7 +209,7 @@ library CEGEncoder { } } - function decodeAndGetAddressValueForForCEGAttribute(Asset storage asset, bytes32 attributeKey) + function decodeAndGetAddressValueForCEGTermsAttribute(Asset storage asset, bytes32 attributeKey) external view returns (address) @@ -217,7 +223,7 @@ library CEGEncoder { } } - function decodeAndGetBytes32ValueForForCEGAttribute(Asset storage asset, bytes32 attributeKey) + function decodeAndGetBytes32ValueForCEGTermsAttribute(Asset storage asset, bytes32 attributeKey) external view returns (bytes32) @@ -225,7 +231,7 @@ library CEGEncoder { return asset.packedTerms[attributeKey]; } - function decodeAndGetUIntValueForForCEGAttribute(Asset storage asset, bytes32 attributeKey) + function decodeAndGetUIntValueForCEGTermsAttribute(Asset storage asset, bytes32 attributeKey) external view returns (uint256) @@ -233,7 +239,7 @@ library CEGEncoder { return uint256(asset.packedTerms[attributeKey]); } - function decodeAndGetIntValueForForCEGAttribute(Asset storage asset, bytes32 attributeKey) + function decodeAndGetIntValueForCEGTermsAttribute(Asset storage asset, bytes32 attributeKey) external view returns (int256) @@ -241,7 +247,7 @@ library CEGEncoder { return int256(asset.packedTerms[attributeKey]); } - function decodeAndGetPeriodValueForForCEGAttribute(Asset storage asset, bytes32 attributeKey) + function decodeAndGetPeriodValueForCEGTermsAttribute(Asset storage asset, bytes32 attributeKey) external view returns (IP memory) @@ -260,7 +266,7 @@ library CEGEncoder { } } - function decodeAndGetCycleValueForForCEGAttribute(Asset storage asset, bytes32 attributeKey) + function decodeAndGetCycleValueForCEGTermsAttribute(Asset storage asset, bytes32 attributeKey) external view returns (IPS memory) @@ -279,7 +285,7 @@ library CEGEncoder { } } - function decodeAndGetContractReferenceValueForCEGAttribute(Asset storage asset , bytes32 attributeKey ) + function decodeAndGetContractReferenceValueForCEGTermsAttribute(Asset storage asset , bytes32 attributeKey ) external view returns (ContractReference memory) @@ -307,4 +313,108 @@ library CEGEncoder { ); } } + + /** + * @dev Tightly pack and store CEGState + */ + function encodeAndSetCEGState(Asset storage asset, CEGState memory state) external { + storeInPackedState(asset, "contractPerformance", bytes32(uint256(uint8(state.contractPerformance))) << 248); + storeInPackedState(asset, "statusDate", bytes32(state.statusDate)); + storeInPackedState(asset, "nonPerformingDate", bytes32(state.nonPerformingDate)); + storeInPackedState(asset, "maturityDate", bytes32(state.maturityDate)); + storeInPackedState(asset, "exerciseDate", bytes32(state.exerciseDate)); + storeInPackedState(asset, "terminationDate", bytes32(state.terminationDate)); + storeInPackedState(asset, "notionalPrincipal", bytes32(state.notionalPrincipal)); + storeInPackedState(asset, "feeAccrued", bytes32(state.feeAccrued)); + storeInPackedState(asset, "exerciseAmount", bytes32(state.exerciseAmount)); + } + + /** + * @dev Tightly pack and store finalized CEGState + */ + function encodeAndSetFinalizedCEGState(Asset storage asset, CEGState memory state) external { + storeInPackedState(asset, "F_contractPerformance", bytes32(uint256(uint8(state.contractPerformance))) << 248); + storeInPackedState(asset, "F_statusDate", bytes32(state.statusDate)); + storeInPackedState(asset, "F_nonPerformingDate", bytes32(state.nonPerformingDate)); + storeInPackedState(asset, "F_maturityDate", bytes32(state.maturityDate)); + storeInPackedState(asset, "F_exerciseDate", bytes32(state.exerciseDate)); + storeInPackedState(asset, "F_terminationDate", bytes32(state.terminationDate)); + storeInPackedState(asset, "F_notionalPrincipal", bytes32(state.notionalPrincipal)); + storeInPackedState(asset, "F_feeAccrued", bytes32(state.feeAccrued)); + storeInPackedState(asset, "F_exerciseAmount", bytes32(state.exerciseAmount)); + } + + /** + * @dev Decode and load the CEGState of the asset + */ + function decodeAndGetCEGState(Asset storage asset) + external + view + returns (CEGState memory) + { + return CEGState( + ContractPerformance(uint8(uint256(asset.packedState["contractPerformance"] >> 248))), + uint256(asset.packedState["statusDate"]), + uint256(asset.packedState["nonPerformingDate"]), + uint256(asset.packedState["maturityDate"]), + uint256(asset.packedState["exerciseDate"]), + uint256(asset.packedState["terminationDate"]), + + int256(asset.packedState["notionalPrincipal"]), + int256(asset.packedState["feeAccrued"]), + int256(asset.packedState["exerciseAmomunt"]) + ); + } + + /** + * @dev Decode and load the finalized CEGState of the asset + */ + function decodeAndGetFinalizedCEGState(Asset storage asset) + external + view + returns (CEGState memory) + { + return CEGState( + ContractPerformance(uint8(uint256(asset.packedState["F_contractPerformance"] >> 248))), + uint256(asset.packedState["F_statusDate"]), + uint256(asset.packedState["F_nonPerformingDate"]), + uint256(asset.packedState["F_maturityDate"]), + uint256(asset.packedState["F_exerciseDate"]), + uint256(asset.packedState["F_terminationDate"]), + + int256(asset.packedState["F_notionalPrincipal"]), + int256(asset.packedState["F_feeAccrued"]), + int256(asset.packedState["F_exerciseAmomunt"]) + ); + } + + function decodeAndGetEnumValueForCEGStateAttribute(Asset storage asset, bytes32 attributeKey) + external + view + returns (uint8) + { + if (attributeKey == bytes32("contractPerformance")) { + return uint8(uint256(asset.packedState["contractPerformance"] >> 248)); + } else if (attributeKey == bytes32("F_contractPerformance")) { + return uint8(uint256(asset.packedState["F_contractPerformance"] >> 248)); + } else { + return uint8(0); + } + } + + function decodeAndGetUIntValueForCEGStateAttribute(Asset storage asset, bytes32 attributeKey) + external + view + returns (uint256) + { + return uint256(asset.packedState[attributeKey]); + } + + function decodeAndGetIntValueForCEGStateAttribute(Asset storage asset, bytes32 attributeKey) + external + view + returns (int256) + { + return int256(asset.packedState[attributeKey]); + } } \ No newline at end of file diff --git a/packages/ap-contracts/contracts/Core/CEG/CEGRegistry.sol b/packages/ap-contracts/contracts/Core/CEG/CEGRegistry.sol index feda5c91..5bbf64a0 100644 --- a/packages/ap-contracts/contracts/Core/CEG/CEGRegistry.sol +++ b/packages/ap-contracts/contracts/Core/CEG/CEGRegistry.sol @@ -38,7 +38,7 @@ contract CEGRegistry is BaseRegistry, ICEGRegistry { function registerAsset( bytes32 assetId, CEGTerms calldata terms, - State calldata state, + CEGState calldata state, bytes32[] calldata schedule, AssetOwnership calldata ownership, address engine, @@ -49,8 +49,10 @@ contract CEGRegistry is BaseRegistry, ICEGRegistry { override onlyApprovedActors { - setAsset(assetId, state, schedule, ownership, engine, actor, admin); + setAsset(assetId, schedule, ownership, engine, actor, admin); assets[assetId].encodeAndSetCEGTerms(terms); + assets[assetId].encodeAndSetCEGState(state); + assets[assetId].encodeAndSetFinalizedCEGState(state); } /** @@ -88,7 +90,7 @@ contract CEGRegistry is BaseRegistry, ICEGRegistry { override(ITermsRegistry, TermsRegistry) returns (uint8) { - return assets[assetId].decodeAndGetEnumValueForCEGAttribute(attribute); + return assets[assetId].decodeAndGetEnumValueForCEGTermsAttribute(attribute); } function getAddressValueForTermsAttribute(bytes32 assetId, bytes32 attribute) @@ -97,7 +99,7 @@ contract CEGRegistry is BaseRegistry, ICEGRegistry { override(ITermsRegistry, TermsRegistry) returns (address) { - return assets[assetId].decodeAndGetAddressValueForForCEGAttribute(attribute); + return assets[assetId].decodeAndGetAddressValueForCEGTermsAttribute(attribute); } function getBytes32ValueForTermsAttribute(bytes32 assetId, bytes32 attribute) @@ -106,7 +108,7 @@ contract CEGRegistry is BaseRegistry, ICEGRegistry { override(ITermsRegistry, TermsRegistry) returns (bytes32) { - return assets[assetId].decodeAndGetBytes32ValueForForCEGAttribute(attribute); + return assets[assetId].decodeAndGetBytes32ValueForCEGTermsAttribute(attribute); } function getUIntValueForTermsAttribute(bytes32 assetId, bytes32 attribute) @@ -115,7 +117,7 @@ contract CEGRegistry is BaseRegistry, ICEGRegistry { override(ITermsRegistry, TermsRegistry) returns (uint256) { - return assets[assetId].decodeAndGetUIntValueForForCEGAttribute(attribute); + return assets[assetId].decodeAndGetUIntValueForCEGTermsAttribute(attribute); } function getIntValueForTermsAttribute(bytes32 assetId, bytes32 attribute) @@ -124,7 +126,7 @@ contract CEGRegistry is BaseRegistry, ICEGRegistry { override(ITermsRegistry, TermsRegistry) returns (int256) { - return assets[assetId].decodeAndGetIntValueForForCEGAttribute(attribute); + return assets[assetId].decodeAndGetIntValueForCEGTermsAttribute(attribute); } function getPeriodValueForTermsAttribute(bytes32 assetId, bytes32 attribute) @@ -133,7 +135,7 @@ contract CEGRegistry is BaseRegistry, ICEGRegistry { override(ITermsRegistry, TermsRegistry) returns (IP memory) { - return assets[assetId].decodeAndGetPeriodValueForForCEGAttribute(attribute); + return assets[assetId].decodeAndGetPeriodValueForCEGTermsAttribute(attribute); } function getCycleValueForTermsAttribute(bytes32 assetId, bytes32 attribute) @@ -142,7 +144,7 @@ contract CEGRegistry is BaseRegistry, ICEGRegistry { override(ITermsRegistry, TermsRegistry) returns (IPS memory) { - return assets[assetId].decodeAndGetCycleValueForForCEGAttribute(attribute); + return assets[assetId].decodeAndGetCycleValueForCEGTermsAttribute(attribute); } function getContractReferenceValueForTermsAttribute(bytes32 assetId, bytes32 attribute) @@ -151,7 +153,92 @@ contract CEGRegistry is BaseRegistry, ICEGRegistry { override(ITermsRegistry, TermsRegistry) returns (ContractReference memory) { - return assets[assetId].decodeAndGetContractReferenceValueForCEGAttribute(attribute); + return assets[assetId].decodeAndGetContractReferenceValueForCEGTermsAttribute(attribute); + } + + /** + * @notice Returns the state of an asset. + * @param assetId id of the asset + * @return state of the asset + */ + function getState(bytes32 assetId) + external + view + override + returns (CEGState memory) + { + return assets[assetId].decodeAndGetCEGState(); + } + + /** + * @notice Returns the state of an asset. + * @param assetId id of the asset + * @return state of the asset + */ + function getFinalizedState(bytes32 assetId) + external + view + override + returns (CEGState memory) + { + return assets[assetId].decodeAndGetFinalizedCEGState(); + } + + /** + * @notice Sets next state of an asset. + * @dev Can only be updated by the assets actor or by an authorized account. + * @param assetId id of the asset + * @param state next state of the asset + */ + function setState(bytes32 assetId, CEGState calldata state) + external + override + isAuthorized (assetId) + { + assets[assetId].encodeAndSetCEGState(state); + emit UpdatedState(assetId, state.statusDate); + } + + /** + * @notice Sets next finalized state of an asset. + * @dev Can only be updated by the assets actor or by an authorized account. + * @param assetId id of the asset + * @param state next state of the asset + */ + function setFinalizedState(bytes32 assetId, CEGState calldata state) + external + override + isAuthorized (assetId) + { + assets[assetId].encodeAndSetFinalizedCEGState(state); + emit UpdatedFinalizedState(assetId, state.statusDate); + } + + function getEnumValueForStateAttribute(bytes32 assetId, bytes32 attribute) + public + view + override(IStateRegistry, StateRegistry) + returns (uint8) + { + return assets[assetId].decodeAndGetEnumValueForCEGStateAttribute(attribute); + } + + function getIntValueForStateAttribute(bytes32 assetId, bytes32 attribute) + public + view + override(IStateRegistry, StateRegistry) + returns (int256) + { + return assets[assetId].decodeAndGetIntValueForCEGStateAttribute(attribute); + } + + function getUintValueForStateAttribute(bytes32 assetId, bytes32 attribute) + public + view + override(IStateRegistry, StateRegistry) + returns (uint256) + { + return assets[assetId].decodeAndGetUIntValueForCEGStateAttribute(attribute); } function getNextCyclicEvent(bytes32 assetId) diff --git a/packages/ap-contracts/contracts/Core/CEG/ICEGRegistry.sol b/packages/ap-contracts/contracts/Core/CEG/ICEGRegistry.sol index df3803d7..363de54b 100644 --- a/packages/ap-contracts/contracts/Core/CEG/ICEGRegistry.sol +++ b/packages/ap-contracts/contracts/Core/CEG/ICEGRegistry.sol @@ -11,7 +11,7 @@ interface ICEGRegistry is IAssetRegistry { function registerAsset( bytes32 assetId, CEGTerms calldata terms, - State calldata state, + CEGState calldata state, bytes32[] calldata schedule, AssetOwnership calldata ownership, address engine, @@ -27,4 +27,20 @@ interface ICEGRegistry is IAssetRegistry { function setTerms(bytes32 assetId, CEGTerms calldata terms) external; + + function getState(bytes32 assetId) + external + view + returns (CEGState memory); + + function setState(bytes32 assetId, CEGState calldata terms) + external; + + function getFinalizedState(bytes32 assetId) + external + view + returns (CEGState memory); + + function setFinalizedState(bytes32 assetId, CEGState calldata terms) + external; } diff --git a/packages/ap-contracts/contracts/Core/CERTF/CERTFActor.sol b/packages/ap-contracts/contracts/Core/CERTF/CERTFActor.sol index db73b3d3..ec6d5cd7 100644 --- a/packages/ap-contracts/contracts/Core/CERTF/CERTFActor.sol +++ b/packages/ap-contracts/contracts/Core/CERTF/CERTFActor.sol @@ -46,7 +46,7 @@ contract CERTFActor is BaseActor { bytes32 assetId = keccak256(abi.encode(terms, block.timestamp)); // compute the initial state of the asset - State memory initialState = ICERTFEngine(engine).computeInitialState(terms); + CERTFState memory initialState = ICERTFEngine(engine).computeInitialState(terms); // register the asset in the AssetRegistry ICERTFRegistry(address(assetRegistry)).registerAsset( @@ -63,16 +63,28 @@ contract CERTFActor is BaseActor { emit InitializedAsset(assetId, ContractType.CEG, ownership.creatorObligor, ownership.counterpartyObligor); } - function computeStateAndPayoffForEvent(bytes32 assetId, State memory state, bytes32 _event) + /** + * @notice Contract-type specific logic for processing an event required by the use of + * contract-type specific Terms and State. + */ + function settleEventAndUpdateState(bytes32 assetId, bytes32 _event) internal - view override - returns (State memory, int256) + returns (bool, int256) { - address engine = assetRegistry.getEngine(assetId); CERTFTerms memory terms = ICERTFRegistry(address(assetRegistry)).getTerms(assetId); + CERTFState memory state = ICERTFRegistry(address(assetRegistry)).getState(assetId); + address engine = assetRegistry.getEngine(assetId); + + // get finalized state if asset is not performant + if (state.contractPerformance != ContractPerformance.PF) { + state = ICERTFRegistry(address(assetRegistry)).getFinalizedState(assetId); + } + (EventType eventType, uint256 scheduleTime) = decodeEvent(_event); + // get external data for the next event + // compute payoff and the next state by applying the event to the current state int256 payoff = ICERTFEngine(engine).computePayoffForEvent( terms, state, @@ -83,7 +95,7 @@ contract CERTFActor is BaseActor { shiftCalcTime(scheduleTime, terms.businessDayConvention, terms.calendar, terms.maturityDate) ) ); - state = ICERTFEngine(engine).computeStateForEvent( + CERTFState memory nextState = ICERTFEngine(engine).computeStateForEvent( terms, state, _event, @@ -94,6 +106,40 @@ contract CERTFActor is BaseActor { ) ); - return (state, payoff); + // try to settle payoff of event + bool settledPayoff = settlePayoffForEvent(assetId, _event, payoff); + + if (settledPayoff == false) { + // if the obligation can't be fulfilled and the performance changed from performant to DL, DQ or DF, + // store the last performant state of the asset + // (if the obligation is later fulfilled before the asset reaches default, + // the last performant state is used to derive subsequent states of the asset) + if (state.contractPerformance == ContractPerformance.PF) { + ICERTFRegistry(address(assetRegistry)).setFinalizedState(assetId, state); + } + + // store event as pending event for future settlement + assetRegistry.pushPendingEvent(assetId, _event); + + // create CreditEvent + bytes32 ceEvent = encodeEvent(EventType.CE, scheduleTime); + + // derive the actual state of the asset by applying the CreditEvent (updates performance of asset) + nextState = ICERTFEngine(engine).computeStateForEvent( + terms, + state, + ceEvent, + getExternalDataForSTF( + assetId, + EventType.CE, + shiftCalcTime(scheduleTime, terms.businessDayConvention, terms.calendar, terms.maturityDate) + ) + ); + } + + // store the resulting state + ICERTFRegistry(address(assetRegistry)).setState(assetId, nextState); + + return (settledPayoff, payoff); } } \ No newline at end of file diff --git a/packages/ap-contracts/contracts/Core/CERTF/CERTFEncoder.sol b/packages/ap-contracts/contracts/Core/CERTF/CERTFEncoder.sol index e03c013b..51560761 100644 --- a/packages/ap-contracts/contracts/Core/CERTF/CERTFEncoder.sol +++ b/packages/ap-contracts/contracts/Core/CERTF/CERTFEncoder.sol @@ -13,6 +13,12 @@ library CERTFEncoder { if (asset.packedTerms[attributeKey] == value) return; asset.packedTerms[attributeKey] = value; } + + function storeInPackedState(Asset storage asset, bytes32 attributeKey, bytes32 value) private { + // skip if value did not change + if (asset.packedState[attributeKey] == value) return; + asset.packedState[attributeKey] = value; + } /** * @dev Tightly pack and store only non-zero overwritten terms (LifecycleTerms) @@ -235,7 +241,7 @@ library CERTFEncoder { ); } - function decodeAndGetEnumValueForCERTFAttribute(Asset storage asset, bytes32 attributeKey) + function decodeAndGetEnumValueForCERTFTermsAttribute(Asset storage asset, bytes32 attributeKey) external view returns (uint8) @@ -259,7 +265,7 @@ library CERTFEncoder { } } - function decodeAndGetAddressValueForForCERTFAttribute(Asset storage asset, bytes32 attributeKey) + function decodeAndGetAddressValueForCERTFTermsAttribute(Asset storage asset, bytes32 attributeKey) external view returns (address) @@ -273,7 +279,7 @@ library CERTFEncoder { } } - function decodeAndGetBytes32ValueForForCERTFAttribute(Asset storage asset, bytes32 attributeKey) + function decodeAndGetBytes32ValueForCERTFTermsAttribute(Asset storage asset, bytes32 attributeKey) external view returns (bytes32) @@ -281,7 +287,7 @@ library CERTFEncoder { return asset.packedTerms[attributeKey]; } - function decodeAndGetUIntValueForForCERTFAttribute(Asset storage asset, bytes32 attributeKey) + function decodeAndGetUIntValueForCERTFTermsAttribute(Asset storage asset, bytes32 attributeKey) external view returns (uint256) @@ -289,7 +295,7 @@ library CERTFEncoder { return uint256(asset.packedTerms[attributeKey]); } - function decodeAndGetIntValueForForCERTFAttribute(Asset storage asset, bytes32 attributeKey) + function decodeAndGetIntValueForCERTFTermsAttribute(Asset storage asset, bytes32 attributeKey) external view returns (int256) @@ -297,7 +303,7 @@ library CERTFEncoder { return int256(asset.packedTerms[attributeKey]); } - function decodeAndGetPeriodValueForForCERTFAttribute(Asset storage asset, bytes32 attributeKey) + function decodeAndGetPeriodValueForCERTFTermsAttribute(Asset storage asset, bytes32 attributeKey) external view returns (IP memory) @@ -319,7 +325,7 @@ library CERTFEncoder { } } - function decodeAndGetCycleValueForForCERTFAttribute(Asset storage asset, bytes32 attributeKey) + function decodeAndGetCycleValueForCERTFTermsAttribute(Asset storage asset, bytes32 attributeKey) external view returns (IPS memory) @@ -340,7 +346,7 @@ library CERTFEncoder { } } - function decodeAndGetContractReferenceValueForCERTFAttribute(Asset storage asset, bytes32 attributeKey) + function decodeAndGetContractReferenceValueForCERTFTermsAttribute(Asset storage asset, bytes32 attributeKey) external view returns (ContractReference memory) @@ -368,4 +374,122 @@ library CERTFEncoder { ); } } + + /** + * @dev Tightly pack and store CERTFState + */ + function encodeAndSetCERTFState(Asset storage asset, CERTFState memory state) external { + storeInPackedState(asset, "contractPerformance", bytes32(uint256(uint8(state.contractPerformance))) << 248); + storeInPackedState(asset, "statusDate", bytes32(state.statusDate)); + storeInPackedState(asset, "nonPerformingDate", bytes32(state.nonPerformingDate)); + storeInPackedState(asset, "maturityDate", bytes32(state.maturityDate)); + storeInPackedState(asset, "exerciseDate", bytes32(state.exerciseDate)); + storeInPackedState(asset, "terminationDate", bytes32(state.terminationDate)); + storeInPackedState(asset, "lastCouponDay", bytes32(state.lastCouponDay)); + storeInPackedState(asset, "exerciseAmount", bytes32(state.exerciseAmount)); + storeInPackedState(asset, "exerciseQuantity", bytes32(state.exerciseQuantity)); + storeInPackedState(asset, "quantity", bytes32(state.quantity)); + storeInPackedState(asset, "couponAmountFixed", bytes32(state.couponAmountFixed)); + storeInPackedState(asset, "marginFactor", bytes32(state.marginFactor)); + storeInPackedState(asset, "adjustmentFactor", bytes32(state.adjustmentFactor)); + } + + /** + * @dev Tightly pack and store finalized CERTFState + */ + function encodeAndSetFinalizedCERTFState(Asset storage asset, CERTFState memory state) external { + storeInPackedState(asset, "F_contractPerformance", bytes32(uint256(uint8(state.contractPerformance))) << 248); + storeInPackedState(asset, "F_statusDate", bytes32(state.statusDate)); + storeInPackedState(asset, "F_nonPerformingDate", bytes32(state.nonPerformingDate)); + storeInPackedState(asset, "F_maturityDate", bytes32(state.maturityDate)); + storeInPackedState(asset, "F_exerciseDate", bytes32(state.exerciseDate)); + storeInPackedState(asset, "F_terminationDate", bytes32(state.terminationDate)); + storeInPackedState(asset, "F_lastCouponDay", bytes32(state.lastCouponDay)); + storeInPackedState(asset, "F_exerciseAmount", bytes32(state.exerciseAmount)); + storeInPackedState(asset, "F_exerciseQuantity", bytes32(state.exerciseQuantity)); + storeInPackedState(asset, "F_quantity", bytes32(state.quantity)); + storeInPackedState(asset, "F_couponAmountFixed", bytes32(state.couponAmountFixed)); + storeInPackedState(asset, "F_marginFactor", bytes32(state.marginFactor)); + storeInPackedState(asset, "F_adjustmentFactor", bytes32(state.adjustmentFactor)); + } + + /** + * @dev Decode and load the CERTFState of the asset + */ + function decodeAndGetCERTFState(Asset storage asset) + external + view + returns (CERTFState memory) + { + return CERTFState( + ContractPerformance(uint8(uint256(asset.packedState["contractPerformance"] >> 248))), + uint256(asset.packedState["statusDate"]), + uint256(asset.packedState["nonPerformingDate"]), + uint256(asset.packedState["maturityDate"]), + uint256(asset.packedState["exerciseDate"]), + uint256(asset.packedState["terminationDate"]), + uint256(asset.packedState["lastCouponDate"]), + int256(asset.packedState["exerciseAmomunt"]), + int256(asset.packedState["exerciseQuantity"]), + int256(asset.packedState["quantity"]), + int256(asset.packedState["couponAmountFixed"]), + int256(asset.packedState["marginFactor"]), + int256(asset.packedState["adjustmentFactor"]) + ); + } + + /** + * @dev Decode and load the finalized CERTFState of the asset + */ + function decodeAndGetFinalizedCERTFState(Asset storage asset) + external + view + returns (CERTFState memory) + { + return CERTFState( + ContractPerformance(uint8(uint256(asset.packedState["F_contractPerformance"] >> 248))), + uint256(asset.packedState["F_statusDate"]), + uint256(asset.packedState["F_nonPerformingDate"]), + uint256(asset.packedState["F_maturityDate"]), + uint256(asset.packedState["F_exerciseDate"]), + uint256(asset.packedState["F_terminationDate"]), + uint256(asset.packedState["F_lastCouponDate"]), + int256(asset.packedState["F_exerciseAmomunt"]), + int256(asset.packedState["F_exerciseQuantity"]), + int256(asset.packedState["F_quantity"]), + int256(asset.packedState["F_couponAmountFixed"]), + int256(asset.packedState["F_marginFactor"]), + int256(asset.packedState["F_adjustmentFactor"]) + ); + } + + function decodeAndGetEnumValueForCERTFStateAttribute(Asset storage asset, bytes32 attributeKey) + external + view + returns (uint8) + { + if (attributeKey == bytes32("contractPerformance")) { + return uint8(uint256(asset.packedState["contractPerformance"] >> 248)); + } else if (attributeKey == bytes32("F_contractPerformance")) { + return uint8(uint256(asset.packedState["F_contractPerformance"] >> 248)); + } else { + return uint8(0); + } + } + + function decodeAndGetUIntValueForCERTFStateAttribute(Asset storage asset, bytes32 attributeKey) + external + view + returns (uint256) + { + return uint256(asset.packedState[attributeKey]); + } + + function decodeAndGetIntValueForCERTFStateAttribute(Asset storage asset, bytes32 attributeKey) + external + view + returns (int256) + { + return int256(asset.packedState[attributeKey]); + } } \ No newline at end of file diff --git a/packages/ap-contracts/contracts/Core/CERTF/CERTFRegistry.sol b/packages/ap-contracts/contracts/Core/CERTF/CERTFRegistry.sol index d50ca8c4..1203aa79 100644 --- a/packages/ap-contracts/contracts/Core/CERTF/CERTFRegistry.sol +++ b/packages/ap-contracts/contracts/Core/CERTF/CERTFRegistry.sol @@ -38,7 +38,7 @@ contract CERTFRegistry is BaseRegistry, ICERTFRegistry { function registerAsset( bytes32 assetId, CERTFTerms calldata terms, - State calldata state, + CERTFState calldata state, bytes32[] calldata schedule, AssetOwnership calldata ownership, address engine, @@ -49,8 +49,10 @@ contract CERTFRegistry is BaseRegistry, ICERTFRegistry { override onlyApprovedActors { - setAsset(assetId, state, schedule, ownership, engine, actor, admin); + setAsset(assetId, schedule, ownership, engine, actor, admin); assets[assetId].encodeAndSetCERTFTerms(terms); + assets[assetId].encodeAndSetCERTFState(state); + assets[assetId].encodeAndSetFinalizedCERTFState(state); } /** @@ -88,7 +90,7 @@ contract CERTFRegistry is BaseRegistry, ICERTFRegistry { override(ITermsRegistry, TermsRegistry) returns (uint8) { - return assets[assetId].decodeAndGetEnumValueForCERTFAttribute(attribute); + return assets[assetId].decodeAndGetEnumValueForCERTFTermsAttribute(attribute); } function getAddressValueForTermsAttribute(bytes32 assetId, bytes32 attribute) @@ -97,7 +99,7 @@ contract CERTFRegistry is BaseRegistry, ICERTFRegistry { override(ITermsRegistry, TermsRegistry) returns (address) { - return assets[assetId].decodeAndGetAddressValueForForCERTFAttribute(attribute); + return assets[assetId].decodeAndGetAddressValueForCERTFTermsAttribute(attribute); } function getBytes32ValueForTermsAttribute(bytes32 assetId, bytes32 attribute) @@ -106,7 +108,7 @@ contract CERTFRegistry is BaseRegistry, ICERTFRegistry { override(ITermsRegistry, TermsRegistry) returns (bytes32) { - return assets[assetId].decodeAndGetBytes32ValueForForCERTFAttribute(attribute); + return assets[assetId].decodeAndGetBytes32ValueForCERTFTermsAttribute(attribute); } function getUIntValueForTermsAttribute(bytes32 assetId, bytes32 attribute) @@ -115,7 +117,7 @@ contract CERTFRegistry is BaseRegistry, ICERTFRegistry { override(ITermsRegistry, TermsRegistry) returns (uint256) { - return assets[assetId].decodeAndGetUIntValueForForCERTFAttribute(attribute); + return assets[assetId].decodeAndGetUIntValueForCERTFTermsAttribute(attribute); } function getIntValueForTermsAttribute(bytes32 assetId, bytes32 attribute) @@ -124,7 +126,7 @@ contract CERTFRegistry is BaseRegistry, ICERTFRegistry { override(ITermsRegistry, TermsRegistry) returns (int256) { - return assets[assetId].decodeAndGetIntValueForForCERTFAttribute(attribute); + return assets[assetId].decodeAndGetIntValueForCERTFTermsAttribute(attribute); } function getPeriodValueForTermsAttribute(bytes32 assetId, bytes32 attribute) @@ -133,7 +135,7 @@ contract CERTFRegistry is BaseRegistry, ICERTFRegistry { override(ITermsRegistry, TermsRegistry) returns (IP memory) { - return assets[assetId].decodeAndGetPeriodValueForForCERTFAttribute(attribute); + return assets[assetId].decodeAndGetPeriodValueForCERTFTermsAttribute(attribute); } function getCycleValueForTermsAttribute(bytes32 assetId, bytes32 attribute) @@ -142,7 +144,7 @@ contract CERTFRegistry is BaseRegistry, ICERTFRegistry { override(ITermsRegistry, TermsRegistry) returns (IPS memory) { - return assets[assetId].decodeAndGetCycleValueForForCERTFAttribute(attribute); + return assets[assetId].decodeAndGetCycleValueForCERTFTermsAttribute(attribute); } function getContractReferenceValueForTermsAttribute(bytes32 assetId, bytes32 attribute) @@ -151,7 +153,92 @@ contract CERTFRegistry is BaseRegistry, ICERTFRegistry { override(ITermsRegistry, TermsRegistry) returns (ContractReference memory) { - return assets[assetId].decodeAndGetContractReferenceValueForCERTFAttribute(attribute); + return assets[assetId].decodeAndGetContractReferenceValueForCERTFTermsAttribute(attribute); + } + + /** + * @notice Returns the state of an asset. + * @param assetId id of the asset + * @return state of the asset + */ + function getState(bytes32 assetId) + external + view + override + returns (CERTFState memory) + { + return assets[assetId].decodeAndGetCERTFState(); + } + + /** + * @notice Returns the state of an asset. + * @param assetId id of the asset + * @return state of the asset + */ + function getFinalizedState(bytes32 assetId) + external + view + override + returns (CERTFState memory) + { + return assets[assetId].decodeAndGetFinalizedCERTFState(); + } + + /** + * @notice Sets next state of an asset. + * @dev Can only be updated by the assets actor or by an authorized account. + * @param assetId id of the asset + * @param state next state of the asset + */ + function setState(bytes32 assetId, CERTFState calldata state) + external + override + isAuthorized (assetId) + { + assets[assetId].encodeAndSetCERTFState(state); + emit UpdatedState(assetId, state.statusDate); + } + + /** + * @notice Sets next finalized state of an asset. + * @dev Can only be updated by the assets actor or by an authorized account. + * @param assetId id of the asset + * @param state next state of the asset + */ + function setFinalizedState(bytes32 assetId, CERTFState calldata state) + external + override + isAuthorized (assetId) + { + assets[assetId].encodeAndSetFinalizedCERTFState(state); + emit UpdatedFinalizedState(assetId, state.statusDate); + } + + function getEnumValueForStateAttribute(bytes32 assetId, bytes32 attribute) + public + view + override(IStateRegistry, StateRegistry) + returns (uint8) + { + return assets[assetId].decodeAndGetEnumValueForCERTFStateAttribute(attribute); + } + + function getIntValueForStateAttribute(bytes32 assetId, bytes32 attribute) + public + view + override(IStateRegistry, StateRegistry) + returns (int256) + { + return assets[assetId].decodeAndGetIntValueForCERTFStateAttribute(attribute); + } + + function getUintValueForStateAttribute(bytes32 assetId, bytes32 attribute) + public + view + override(IStateRegistry, StateRegistry) + returns (uint256) + { + return assets[assetId].decodeAndGetUIntValueForCERTFStateAttribute(attribute); } function getNextCyclicEvent(bytes32 assetId) diff --git a/packages/ap-contracts/contracts/Core/CERTF/ICERTFRegistry.sol b/packages/ap-contracts/contracts/Core/CERTF/ICERTFRegistry.sol index 0eec7ad6..54da7bb0 100644 --- a/packages/ap-contracts/contracts/Core/CERTF/ICERTFRegistry.sol +++ b/packages/ap-contracts/contracts/Core/CERTF/ICERTFRegistry.sol @@ -11,7 +11,7 @@ interface ICERTFRegistry is IAssetRegistry { function registerAsset( bytes32 assetId, CERTFTerms calldata terms, - State calldata state, + CERTFState calldata state, bytes32[] calldata schedule, AssetOwnership calldata ownership, address engine, @@ -27,4 +27,20 @@ interface ICERTFRegistry is IAssetRegistry { function setTerms(bytes32 assetId, CERTFTerms calldata terms) external; + + function getState(bytes32 assetId) + external + view + returns (CERTFState memory); + + function setState(bytes32 assetId, CERTFState calldata terms) + external; + + function getFinalizedState(bytes32 assetId) + external + view + returns (CERTFState memory); + + function setFinalizedState(bytes32 assetId, CERTFState calldata terms) + external; } diff --git a/packages/ap-contracts/contracts/Core/PAM/IPAMRegistry.sol b/packages/ap-contracts/contracts/Core/PAM/IPAMRegistry.sol index 4e6ed98d..bc45c35b 100644 --- a/packages/ap-contracts/contracts/Core/PAM/IPAMRegistry.sol +++ b/packages/ap-contracts/contracts/Core/PAM/IPAMRegistry.sol @@ -11,7 +11,7 @@ interface IPAMRegistry is IAssetRegistry { function registerAsset( bytes32 assetId, PAMTerms calldata terms, - State calldata state, + PAMState calldata state, bytes32[] calldata schedule, AssetOwnership calldata ownership, address engine, @@ -27,4 +27,20 @@ interface IPAMRegistry is IAssetRegistry { function setTerms(bytes32 assetId, PAMTerms calldata terms) external; + + function getState(bytes32 assetId) + external + view + returns (PAMState memory); + + function setState(bytes32 assetId, PAMState calldata terms) + external; + + function getFinalizedState(bytes32 assetId) + external + view + returns (PAMState memory); + + function setFinalizedState(bytes32 assetId, PAMState calldata terms) + external; } diff --git a/packages/ap-contracts/contracts/Core/PAM/PAMActor.sol b/packages/ap-contracts/contracts/Core/PAM/PAMActor.sol index e63f6039..0b77b8d6 100644 --- a/packages/ap-contracts/contracts/Core/PAM/PAMActor.sol +++ b/packages/ap-contracts/contracts/Core/PAM/PAMActor.sol @@ -46,7 +46,7 @@ contract PAMActor is BaseActor { bytes32 assetId = keccak256(abi.encode(terms, block.timestamp)); // compute the initial state of the asset - State memory initialState = IPAMEngine(engine).computeInitialState(terms); + PAMState memory initialState = IPAMEngine(engine).computeInitialState(terms); // register the asset in the AssetRegistry IPAMRegistry(address(assetRegistry)).registerAsset( @@ -63,16 +63,28 @@ contract PAMActor is BaseActor { emit InitializedAsset(assetId, ContractType.PAM, ownership.creatorObligor, ownership.counterpartyObligor); } - function computeStateAndPayoffForEvent(bytes32 assetId, State memory state, bytes32 _event) + /** + * @notice Contract-type specific logic for processing an event required by the use of + * contract-type specific Terms and State. + */ + function settleEventAndUpdateState(bytes32 assetId, bytes32 _event) internal - view override - returns (State memory, int256) + returns (bool, int256) { - address engine = assetRegistry.getEngine(assetId); PAMTerms memory terms = IPAMRegistry(address(assetRegistry)).getTerms(assetId); + PAMState memory state = IPAMRegistry(address(assetRegistry)).getState(assetId); + address engine = assetRegistry.getEngine(assetId); + + // get finalized state if asset is not performant + if (state.contractPerformance != ContractPerformance.PF) { + state = IPAMRegistry(address(assetRegistry)).getFinalizedState(assetId); + } + (EventType eventType, uint256 scheduleTime) = decodeEvent(_event); + // get external data for the next event + // compute payoff and the next state by applying the event to the current state int256 payoff = IPAMEngine(engine).computePayoffForEvent( terms, state, @@ -83,7 +95,7 @@ contract PAMActor is BaseActor { shiftCalcTime(scheduleTime, terms.businessDayConvention, terms.calendar, terms.maturityDate) ) ); - state = IPAMEngine(engine).computeStateForEvent( + PAMState memory nextState = IPAMEngine(engine).computeStateForEvent( terms, state, _event, @@ -94,6 +106,40 @@ contract PAMActor is BaseActor { ) ); - return (state, payoff); + // try to settle payoff of event + bool settledPayoff = settlePayoffForEvent(assetId, _event, payoff); + + if (settledPayoff == false) { + // if the obligation can't be fulfilled and the performance changed from performant to DL, DQ or DF, + // store the last performant state of the asset + // (if the obligation is later fulfilled before the asset reaches default, + // the last performant state is used to derive subsequent states of the asset) + if (state.contractPerformance == ContractPerformance.PF) { + IPAMRegistry(address(assetRegistry)).setFinalizedState(assetId, state); + } + + // store event as pending event for future settlement + assetRegistry.pushPendingEvent(assetId, _event); + + // create CreditEvent + bytes32 ceEvent = encodeEvent(EventType.CE, scheduleTime); + + // derive the actual state of the asset by applying the CreditEvent (updates performance of asset) + nextState = IPAMEngine(engine).computeStateForEvent( + terms, + state, + ceEvent, + getExternalDataForSTF( + assetId, + EventType.CE, + shiftCalcTime(scheduleTime, terms.businessDayConvention, terms.calendar, terms.maturityDate) + ) + ); + } + + // store the resulting state + IPAMRegistry(address(assetRegistry)).setState(assetId, nextState); + + return (settledPayoff, payoff); } } \ No newline at end of file diff --git a/packages/ap-contracts/contracts/Core/PAM/PAMEncoder.sol b/packages/ap-contracts/contracts/Core/PAM/PAMEncoder.sol index b7fb0b3a..c7ebfaec 100644 --- a/packages/ap-contracts/contracts/Core/PAM/PAMEncoder.sol +++ b/packages/ap-contracts/contracts/Core/PAM/PAMEncoder.sol @@ -13,6 +13,12 @@ library PAMEncoder { if (asset.packedTerms[attributeKey] == value) return; asset.packedTerms[attributeKey] = value; } + + function storeInPackedState(Asset storage asset, bytes32 attributeKey, bytes32 value) private { + // skip if value did not change + if (asset.packedState[attributeKey] == value) return; + asset.packedState[attributeKey] = value; + } /** * @dev Tightly pack and store only non-zero overwritten terms (LifecycleTerms) @@ -202,7 +208,7 @@ library PAMEncoder { ); } - function decodeAndGetEnumValueForPAMAttribute(Asset storage asset, bytes32 attributeKey) + function decodeAndGetEnumValueForPAMTermsAttribute(Asset storage asset, bytes32 attributeKey) external view returns (uint8) @@ -230,7 +236,7 @@ library PAMEncoder { } } - function decodeAndGetAddressValueForForPAMAttribute(Asset storage asset, bytes32 attributeKey) + function decodeAndGetAddressValueForPAMTermsAttribute(Asset storage asset, bytes32 attributeKey) external view returns (address) @@ -244,7 +250,7 @@ library PAMEncoder { } } - function decodeAndGetBytes32ValueForForPAMAttribute(Asset storage asset, bytes32 attributeKey) + function decodeAndGetBytes32ValueForPAMTermsAttribute(Asset storage asset, bytes32 attributeKey) external view returns (bytes32) @@ -252,7 +258,7 @@ library PAMEncoder { return asset.packedTerms[attributeKey]; } - function decodeAndGetUIntValueForForPAMAttribute(Asset storage asset, bytes32 attributeKey) + function decodeAndGetUIntValueForPAMTermsAttribute(Asset storage asset, bytes32 attributeKey) external view returns (uint256) @@ -260,7 +266,7 @@ library PAMEncoder { return uint256(asset.packedTerms[attributeKey]); } - function decodeAndGetIntValueForForPAMAttribute(Asset storage asset, bytes32 attributeKey) + function decodeAndGetIntValueForPAMTermsAttribute(Asset storage asset, bytes32 attributeKey) external view returns (int256) @@ -268,7 +274,7 @@ library PAMEncoder { return int256(asset.packedTerms[attributeKey]); } - function decodeAndGetPeriodValueForForPAMAttribute(Asset storage asset, bytes32 attributeKey) + function decodeAndGetPeriodValueForPAMTermsAttribute(Asset storage asset, bytes32 attributeKey) external view returns (IP memory) @@ -287,7 +293,7 @@ library PAMEncoder { } } - function decodeAndGetCycleValueForForPAMAttribute(Asset storage asset, bytes32 attributeKey) + function decodeAndGetCycleValueForPAMTermsAttribute(Asset storage asset, bytes32 attributeKey) external view returns (IPS memory) @@ -309,7 +315,7 @@ library PAMEncoder { } } - function decodeAndGetContractReferenceValueForPAMAttribute(Asset storage /* asset */, bytes32 /* attributeKey */) + function decodeAndGetContractReferenceValueForPAMTermsAttribute(Asset storage /* asset */, bytes32 /* attributeKey */) external pure returns (ContractReference memory) @@ -321,4 +327,116 @@ library PAMEncoder { ContractReferenceRole(0) ); } + + /** + * @dev Tightly pack and store PAMState + */ + function encodeAndSetPAMState(Asset storage asset, PAMState memory state) external { + storeInPackedState(asset, "contractPerformance", bytes32(uint256(uint8(state.contractPerformance))) << 248); + storeInPackedState(asset, "statusDate", bytes32(state.statusDate)); + storeInPackedState(asset, "nonPerformingDate", bytes32(state.nonPerformingDate)); + storeInPackedState(asset, "maturityDate", bytes32(state.maturityDate)); + storeInPackedState(asset, "terminationDate", bytes32(state.terminationDate)); + storeInPackedState(asset, "notionalPrincipal", bytes32(state.notionalPrincipal)); + storeInPackedState(asset, "accruedInterest", bytes32(state.accruedInterest)); + storeInPackedState(asset, "feeAccrued", bytes32(state.feeAccrued)); + storeInPackedState(asset, "nominalInterestRate", bytes32(state.nominalInterestRate)); + storeInPackedState(asset, "interestScalingMultiplier", bytes32(state.interestScalingMultiplier)); + storeInPackedState(asset, "notionalScalingMultiplier", bytes32(state.notionalScalingMultiplier)); + } + + /** + * @dev Tightly pack and store finalized PAMState + */ + function encodeAndSetFinalizedPAMState(Asset storage asset, PAMState memory state) external { + storeInPackedState(asset, "F_contractPerformance", bytes32(uint256(uint8(state.contractPerformance))) << 248); + storeInPackedState(asset, "F_statusDate", bytes32(state.statusDate)); + storeInPackedState(asset, "F_nonPerformingDate", bytes32(state.nonPerformingDate)); + storeInPackedState(asset, "F_maturityDate", bytes32(state.maturityDate)); + storeInPackedState(asset, "F_terminationDate", bytes32(state.terminationDate)); + storeInPackedState(asset, "F_notionalPrincipal", bytes32(state.notionalPrincipal)); + storeInPackedState(asset, "F_accruedInterest", bytes32(state.accruedInterest)); + storeInPackedState(asset, "F_feeAccrued", bytes32(state.feeAccrued)); + storeInPackedState(asset, "F_nominalInterestRate", bytes32(state.nominalInterestRate)); + storeInPackedState(asset, "F_interestScalingMultiplier", bytes32(state.interestScalingMultiplier)); + storeInPackedState(asset, "F_notionalScalingMultiplier", bytes32(state.notionalScalingMultiplier)); + } + + /** + * @dev Decode and load the PAMState of the asset + */ + function decodeAndGetPAMState(Asset storage asset) + external + view + returns (PAMState memory) + { + return PAMState( + ContractPerformance(uint8(uint256(asset.packedState["contractPerformance"] >> 248))), + uint256(asset.packedState["statusDate"]), + uint256(asset.packedState["nonPerformingDate"]), + uint256(asset.packedState["maturityDate"]), + uint256(asset.packedState["terminationDate"]), + + int256(asset.packedState["notionalPrincipal"]), + int256(asset.packedState["accruedInterest"]), + int256(asset.packedState["feeAccrued"]), + int256(asset.packedState["nominalInterestRate"]), + int256(asset.packedState["interestScalingMultiplier"]), + int256(asset.packedState["notionalScalingMultiplier"]) + ); + } + + /** + * @dev Decode and load the finalized PAMState of the asset + */ + function decodeAndGetFinalizedPAMState(Asset storage asset) + external + view + returns (PAMState memory) + { + return PAMState( + ContractPerformance(uint8(uint256(asset.packedState["F_contractPerformance"] >> 248))), + uint256(asset.packedState["F_statusDate"]), + uint256(asset.packedState["F_nonPerformingDate"]), + uint256(asset.packedState["F_maturityDate"]), + uint256(asset.packedState["F_terminationDate"]), + + int256(asset.packedState["F_notionalPrincipal"]), + int256(asset.packedState["F_accruedInterest"]), + int256(asset.packedState["F_feeAccrued"]), + int256(asset.packedState["F_nominalInterestRate"]), + int256(asset.packedState["F_interestScalingMultiplier"]), + int256(asset.packedState["F_notionalScalingMultiplier"]) + ); + } + + function decodeAndGetEnumValueForPAMStateAttribute(Asset storage asset, bytes32 attributeKey) + external + view + returns (uint8) + { + if (attributeKey == bytes32("contractPerformance")) { + return uint8(uint256(asset.packedState["contractPerformance"] >> 248)); + } else if (attributeKey == bytes32("F_contractPerformance")) { + return uint8(uint256(asset.packedState["F_contractPerformance"] >> 248)); + } else { + return uint8(0); + } + } + + function decodeAndGetUIntValueForPAMStateAttribute(Asset storage asset, bytes32 attributeKey) + external + view + returns (uint256) + { + return uint256(asset.packedState[attributeKey]); + } + + function decodeAndGetIntValueForPAMStateAttribute(Asset storage asset, bytes32 attributeKey) + external + view + returns (int256) + { + return int256(asset.packedState[attributeKey]); + } } \ No newline at end of file diff --git a/packages/ap-contracts/contracts/Core/PAM/PAMRegistry.sol b/packages/ap-contracts/contracts/Core/PAM/PAMRegistry.sol index e2407f5f..60daac33 100644 --- a/packages/ap-contracts/contracts/Core/PAM/PAMRegistry.sol +++ b/packages/ap-contracts/contracts/Core/PAM/PAMRegistry.sol @@ -38,7 +38,7 @@ contract PAMRegistry is BaseRegistry, IPAMRegistry { function registerAsset( bytes32 assetId, PAMTerms calldata terms, - State calldata state, + PAMState calldata state, bytes32[] calldata schedule, AssetOwnership calldata ownership, address engine, @@ -49,8 +49,10 @@ contract PAMRegistry is BaseRegistry, IPAMRegistry { override onlyApprovedActors { - setAsset(assetId, state, schedule, ownership, engine, actor, admin); + setAsset(assetId, schedule, ownership, engine, actor, admin); assets[assetId].encodeAndSetPAMTerms(terms); + assets[assetId].encodeAndSetPAMState(state); + assets[assetId].encodeAndSetFinalizedPAMState(state); } /** @@ -88,7 +90,7 @@ contract PAMRegistry is BaseRegistry, IPAMRegistry { override(ITermsRegistry, TermsRegistry) returns (uint8) { - return assets[assetId].decodeAndGetEnumValueForPAMAttribute(attribute); + return assets[assetId].decodeAndGetEnumValueForPAMTermsAttribute(attribute); } function getAddressValueForTermsAttribute(bytes32 assetId, bytes32 attribute) @@ -97,7 +99,7 @@ contract PAMRegistry is BaseRegistry, IPAMRegistry { override(ITermsRegistry, TermsRegistry) returns (address) { - return assets[assetId].decodeAndGetAddressValueForForPAMAttribute(attribute); + return assets[assetId].decodeAndGetAddressValueForPAMTermsAttribute(attribute); } function getBytes32ValueForTermsAttribute(bytes32 assetId, bytes32 attribute) @@ -106,7 +108,7 @@ contract PAMRegistry is BaseRegistry, IPAMRegistry { override(ITermsRegistry, TermsRegistry) returns (bytes32) { - return assets[assetId].decodeAndGetBytes32ValueForForPAMAttribute(attribute); + return assets[assetId].decodeAndGetBytes32ValueForPAMTermsAttribute(attribute); } function getUIntValueForTermsAttribute(bytes32 assetId, bytes32 attribute) @@ -115,7 +117,7 @@ contract PAMRegistry is BaseRegistry, IPAMRegistry { override(ITermsRegistry, TermsRegistry) returns (uint256) { - return assets[assetId].decodeAndGetUIntValueForForPAMAttribute(attribute); + return assets[assetId].decodeAndGetUIntValueForPAMTermsAttribute(attribute); } function getIntValueForTermsAttribute(bytes32 assetId, bytes32 attribute) @@ -124,7 +126,7 @@ contract PAMRegistry is BaseRegistry, IPAMRegistry { override(ITermsRegistry, TermsRegistry) returns (int256) { - return assets[assetId].decodeAndGetIntValueForForPAMAttribute(attribute); + return assets[assetId].decodeAndGetIntValueForPAMTermsAttribute(attribute); } function getPeriodValueForTermsAttribute(bytes32 assetId, bytes32 attribute) @@ -133,7 +135,7 @@ contract PAMRegistry is BaseRegistry, IPAMRegistry { override(ITermsRegistry, TermsRegistry) returns (IP memory) { - return assets[assetId].decodeAndGetPeriodValueForForPAMAttribute(attribute); + return assets[assetId].decodeAndGetPeriodValueForPAMTermsAttribute(attribute); } function getCycleValueForTermsAttribute(bytes32 assetId, bytes32 attribute) @@ -142,7 +144,7 @@ contract PAMRegistry is BaseRegistry, IPAMRegistry { override(ITermsRegistry, TermsRegistry) returns (IPS memory) { - return assets[assetId].decodeAndGetCycleValueForForPAMAttribute(attribute); + return assets[assetId].decodeAndGetCycleValueForPAMTermsAttribute(attribute); } function getContractReferenceValueForTermsAttribute(bytes32 assetId, bytes32 attribute) @@ -151,7 +153,92 @@ contract PAMRegistry is BaseRegistry, IPAMRegistry { override(ITermsRegistry, TermsRegistry) returns (ContractReference memory) { - return assets[assetId].decodeAndGetContractReferenceValueForPAMAttribute(attribute); + return assets[assetId].decodeAndGetContractReferenceValueForPAMTermsAttribute(attribute); + } + + /** + * @notice Returns the state of an asset. + * @param assetId id of the asset + * @return state of the asset + */ + function getState(bytes32 assetId) + external + view + override + returns (PAMState memory) + { + return assets[assetId].decodeAndGetPAMState(); + } + + /** + * @notice Returns the state of an asset. + * @param assetId id of the asset + * @return state of the asset + */ + function getFinalizedState(bytes32 assetId) + external + view + override + returns (PAMState memory) + { + return assets[assetId].decodeAndGetFinalizedPAMState(); + } + + /** + * @notice Sets next state of an asset. + * @dev Can only be updated by the assets actor or by an authorized account. + * @param assetId id of the asset + * @param state next state of the asset + */ + function setState(bytes32 assetId, PAMState calldata state) + external + override + isAuthorized (assetId) + { + assets[assetId].encodeAndSetPAMState(state); + emit UpdatedState(assetId, state.statusDate); + } + + /** + * @notice Sets next finalized state of an asset. + * @dev Can only be updated by the assets actor or by an authorized account. + * @param assetId id of the asset + * @param state next state of the asset + */ + function setFinalizedState(bytes32 assetId, PAMState calldata state) + external + override + isAuthorized (assetId) + { + assets[assetId].encodeAndSetFinalizedPAMState(state); + emit UpdatedFinalizedState(assetId, state.statusDate); + } + + function getEnumValueForStateAttribute(bytes32 assetId, bytes32 attribute) + public + view + override(IStateRegistry, StateRegistry) + returns (uint8) + { + return assets[assetId].decodeAndGetEnumValueForPAMStateAttribute(attribute); + } + + function getIntValueForStateAttribute(bytes32 assetId, bytes32 attribute) + public + view + override(IStateRegistry, StateRegistry) + returns (int256) + { + return assets[assetId].decodeAndGetIntValueForPAMStateAttribute(attribute); + } + + function getUintValueForStateAttribute(bytes32 assetId, bytes32 attribute) + public + view + override(IStateRegistry, StateRegistry) + returns (uint256) + { + return assets[assetId].decodeAndGetUIntValueForPAMStateAttribute(attribute); } function getNextCyclicEvent(bytes32 assetId)