@@ -15,6 +15,16 @@ import { FinancialOps } from "@synaps3/core/libraries/FinancialOps.sol";
1515abstract contract BalanceOperatorUpgradeable is Initializable , LedgerUpgradeable , IBalanceOperator {
1616 using FinancialOps for address ;
1717
18+ /// @custom:storage-location erc7201:balanceoperatorupgradeable
19+ struct BalanceOperatorStorage {
20+ /// @dev Holds the relation between a reserved funds the currency and amount
21+ mapping (bytes32 => mapping (address => uint256 )) _reserved;
22+ }
23+
24+ /// @dev Storage slot for BalanceOperatorUpgradeable, calculated using a unique namespace to avoid conflicts.
25+ /// The `BALANCE_OPERATOR_SLOT` constant is used to point to the location of the storage.
26+ bytes32 private constant BALANCE_OPERATOR_SLOT = 0xa8707513830ffbd3c47e0c83d1f5f0270db240ae37bb1f9a13f077f85b949c00 ;
27+
1828 /// @dev Initializes the contract and ensures it is upgradeable.
1929 /// Even if the initialization is harmless, this ensures the contract follows upgradeable contract patterns.
2030 /// This is the method to initialize this contract and any other extended contracts.
@@ -67,4 +77,79 @@ abstract contract BalanceOperatorUpgradeable is Initializable, LedgerUpgradeable
6777 _sumLedgerEntry (recipient, amount, currency);
6878 return amount;
6979 }
80+
81+ /// @notice Reserves a specific amount of funds from the caller's balance for a recipient.
82+ /// @param to The address of the recipient for whom the funds are being reserved.
83+ /// @param amount The amount of funds to reserve.
84+ /// @param currency The address of the ERC20 token to reserve. Use `address(0)` for native tokens.
85+ function _reserve (address to , uint256 amount , address currency ) internal returns (uint256 ) {
86+ if (getLedgerBalance (msg .sender , currency) < amount) revert NoFundsToReserve ();
87+ _subLedgerEntry (msg .sender , amount, currency);
88+ _sumReservedAmount (msg .sender , to, amount, currency);
89+ return amount;
90+ }
91+
92+ /// @notice Collects a specific amount of previously reserved funds.
93+ /// @param from The address of the account from which the reserved funds are being collected.
94+ /// @param amount The amount of funds to collect.
95+ /// @param currency The address of the ERC20 token to collect. Use `address(0)` for native tokens.
96+ function _collect (address from , uint256 amount , address currency ) internal returns (uint256 ) {
97+ if (_getReservedAmount (from, msg .sender , currency) < amount) revert NoFundsToCollect ();
98+ _subReservedAmount (from, msg .sender , amount, currency);
99+ _sumLedgerEntry (msg .sender , amount, currency);
100+ return amount;
101+ }
102+
103+ /// @notice Reduces the reserved funds for a specific relationship and currency.
104+ /// @dev Deducts the specified `amount` from the `_reserved` mapping for the given `from` and `to` relationship and `currency`.
105+ /// @param from The address of the account from which the funds were reserved.
106+ /// @param to The address of the account for which the funds were reserved.
107+ /// @param amount The amount to subtract from the reserved balance.
108+ /// @param currency The address of the currency being reduced.
109+ function _subReservedAmount (address from , address to , uint256 amount , address currency ) private {
110+ BalanceOperatorStorage storage $ = _getBalanceOperatorStorage ();
111+ bytes32 relation = _computeComposedKey (from, to);
112+ $._reserved[relation][currency] -= amount;
113+ }
114+
115+ /// @notice Increases the reserved funds for a specific relationship and currency.
116+ /// @dev Adds the specified `amount` to the `_reserved` mapping for the given `from` and `to` relationship and `currency`.
117+ /// @param from The address of the account from which the funds are reserved.
118+ /// @param to The address of the account for which the funds are reserved.
119+ /// @param amount The amount to add to the reserved balance.
120+ /// @param currency The address of the currency being increased.
121+ function _sumReservedAmount (address from , address to , uint256 amount , address currency ) private {
122+ BalanceOperatorStorage storage $ = _getBalanceOperatorStorage ();
123+ bytes32 relation = _computeComposedKey (from, to);
124+ $._reserved[relation][currency] += amount;
125+ }
126+
127+ /// @notice Retrieves the reserved balance for a specific relationship and currency.
128+ /// @dev Returns the value stored in the `_reserved` mapping for the given `from` and `to` relationship and `currency`.
129+ /// @param from The address of the account from which the funds are reserved.
130+ /// @param to The address of the account for which the funds are reserved.
131+ /// @param currency The address of the currency to check the reserved balance for.
132+ /// @return The reserved balance for the specified relationship and currency.
133+ function _getReservedAmount (address from , address to , address currency ) private view returns (uint256 ) {
134+ BalanceOperatorStorage storage $ = _getBalanceOperatorStorage ();
135+ bytes32 relation = _computeComposedKey (from, to);
136+ return $._reserved[relation][currency];
137+ }
138+
139+ /// @notice Computes a unique key by combining two addresses.
140+ /// @dev This key is used to map relationships between accounts.
141+ /// @param from The address of the user for whom the key is being generated.
142+ /// @param to Encoded data representing the context for the operation.
143+ /// @return A `bytes32` hash that uniquely identifies the context-account pair.
144+ function _computeComposedKey (address from , address to ) private pure returns (bytes32 ) {
145+ return keccak256 (abi.encodePacked (from, to));
146+ }
147+
148+ /// @notice Internal function to get the balance operator storage.
149+ /// @dev Uses assembly to retrieve the storage at the pre-calculated storage slot.
150+ function _getBalanceOperatorStorage () private pure returns (BalanceOperatorStorage storage $) {
151+ assembly {
152+ $.slot := BALANCE_OPERATOR_SLOT
153+ }
154+ }
70155}
0 commit comments