Skip to content

Commit 2f17fda

Browse files
feat: add loansPortfolio
Signed-off-by: Axel-IoBuilders <108282711+Axel-IoBuilders@users.noreply.github.com>
1 parent 5968e9f commit 2f17fda

33 files changed

Lines changed: 2715 additions & 823 deletions

File tree

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@hashgraph/asset-tokenization-contracts": minor
3+
---
4+
5+
Add LoansPortfolioFacet for LoanPortfolio asset type. Manages a portfolio of loan and cash holdings with classification by collateral, performance status, and country. Includes LoansPortfolioStorageWrapper, full Diamond integration (resolver keys, roles, storage positions), TimeTravel test variant, and a complete integration test suite.

packages/ats/contracts/contracts/constants/resolverKeys.sol

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -653,11 +653,11 @@ bytes32 constant _DIVIDEND_RESOLVER_KEY = 0x63752e3f4bd54d9fec1ad1667ef4de4f80e9
653653
// keccak256('security.token.standard.amortization.resolverKey');
654654
bytes32 constant _AMORTIZATION_RESOLVER_KEY = 0xe45d89550ef8988da0d14267142ce98f7a8b9c0d1e2f3a4b5c6d7e8f9a0b1c2d;
655655

656-
// keccak256('security.token.standard.loansPortfolio.resolverKey');
657-
bytes32 constant _LOANS_PORTFOLIO_RESOLVER_KEY = 0x9c0d1e2f3a4b5c6d7e8f9a0b1c2d3e4f5a6b7c8d9e0f1a2b3c4d5e6f7a8b9c0d;
656+
// keccak256("security.token.standard.loan.resolverKey");
657+
bytes32 constant _LOAN_RESOLVER_KEY = 0x99001f821b750d64e6a82cf207800dc005acf93b2880abe9f1cdbbfb5e996b86;
658658

659-
// keccak256('security.token.standard.loan.resolverKey');
660-
bytes32 constant _LOAN_RESOLVER_KEY = 0x0d1e2f3a4b5c6d7e8f9a0b1c2d3e4f5a6b7c8d9e0f1a2b3c4d5e6f7a8b9c0d1e;
659+
// keccak256("security.token.standard.loansPortfolio.resolverKey");
660+
bytes32 constant _LOANS_PORTFOLIO_RESOLVER_KEY = 0x364410ebf7978001f91cfe2189e143fa8d75bb7365901fbc3d071ece7f86bd46;
661661

662662
// Layer 3 Resolver Keys
663663

@@ -672,3 +672,5 @@ bytes32 constant _TRANSFER_AND_LOCK_KPI_LINKED_RATE_RESOLVER_KEY = 0x3e5f7a9b1c2
672672

673673
// keccak256("security.token.standard.transferandlock.SustainabilityPerformanceTarget.rate.resolverKey");
674674
bytes32 constant _TRANSFER_AND_LOCK_SUSTAINABILITY_PERFORMANCE_TARGET_RATE_RESOLVER_KEY = 0x9d1e3f5a7b9c0d2e4f6a8b0c1d3e5f7a9b0c2d4e6f8a9b1c3d5e7f9a0b2c4d6e;
675+
676+

packages/ats/contracts/contracts/constants/roles.sol

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -115,9 +115,6 @@ bytes32 constant _WILD_CARD_ROLE = 0x96658f163b67573bbf1e3f9e9330b199b3ac2f6ec01
115115
// keccak256('security.token.standard.role.nominalValue');
116116
bytes32 constant _NOMINAL_VALUE_ROLE = 0x127c185a9f04723376575bc896cc0d3cf15a32dd0db17f01168dcac5d2de6102;
117117

118-
// keccak256('security.token.standard.role.loanOriginator');
119-
bytes32 constant _LOAN_ORIGINATOR_ROLE = 0x2c535291a6d0dde45c902f1589f13425a81654fd732c65147b4decf85c17e707;
120-
121118
// keccak256('security.token.standard.role.loanManager');
122119
bytes32 constant _LOAN_MANAGER_ROLE = 0xc085daff7cbf912b30437b0b95363f3920f33cbd53213a269a2fc5d44ee8289d;
123120

packages/ats/contracts/contracts/constants/storagePositions.sol

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -147,12 +147,12 @@ bytes32 constant _NOMINAL_VALUE_STORAGE_POSITION = 0xc0fde456b6db4817b54ba988cff
147147
// keccak256('security.token.standard.amortization.storage');
148148
bytes32 constant _AMORTIZATION_STORAGE_POSITION = 0x811809f3b5cf04ae28619650750f871e7fd7d91ca60e5d1c85e15eb170c74524;
149149

150-
// keccak256('security.token.standard.loansPortfolio.storage');
151-
bytes32 constant _LOANS_PORTFOLIO_STORAGE_POSITION = 0x7a8b9c0d1e2f3a4b5c6d7e8f9a0b1c2d3e4f5a6b7c8d9e0f1a2b3c4d5e6f7a8b;
152-
153150
// keccak256('security.token.standard.loan.storage');
154151
bytes32 constant _LOAN_STORAGE_POSITION = 0x8b9c0d1e2f3a4b5c6d7e8f9a0b1c2d3e4f5a6b7c8d9e0f1a2b3c4d5e6f7a8b9c;
155152

153+
// keccak256('security.token.standard.loansPortfolio.storage');
154+
bytes32 constant _LOANS_PORTFOLIO_STORAGE_POSITION = 0xce43d022d4ff90dcc241a663e3a14313e42aad8fd58de13c348659eb5fd12925;
155+
156156
// Layer 3 Unique Constants
157157
// keccak256('security.token.standard.security.storage');
158158
bytes32 constant _SECURITY_STORAGE_POSITION = 0x95205812666aa702c6386778a59ee67a93a9559063c49d34d12e4072e5995379;

packages/ats/contracts/contracts/domain/asset/amortization/AmortizationStorageWrapper.sol

Lines changed: 14 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -235,7 +235,6 @@ library AmortizationStorageWrapper {
235235

236236
) = getAmortization(_amortizationID);
237237

238-
amortizationFor_.account = _account;
239238
amortizationFor_.recordDate = registeredAmortization.amortization.recordDate;
240239
amortizationFor_.executionDate = registeredAmortization.amortization.executionDate;
241240

@@ -281,12 +280,12 @@ library AmortizationStorageWrapper {
281280
uint256 _amortizationID,
282281
uint256 _pageIndex,
283282
uint256 _pageLength
284-
) internal view returns (IAmortization.AmortizationFor[] memory amortizationsFor_) {
285-
address[] memory holders = getAmortizationHolders(_amortizationID, _pageIndex, _pageLength);
286-
uint256 length = holders.length;
283+
) internal view returns (IAmortization.AmortizationFor[] memory amortizationsFor_, address[] memory holders_) {
284+
holders_ = getAmortizationHolders(_amortizationID, _pageIndex, _pageLength);
285+
uint256 length = holders_.length;
287286
amortizationsFor_ = new IAmortization.AmortizationFor[](length);
288287
for (uint256 i; i < length; ) {
289-
amortizationsFor_[i] = getAmortizationFor(_amortizationID, holders[i]);
288+
amortizationsFor_[i] = getAmortizationFor(_amortizationID, holders_[i]);
290289
unchecked {
291290
++i;
292291
}
@@ -335,15 +334,6 @@ library AmortizationStorageWrapper {
335334
return ERC1410StorageWrapper.getTotalTokenHolders();
336335
}
337336

338-
function getAmortizationPaymentAmount(
339-
uint256 _amortizationID,
340-
address _tokenHolder
341-
) internal view returns (uint256 tokenAmount_, uint8 decimals_) {
342-
IAmortization.AmortizationFor memory amortizationFor = getAmortizationFor(_amortizationID, _tokenHolder);
343-
tokenAmount_ = amortizationFor.tokenHeldAmount;
344-
decimals_ = amortizationFor.decimalsHeld;
345-
}
346-
347337
function getAmortizationActiveHolders(
348338
uint256 _amortizationID,
349339
uint256 _pageIndex,
@@ -361,25 +351,19 @@ library AmortizationStorageWrapper {
361351
}
362352

363353
function getTotalAmortizationActiveHolders(uint256 _amortizationID) internal view returns (uint256) {
364-
return
365-
_amortizationStorage()
366-
.activeHoldHolders[
367-
CorporateActionsStorageWrapper.getCorporateActionIdByTypeIndex(
368-
AMORTIZATION_CORPORATE_ACTION_TYPE,
369-
_amortizationID - 1
370-
)
371-
]
372-
.length();
354+
bytes32 corporateActionId = CorporateActionsStorageWrapper.getCorporateActionIdByTypeIndex(
355+
AMORTIZATION_CORPORATE_ACTION_TYPE,
356+
_amortizationID - 1
357+
);
358+
return _amortizationStorage().activeHoldHolders[corporateActionId].length();
373359
}
374360

375361
function getTotalHoldByAmortizationId(uint256 _amortizationID) internal view returns (uint256) {
376-
return
377-
_amortizationStorage().totalHoldByAmortizationId[
378-
CorporateActionsStorageWrapper.getCorporateActionIdByTypeIndex(
379-
AMORTIZATION_CORPORATE_ACTION_TYPE,
380-
_amortizationID - 1
381-
)
382-
];
362+
bytes32 corporateActionId = CorporateActionsStorageWrapper.getCorporateActionIdByTypeIndex(
363+
AMORTIZATION_CORPORATE_ACTION_TYPE,
364+
_amortizationID - 1
365+
);
366+
return _amortizationStorage().totalHoldByAmortizationId[corporateActionId];
383367
}
384368

385369
function getActiveAmortizationIds(
Lines changed: 3 additions & 102 deletions
Original file line numberDiff line numberDiff line change
@@ -1,107 +1,8 @@
11
// SPDX-License-Identifier: Apache-2.0
22
pragma solidity >=0.8.0 <0.9.0;
33

4-
/**
5-
* @title ILoansPortfolioStorageWrapper
6-
* @notice Interface for loan and portfolio storage operations
7-
* @dev Defines storage layout, events, and custom errors for loan lifecycle
8-
* @author Hashgraph
9-
*/
104
interface ILoansPortfolioStorageWrapper {
11-
struct LoanPortfolioData {
12-
uint256 totalLoans;
13-
uint256 activeLoans;
14-
uint256 totalValue;
15-
mapping(uint256 => bytes32) loanIds;
16-
mapping(bytes32 => uint256) loanIndex;
17-
}
18-
19-
/**
20-
* @notice Emitted when a loan is added to the portfolio.
21-
* @param portfolioId The portfolio identifier.
22-
* @param loanId The unique loan identifier.
23-
* @param operator Address that performed the operation.
24-
* @param timestamp Timestamp when the loan was added.
25-
*/
26-
event LoanAddedToPortfolio(
27-
bytes32 indexed portfolioId,
28-
bytes32 indexed loanId,
29-
address indexed operator,
30-
uint256 timestamp
31-
);
32-
33-
/**
34-
* @notice Emitted when a loan is removed from the portfolio.
35-
* @param portfolioId The portfolio identifier.
36-
* @param loanId The unique loan identifier.
37-
* @param operator Address that performed the operation.
38-
*/
39-
event LoanRemovedFromPortfolio(bytes32 indexed portfolioId, bytes32 indexed loanId, address indexed operator);
40-
41-
/**
42-
* @notice Emitted when a loan is created.
43-
* @param loanId The unique loan identifier.
44-
* @param operator Address that created the loan.
45-
* @param timestamp Timestamp when the loan was created.
46-
*/
47-
event LoanCreated(bytes32 indexed loanId, address indexed operator, uint256 timestamp);
48-
49-
/**
50-
* @notice Emitted when a loan is cancelled.
51-
* @param loanId The unique loan identifier.
52-
* @param operator Address that performed the cancellation.
53-
*/
54-
event LoanCancelled(bytes32 indexed loanId, address indexed operator);
55-
56-
/**
57-
* @notice Loan creation failed due to an internal failure.
58-
*/
59-
error LoanCreationFailed();
60-
61-
/**
62-
* @notice Thrown when attempting to operate on a cancelled loan.
63-
* @param loanId The loan identifier.
64-
*/
65-
error LoanNotActive(bytes32 loanId);
66-
67-
/**
68-
* @notice Thrown when attempting to cancel a loan that still has active operations.
69-
* @param loanId The loan identifier.
70-
*/
71-
error LoanHasActiveOperations(bytes32 loanId);
72-
73-
/**
74-
* @notice Thrown when loan ID is not found.
75-
* @param loanId The loan identifier.
76-
*/
77-
error LoanNotFound(bytes32 loanId);
78-
79-
/**
80-
* @notice Thrown when attempting to add a duplicate loan to portfolio.
81-
* @param portfolioId The portfolio identifier.
82-
* @param loanId The loan identifier.
83-
*/
84-
error LoanAlreadyInPortfolio(bytes32 portfolioId, bytes32 loanId);
85-
86-
/**
87-
* @notice Thrown when attempting to remove a loan that is not in the portfolio.
88-
* @param portfolioId The portfolio identifier.
89-
* @param loanId The loan identifier.
90-
*/
91-
error LoanNotInPortfolio(bytes32 portfolioId, bytes32 loanId);
92-
93-
/**
94-
* @notice Thrown when loan state is invalid for the requested operation.
95-
* @param loanId The loan identifier.
96-
* @param currentState The current loan state.
97-
*/
98-
error InvalidLoanState(bytes32 loanId, uint8 currentState);
99-
100-
/**
101-
* @notice Thrown when state transition is not allowed.
102-
* @param loanId The loan identifier.
103-
* @param fromState The current state.
104-
* @param toState The target state.
105-
*/
106-
error InvalidStateTransition(bytes32 loanId, uint8 fromState, uint8 toState);
5+
error HoldingsAssetAlreadyExists(address assetAddress);
6+
error HoldingAssetNotFound(address assetAddress);
7+
error HoldingsAssetTypeNotSupported(uint8 holdingsAssetType);
1078
}

0 commit comments

Comments
 (0)