Skip to content

Commit c9e8ffa

Browse files
committed
feat(warm-storage): validate operator approvals in dataSetCreated
Add comprehensive validation of operator approval settings alongside existing minimum balance check during createDataSet operation. Validates operator is approved with sufficient rate allowance, lockup allowance, and max lockup period.
1 parent 7e94755 commit c9e8ffa

File tree

4 files changed

+470
-9
lines changed

4 files changed

+470
-9
lines changed

service_contracts/abi/FilecoinWarmStorageService.abi.json

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1712,6 +1712,94 @@
17121712
}
17131713
]
17141714
},
1715+
{
1716+
"type": "error",
1717+
"name": "InsufficientLockupAllowance",
1718+
"inputs": [
1719+
{
1720+
"name": "payer",
1721+
"type": "address",
1722+
"internalType": "address"
1723+
},
1724+
{
1725+
"name": "operator",
1726+
"type": "address",
1727+
"internalType": "address"
1728+
},
1729+
{
1730+
"name": "lockupAllowance",
1731+
"type": "uint256",
1732+
"internalType": "uint256"
1733+
},
1734+
{
1735+
"name": "lockupUsage",
1736+
"type": "uint256",
1737+
"internalType": "uint256"
1738+
},
1739+
{
1740+
"name": "minimumLockupRequired",
1741+
"type": "uint256",
1742+
"internalType": "uint256"
1743+
}
1744+
]
1745+
},
1746+
{
1747+
"type": "error",
1748+
"name": "InsufficientMaxLockupPeriod",
1749+
"inputs": [
1750+
{
1751+
"name": "payer",
1752+
"type": "address",
1753+
"internalType": "address"
1754+
},
1755+
{
1756+
"name": "operator",
1757+
"type": "address",
1758+
"internalType": "address"
1759+
},
1760+
{
1761+
"name": "maxLockupPeriod",
1762+
"type": "uint256",
1763+
"internalType": "uint256"
1764+
},
1765+
{
1766+
"name": "requiredLockupPeriod",
1767+
"type": "uint256",
1768+
"internalType": "uint256"
1769+
}
1770+
]
1771+
},
1772+
{
1773+
"type": "error",
1774+
"name": "InsufficientRateAllowance",
1775+
"inputs": [
1776+
{
1777+
"name": "payer",
1778+
"type": "address",
1779+
"internalType": "address"
1780+
},
1781+
{
1782+
"name": "operator",
1783+
"type": "address",
1784+
"internalType": "address"
1785+
},
1786+
{
1787+
"name": "rateAllowance",
1788+
"type": "uint256",
1789+
"internalType": "uint256"
1790+
},
1791+
{
1792+
"name": "rateUsage",
1793+
"type": "uint256",
1794+
"internalType": "uint256"
1795+
},
1796+
{
1797+
"name": "minimumRateRequired",
1798+
"type": "uint256",
1799+
"internalType": "uint256"
1800+
}
1801+
]
1802+
},
17151803
{
17161804
"type": "error",
17171805
"name": "InvalidChallengeCount",
@@ -1988,6 +2076,22 @@
19882076
}
19892077
]
19902078
},
2079+
{
2080+
"type": "error",
2081+
"name": "OperatorNotApproved",
2082+
"inputs": [
2083+
{
2084+
"name": "payer",
2085+
"type": "address",
2086+
"internalType": "address"
2087+
},
2088+
{
2089+
"name": "operator",
2090+
"type": "address",
2091+
"internalType": "address"
2092+
}
2093+
]
2094+
},
19912095
{
19922096
"type": "error",
19932097
"name": "OwnableInvalidOwner",

service_contracts/src/Errors.sol

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -292,4 +292,46 @@ library Errors {
292292
/// @param minimumRequired The minimum lockup required to cover the minimum storage rate
293293
/// @param available The available funds in the payer's account
294294
error InsufficientFundsForMinimumRate(address payer, uint256 minimumRequired, uint256 available);
295+
296+
/// @notice Operator is not approved for the payer
297+
/// @param payer The payer address
298+
/// @param operator The operator address (warm storage service)
299+
error OperatorNotApproved(address payer, address operator);
300+
301+
/// @notice Operator has insufficient rate allowance for the minimum storage rate
302+
/// @param payer The payer address
303+
/// @param operator The operator address (warm storage service)
304+
/// @param rateAllowance The total rate allowance approved
305+
/// @param rateUsage The current rate usage
306+
/// @param minimumRateRequired The minimum rate required per epoch
307+
error InsufficientRateAllowance(
308+
address payer,
309+
address operator,
310+
uint256 rateAllowance,
311+
uint256 rateUsage,
312+
uint256 minimumRateRequired
313+
);
314+
315+
/// @notice Operator has insufficient lockup allowance for the minimum lockup
316+
/// @param payer The payer address
317+
/// @param operator The operator address (warm storage service)
318+
/// @param lockupAllowance The total lockup allowance approved
319+
/// @param lockupUsage The current lockup usage
320+
/// @param minimumLockupRequired The minimum lockup required
321+
error InsufficientLockupAllowance(
322+
address payer,
323+
address operator,
324+
uint256 lockupAllowance,
325+
uint256 lockupUsage,
326+
uint256 minimumLockupRequired
327+
);
328+
329+
/// @notice Operator's max lockup period is insufficient for the default lockup period
330+
/// @param payer The payer address
331+
/// @param operator The operator address (warm storage service)
332+
/// @param maxLockupPeriod The maximum lockup period approved
333+
/// @param requiredLockupPeriod The required lockup period
334+
error InsufficientMaxLockupPeriod(
335+
address payer, address operator, uint256 maxLockupPeriod, uint256 requiredLockupPeriod
336+
);
295337
}

service_contracts/src/FilecoinWarmStorageService.sol

Lines changed: 45 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -587,15 +587,8 @@ contract FilecoinWarmStorageService is
587587
// Create the payment rails using the FilecoinPayV1 contract
588588
FilecoinPayV1 payments = FilecoinPayV1(paymentsContractAddress);
589589

590-
// Pre-check that payer has sufficient available funds to cover minimum storage rate
591-
// This provides early feedback but doesn't guarantee funds will be available when SP calls nextProvingPeriod
592-
(,, uint256 availableFunds,) = payments.getAccountInfoIfSettled(usdfcTokenAddress, createData.payer);
593-
// Calculate required lockup: multiply first to preserve precision
594-
uint256 minimumLockupRequired = (MINIMUM_STORAGE_RATE_PER_MONTH * DEFAULT_LOCKUP_PERIOD) / EPOCHS_PER_MONTH;
595-
require(
596-
availableFunds >= minimumLockupRequired,
597-
Errors.InsufficientFundsForMinimumRate(createData.payer, minimumLockupRequired, availableFunds)
598-
);
590+
// Validate payer has sufficient funds and operator approvals for minimum pricing
591+
validatePayerOperatorApprovalAndFunds(payments, createData.payer);
599592

600593
uint256 pdpRailId = payments.createRail(
601594
usdfcTokenAddress, // token address
@@ -1109,6 +1102,49 @@ contract FilecoinWarmStorageService is
11091102
}
11101103
}
11111104

1105+
/// @notice Validates that the payer has sufficient funds and operator approvals for minimum pricing
1106+
/// @param payments The FilecoinPayV1 contract instance
1107+
/// @param payer The address of the payer
1108+
function validatePayerOperatorApprovalAndFunds(FilecoinPayV1 payments, address payer) internal view {
1109+
// Calculate required lockup for minimum pricing
1110+
uint256 minimumLockupRequired = (MINIMUM_STORAGE_RATE_PER_MONTH * DEFAULT_LOCKUP_PERIOD) / EPOCHS_PER_MONTH;
1111+
1112+
// Check that payer has sufficient available funds
1113+
(,, uint256 availableFunds,) = payments.getAccountInfoIfSettled(usdfcTokenAddress, payer);
1114+
require(
1115+
availableFunds >= minimumLockupRequired,
1116+
Errors.InsufficientFundsForMinimumRate(payer, minimumLockupRequired, availableFunds)
1117+
);
1118+
1119+
// Check operator approval settings
1120+
(bool isApproved, uint256 rateAllowance, uint256 lockupAllowance, uint256 rateUsage, uint256 lockupUsage, uint256 maxLockupPeriod) =
1121+
payments.operatorApprovals(usdfcTokenAddress, payer, address(this));
1122+
1123+
// Verify operator is approved
1124+
require(isApproved, Errors.OperatorNotApproved(payer, address(this)));
1125+
1126+
// Calculate minimum rate per epoch
1127+
uint256 minimumRatePerEpoch = MINIMUM_STORAGE_RATE_PER_MONTH / EPOCHS_PER_MONTH;
1128+
1129+
// Verify rate allowance is sufficient
1130+
require(
1131+
rateAllowance >= rateUsage + minimumRatePerEpoch,
1132+
Errors.InsufficientRateAllowance(payer, address(this), rateAllowance, rateUsage, minimumRatePerEpoch)
1133+
);
1134+
1135+
// Verify lockup allowance is sufficient
1136+
require(
1137+
lockupAllowance >= lockupUsage + minimumLockupRequired,
1138+
Errors.InsufficientLockupAllowance(payer, address(this), lockupAllowance, lockupUsage, minimumLockupRequired)
1139+
);
1140+
1141+
// Verify max lockup period is sufficient
1142+
require(
1143+
maxLockupPeriod >= DEFAULT_LOCKUP_PERIOD,
1144+
Errors.InsufficientMaxLockupPeriod(payer, address(this), maxLockupPeriod, DEFAULT_LOCKUP_PERIOD)
1145+
);
1146+
}
1147+
11121148
function updatePaymentRates(uint256 dataSetId, uint256 leafCount) internal {
11131149
// Revert if no payment rail is configured for this data set
11141150
require(dataSetInfo[dataSetId].pdpRailId != 0, Errors.NoPDPPaymentRail(dataSetId));

0 commit comments

Comments
 (0)