Skip to content

Commit 9e05f2f

Browse files
committed
fix: separate delegation and provision thaw request lists (TRST-H02)
1 parent 0c0d090 commit 9e05f2f

File tree

8 files changed

+348
-108
lines changed

8 files changed

+348
-108
lines changed

packages/horizon/contracts/interfaces/internal/IHorizonStakingBase.sol

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -137,7 +137,10 @@ interface IHorizonStakingBase {
137137
* @param thawRequestId The id of the thaw request.
138138
* @return The thaw request details.
139139
*/
140-
function getThawRequest(bytes32 thawRequestId) external view returns (IHorizonStakingTypes.ThawRequest memory);
140+
function getThawRequest(
141+
IHorizonStakingTypes.ThawRequestType thawRequestType,
142+
bytes32 thawRequestId
143+
) external view returns (IHorizonStakingTypes.ThawRequest memory);
141144

142145
/**
143146
* @notice Gets the metadata of a thaw request list.
@@ -149,6 +152,7 @@ interface IHorizonStakingBase {
149152
* @return The thaw requests list metadata.
150153
*/
151154
function getThawRequestList(
155+
IHorizonStakingTypes.ThawRequestType thawRequestType,
152156
address serviceProvider,
153157
address verifier,
154158
address owner
@@ -161,7 +165,12 @@ interface IHorizonStakingBase {
161165
* @param owner The owner of the thaw requests. Use either the service provider or delegator address.
162166
* @return The amount of thawed tokens.
163167
*/
164-
function getThawedTokens(address serviceProvider, address verifier, address owner) external view returns (uint256);
168+
function getThawedTokens(
169+
IHorizonStakingTypes.ThawRequestType thawRequestType,
170+
address serviceProvider,
171+
address verifier,
172+
address owner
173+
) external view returns (uint256);
165174

166175
/**
167176
* @notice Gets the maximum allowed thawing period for a provision.

packages/horizon/contracts/interfaces/internal/IHorizonStakingTypes.sol

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,16 @@ interface IHorizonStakingTypes {
131131
uint256 __DEPRECATED_tokensLockedUntil;
132132
}
133133

134+
/**
135+
* @dev Enum to specify the type of thaw request.
136+
* @param Provision Represents a thaw request for a provision.
137+
* @param Delegation Represents a thaw request for a delegation.
138+
*/
139+
enum ThawRequestType {
140+
Provision,
141+
Delegation
142+
}
143+
134144
/**
135145
* @notice Details of a stake thawing operation.
136146
* @dev ThawRequests are stored in linked lists by service provider/delegator,

packages/horizon/contracts/staking/HorizonStaking.sol

Lines changed: 131 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -741,6 +741,7 @@ contract HorizonStaking is HorizonStakingBase, IHorizonStakingMain {
741741
prov.tokensThawing = prov.tokensThawing + _tokens;
742742

743743
bytes32 thawRequestId = _createThawRequest(
744+
ThawRequestType.Provision,
744745
_serviceProvider,
745746
_verifier,
746747
_serviceProvider,
@@ -765,15 +766,18 @@ contract HorizonStaking is HorizonStakingBase, IHorizonStakingMain {
765766
uint256 tokensThawed_ = 0;
766767
uint256 sharesThawing = prov.sharesThawing;
767768
uint256 tokensThawing = prov.tokensThawing;
768-
(tokensThawed_, tokensThawing, sharesThawing) = _fulfillThawRequests(
769-
_serviceProvider,
770-
_verifier,
771-
_serviceProvider,
772-
tokensThawing,
773-
sharesThawing,
774-
_nThawRequests,
775-
prov.thawingNonce
776-
);
769+
770+
FulfillThawRequestsParams memory params = FulfillThawRequestsParams({
771+
requestType: ThawRequestType.Provision,
772+
serviceProvider: _serviceProvider,
773+
verifier: _verifier,
774+
owner: _serviceProvider,
775+
tokensThawing: tokensThawing,
776+
sharesThawing: sharesThawing,
777+
nThawRequests: _nThawRequests,
778+
thawingNonce: prov.thawingNonce
779+
});
780+
(tokensThawed_, tokensThawing, sharesThawing) = _fulfillThawRequests(params);
777781

778782
prov.tokens = prov.tokens - tokensThawed_;
779783
prov.sharesThawing = sharesThawing;
@@ -860,6 +864,7 @@ contract HorizonStaking is HorizonStakingBase, IHorizonStakingMain {
860864
delegation.shares = delegation.shares - _shares;
861865

862866
bytes32 thawRequestId = _createThawRequest(
867+
ThawRequestType.Delegation,
863868
_serviceProvider,
864869
_verifier,
865870
_beneficiary,
@@ -894,15 +899,18 @@ contract HorizonStaking is HorizonStakingBase, IHorizonStakingMain {
894899
uint256 tokensThawed = 0;
895900
uint256 sharesThawing = pool.sharesThawing;
896901
uint256 tokensThawing = pool.tokensThawing;
897-
(tokensThawed, tokensThawing, sharesThawing) = _fulfillThawRequests(
898-
_serviceProvider,
899-
_verifier,
900-
msg.sender,
901-
tokensThawing,
902-
sharesThawing,
903-
_nThawRequests,
904-
pool.thawingNonce
905-
);
902+
903+
FulfillThawRequestsParams memory params = FulfillThawRequestsParams({
904+
requestType: ThawRequestType.Delegation,
905+
serviceProvider: _serviceProvider,
906+
verifier: _verifier,
907+
owner: msg.sender,
908+
tokensThawing: tokensThawing,
909+
sharesThawing: sharesThawing,
910+
nThawRequests: _nThawRequests,
911+
thawingNonce: pool.thawingNonce
912+
});
913+
(tokensThawed, tokensThawing, sharesThawing) = _fulfillThawRequests(params);
906914

907915
// The next subtraction should never revert becase: pool.tokens >= pool.tokensThawing and pool.tokensThawing >= tokensThawed
908916
// In the event the pool gets completely slashed tokensThawed will fulfil to 0.
@@ -936,69 +944,103 @@ contract HorizonStaking is HorizonStakingBase, IHorizonStakingMain {
936944
* @return The ID of the thaw request
937945
*/
938946
function _createThawRequest(
947+
ThawRequestType _requestType,
939948
address _serviceProvider,
940949
address _verifier,
941950
address _owner,
942951
uint256 _shares,
943952
uint64 _thawingUntil,
944953
uint256 _thawingNonce
945954
) private returns (bytes32) {
946-
LinkedList.List storage thawRequestList = _thawRequestLists[_serviceProvider][_verifier][_owner];
955+
LinkedList.List storage thawRequestList = _getThawRequestList(
956+
_requestType,
957+
_serviceProvider,
958+
_verifier,
959+
_owner
960+
);
947961
require(thawRequestList.count < MAX_THAW_REQUESTS, HorizonStakingTooManyThawRequests());
948962

949963
bytes32 thawRequestId = keccak256(abi.encodePacked(_serviceProvider, _verifier, _owner, thawRequestList.nonce));
950-
_thawRequests[thawRequestId] = ThawRequest({
951-
shares: _shares,
952-
thawingUntil: _thawingUntil,
953-
next: bytes32(0),
954-
thawingNonce: _thawingNonce
955-
});
964+
ThawRequest storage thawRequest = _getThawRequest(_requestType, thawRequestId);
965+
thawRequest.shares = _shares;
966+
thawRequest.thawingUntil = _thawingUntil;
967+
thawRequest.next = bytes32(0);
968+
thawRequest.thawingNonce = _thawingNonce;
956969

957-
if (thawRequestList.count != 0) _thawRequests[thawRequestList.tail].next = thawRequestId;
970+
if (thawRequestList.count != 0) _getThawRequest(_requestType, thawRequestList.tail).next = thawRequestId;
958971
thawRequestList.addTail(thawRequestId);
959972

960973
emit ThawRequestCreated(_serviceProvider, _verifier, _owner, _shares, _thawingUntil, thawRequestId);
961974
return thawRequestId;
962975
}
963976

977+
/**
978+
* @param requestType The type of thaw request (Provision or Delegation)
979+
* @param serviceProvider The address of the service provider
980+
* @param verifier The address of the verifier
981+
* @param owner The address of the owner of the thaw request
982+
* @param tokensThawing The current amount of tokens already thawing
983+
* @param sharesThawing The current amount of shares already thawing
984+
* @param nThawRequests The number of thaw requests to fulfill. If set to 0, all thaw requests are fulfilled.
985+
* @param thawingNonce The current valid thawing nonce. Any thaw request with a different nonce is invalid and should be ignored.
986+
*/
987+
struct FulfillThawRequestsParams {
988+
ThawRequestType requestType;
989+
address serviceProvider;
990+
address verifier;
991+
address owner;
992+
uint256 tokensThawing;
993+
uint256 sharesThawing;
994+
uint256 nThawRequests;
995+
uint256 thawingNonce;
996+
}
997+
964998
/**
965999
* @notice Traverses a thaw request list and fulfills expired thaw requests.
9661000
* @dev Emits a {ThawRequestsFulfilled} event and a {ThawRequestFulfilled} event for each thaw request fulfilled.
967-
* @param _serviceProvider The address of the service provider
968-
* @param _verifier The address of the verifier
969-
* @param _owner The address of the owner of the thaw request
970-
* @param _tokensThawing The current amount of tokens already thawing
971-
* @param _sharesThawing The current amount of shares already thawing
972-
* @param _nThawRequests The number of thaw requests to fulfill. If set to 0, all thaw requests are fulfilled.
973-
* @param _thawingNonce The current valid thawing nonce. Any thaw request with a different nonce is invalid and should be ignored.
1001+
* @param params The parameters for fulfilling thaw requests
9741002
* @return The amount of thawed tokens
9751003
* @return The amount of tokens still thawing
9761004
* @return The amount of shares still thawing
9771005
*/
978-
function _fulfillThawRequests(
979-
address _serviceProvider,
980-
address _verifier,
981-
address _owner,
982-
uint256 _tokensThawing,
983-
uint256 _sharesThawing,
984-
uint256 _nThawRequests,
985-
uint256 _thawingNonce
986-
) private returns (uint256, uint256, uint256) {
987-
LinkedList.List storage thawRequestList = _thawRequestLists[_serviceProvider][_verifier][_owner];
1006+
function _fulfillThawRequests(FulfillThawRequestsParams memory params) private returns (uint256, uint256, uint256) {
1007+
LinkedList.List storage thawRequestList = _getThawRequestList(
1008+
params.requestType,
1009+
params.serviceProvider,
1010+
params.verifier,
1011+
params.owner
1012+
);
9881013
require(thawRequestList.count > 0, HorizonStakingNothingThawing());
9891014

990-
uint256 tokensThawed = 0;
1015+
function(bytes32) view returns (bytes32) getNextItem = _getNextItemCallback(params.requestType);
1016+
function(bytes32) deleteItem = _getDeleteThawRequest(params.requestType);
1017+
bytes memory acc = abi.encode(
1018+
params.requestType,
1019+
uint256(0),
1020+
params.tokensThawing,
1021+
params.sharesThawing,
1022+
params.thawingNonce
1023+
);
9911024
(uint256 thawRequestsFulfilled, bytes memory data) = thawRequestList.traverse(
992-
_getNextThawRequest,
1025+
getNextItem,
9931026
_fulfillThawRequest,
994-
_deleteThawRequest,
995-
abi.encode(tokensThawed, _tokensThawing, _sharesThawing, _thawingNonce),
996-
_nThawRequests
1027+
deleteItem,
1028+
acc,
1029+
params.nThawRequests
9971030
);
9981031

999-
(tokensThawed, _tokensThawing, _sharesThawing) = abi.decode(data, (uint256, uint256, uint256));
1000-
emit ThawRequestsFulfilled(_serviceProvider, _verifier, _owner, thawRequestsFulfilled, tokensThawed);
1001-
return (tokensThawed, _tokensThawing, _sharesThawing);
1032+
(, uint256 tokensThawed, uint256 tokensThawing, uint256 sharesThawing) = abi.decode(
1033+
data,
1034+
(ThawRequestType, uint256, uint256, uint256)
1035+
);
1036+
emit ThawRequestsFulfilled(
1037+
params.serviceProvider,
1038+
params.verifier,
1039+
params.owner,
1040+
thawRequestsFulfilled,
1041+
tokensThawed
1042+
);
1043+
return (tokensThawed, tokensThawing, sharesThawing);
10021044
}
10031045

10041046
/**
@@ -1013,19 +1055,22 @@ contract HorizonStaking is HorizonStakingBase, IHorizonStakingMain {
10131055
* @return The updated accumulator data
10141056
*/
10151057
function _fulfillThawRequest(bytes32 _thawRequestId, bytes memory _acc) private returns (bool, bytes memory) {
1016-
ThawRequest storage thawRequest = _thawRequests[_thawRequestId];
1058+
// decode
1059+
(
1060+
ThawRequestType requestType,
1061+
uint256 tokensThawed,
1062+
uint256 tokensThawing,
1063+
uint256 sharesThawing,
1064+
uint256 thawingNonce
1065+
) = abi.decode(_acc, (ThawRequestType, uint256, uint256, uint256, uint256));
1066+
1067+
ThawRequest storage thawRequest = _getThawRequest(requestType, _thawRequestId);
10171068

10181069
// early exit
10191070
if (thawRequest.thawingUntil > block.timestamp) {
10201071
return (true, LinkedList.NULL_BYTES);
10211072
}
10221073

1023-
// decode
1024-
(uint256 tokensThawed, uint256 tokensThawing, uint256 sharesThawing, uint256 thawingNonce) = abi.decode(
1025-
_acc,
1026-
(uint256, uint256, uint256, uint256)
1027-
);
1028-
10291074
// process - only fulfill thaw requests for the current valid nonce
10301075
uint256 tokens = 0;
10311076
bool validThawRequest = thawRequest.thawingNonce == thawingNonce;
@@ -1044,17 +1089,39 @@ contract HorizonStaking is HorizonStakingBase, IHorizonStakingMain {
10441089
);
10451090

10461091
// encode
1047-
_acc = abi.encode(tokensThawed, tokensThawing, sharesThawing, thawingNonce);
1092+
_acc = abi.encode(requestType, tokensThawed, tokensThawing, sharesThawing, thawingNonce);
10481093
return (false, _acc);
10491094
}
10501095

10511096
/**
1052-
* @notice Deletes a ThawRequest.
1053-
* @dev This function is used as a callback in the thaw requests linked list traversal.
1054-
* @param _thawRequestId The ID of the thaw request to delete
1097+
* @notice Determines the correct callback function for `deleteItem` based on the request type.
1098+
* @param _requestType The type of thaw request (Provision or Delegation).
1099+
* @return A function pointer to the appropriate `deleteItem` callback.
1100+
*/
1101+
function _getDeleteThawRequest(ThawRequestType _requestType) private pure returns (function(bytes32)) {
1102+
if (_requestType == ThawRequestType.Provision) {
1103+
return _deleteProvisionThawRequest;
1104+
} else if (_requestType == ThawRequestType.Delegation) {
1105+
return _deleteDelegationThawRequest;
1106+
} else {
1107+
revert("Unknown ThawRequestType");
1108+
}
1109+
}
1110+
1111+
/**
1112+
* @notice Deletes a thaw request for a provision.
1113+
* @param _thawRequestId The ID of the thaw request to delete.
1114+
*/
1115+
function _deleteProvisionThawRequest(bytes32 _thawRequestId) private {
1116+
delete _provisionThawRequests[_thawRequestId];
1117+
}
1118+
1119+
/**
1120+
* @notice Deletes a thaw request for a delegation.
1121+
* @param _thawRequestId The ID of the thaw request to delete.
10551122
*/
1056-
function _deleteThawRequest(bytes32 _thawRequestId) private {
1057-
delete _thawRequests[_thawRequestId];
1123+
function _deleteDelegationThawRequest(bytes32 _thawRequestId) private {
1124+
delete _delegationThawRequests[_thawRequestId];
10581125
}
10591126

10601127
/**

0 commit comments

Comments
 (0)