Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
a7fb875
feat: implement Indexing Agreements
matiasedgeandnode May 23, 2025
1651716
test: Add extra tests for Indexing Agreements
matiasedgeandnode Jun 16, 2025
b53ca01
fix: [TRST-H-1] IndexingAgreement.collect() on CanceledByPayer
matiasedgeandnode Jun 19, 2025
da42fb4
fix: Remove PaymentType constraint from RecurringCollector
matiasedgeandnode Jun 23, 2025
7695c9e
fix: [TRST-H-2] Only agreement owner can collect indexing fee
matiasedgeandnode Jul 21, 2025
8048c4c
fix: [TRST-H-3] collect() checks provision
matiasedgeandnode Jul 22, 2025
29dfdcc
fix: [TRST-M-1] correct TYPEHASH string for RCAU
matiasedgeandnode Jul 22, 2025
345cfc8
fix: [TRST-M-2] shared collection window logic
matiasedgeandnode Jul 23, 2025
8b2e93a
fix: [TRST-M-3] Add nonce-based replay protection
matiasedgeandnode Jul 23, 2025
aac9f8b
fix: [TRST-L-3] Add deterministic agreement ID
matiasedgeandnode Jul 23, 2025
836c0c2
fix: [TRST-L-5] Add slippage protection
matiasedgeandnode Jul 24, 2025
e3d2787
fix: [TRST-L-6] Proper agreement version check
matiasedgeandnode Jul 24, 2025
308d6e6
document: [TRST-L-7] update()
matiasedgeandnode Jul 24, 2025
17b794e
fix: [TRST-L-9] Cancel agreement if over-allocated
matiasedgeandnode Jul 24, 2025
6d9a18c
fix: [TRST-R-1] minor fixes
matiasedgeandnode Jul 24, 2025
58b7a28
fix: [TRST-R-4] CEI violation
matiasedgeandnode Jul 24, 2025
5f732ac
fix: [TRST-R-5] Terms validation
matiasedgeandnode Jul 24, 2025
b492251
fix: [TRST-R-6] Configurable indexing fees cut
matiasedgeandnode Jul 25, 2025
0e469be
fix: [TRST-M-2] improve _getCollectionInfo()
matiasedgeandnode Aug 11, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { IDataServiceFees } from "../interfaces/IDataServiceFees.sol";

import { ProvisionTracker } from "../libraries/ProvisionTracker.sol";
import { LinkedList } from "../../libraries/LinkedList.sol";
import { StakeClaims } from "../libraries/StakeClaims.sol";

import { DataService } from "../DataService.sol";
import { DataServiceFeesV1Storage } from "./DataServiceFeesStorage.sol";
Expand Down Expand Up @@ -41,23 +42,17 @@ abstract contract DataServiceFees is DataService, DataServiceFeesV1Storage, IDat
* @param _unlockTimestamp The timestamp when the tokens can be released
*/
function _lockStake(address _serviceProvider, uint256 _tokens, uint256 _unlockTimestamp) internal {
require(_tokens != 0, DataServiceFeesZeroTokens());
feesProvisionTracker.lock(_graphStaking(), _serviceProvider, _tokens, _delegationRatio);

LinkedList.List storage claimsList = claimsLists[_serviceProvider];

// Save item and add to list
bytes32 claimId = _buildStakeClaimId(_serviceProvider, claimsList.nonce);
claims[claimId] = StakeClaim({
tokens: _tokens,
createdAt: block.timestamp,
releasableAt: _unlockTimestamp,
nextClaim: bytes32(0)
});
if (claimsList.count != 0) claims[claimsList.tail].nextClaim = claimId;
claimsList.addTail(claimId);

emit StakeClaimLocked(_serviceProvider, claimId, _tokens, _unlockTimestamp);
StakeClaims.lockStake(
feesProvisionTracker,
claims,
claimsLists,
_graphStaking(),
address(this),
_delegationRatio,
_serviceProvider,
_tokens,
_unlockTimestamp
);
}

/**
Expand All @@ -80,7 +75,7 @@ abstract contract DataServiceFees is DataService, DataServiceFeesV1Storage, IDat
_numClaimsToRelease
);

emit StakeClaimsReleased(_serviceProvider, claimsReleased, abi.decode(data, (uint256)));
emit StakeClaims.StakeClaimsReleased(_serviceProvider, claimsReleased, abi.decode(data, (uint256)));
}

/**
Expand All @@ -92,23 +87,7 @@ abstract contract DataServiceFees is DataService, DataServiceFeesV1Storage, IDat
* @return The updated accumulator data
*/
function _processStakeClaim(bytes32 _claimId, bytes memory _acc) private returns (bool, bytes memory) {
StakeClaim memory claim = _getStakeClaim(_claimId);

// early exit
if (claim.releasableAt > block.timestamp) {
return (true, LinkedList.NULL_BYTES);
}

// decode
(uint256 tokensClaimed, address serviceProvider) = abi.decode(_acc, (uint256, address));

// process
feesProvisionTracker.release(serviceProvider, claim.tokens);
emit StakeClaimReleased(serviceProvider, _claimId, claim.tokens, claim.releasableAt);

// encode
_acc = abi.encode(tokensClaimed + claim.tokens, serviceProvider);
return (false, _acc);
return StakeClaims.processStakeClaim(feesProvisionTracker, claims, _claimId, _acc);
}

/**
Expand All @@ -117,18 +96,7 @@ abstract contract DataServiceFees is DataService, DataServiceFeesV1Storage, IDat
* @param _claimId The ID of the stake claim to delete
*/
function _deleteStakeClaim(bytes32 _claimId) private {
delete claims[_claimId];
}

/**
* @notice Gets the details of a stake claim
* @param _claimId The ID of the stake claim
* @return The stake claim details
*/
function _getStakeClaim(bytes32 _claimId) private view returns (StakeClaim memory) {
StakeClaim memory claim = claims[_claimId];
require(claim.createdAt != 0, DataServiceFeesClaimNotFound(_claimId));
return claim;
StakeClaims.deleteStakeClaim(claims, _claimId);
}

/**
Expand All @@ -138,16 +106,6 @@ abstract contract DataServiceFees is DataService, DataServiceFeesV1Storage, IDat
* @return The next stake claim ID
*/
function _getNextStakeClaim(bytes32 _claimId) private view returns (bytes32) {
return claims[_claimId].nextClaim;
}

/**
* @notice Builds a stake claim ID
* @param _serviceProvider The address of the service provider
* @param _nonce A nonce of the stake claim
* @return The stake claim ID
*/
function _buildStakeClaimId(address _serviceProvider, uint256 _nonce) private view returns (bytes32) {
return keccak256(abi.encodePacked(address(this), _serviceProvider, _nonce));
return StakeClaims.getNextStakeClaim(claims, _claimId);
}
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity 0.8.27;

import { IDataServiceFees } from "../interfaces/IDataServiceFees.sol";
import { StakeClaims } from "../libraries/StakeClaims.sol";

import { LinkedList } from "../../libraries/LinkedList.sol";

Expand All @@ -15,7 +15,7 @@ abstract contract DataServiceFeesV1Storage {
mapping(address serviceProvider => uint256 tokens) public feesProvisionTracker;

/// @notice List of all locked stake claims to be released to service providers
mapping(bytes32 claimId => IDataServiceFees.StakeClaim claim) public claims;
mapping(bytes32 claimId => StakeClaims.StakeClaim claim) public claims;

/// @notice Service providers registered in the data service
mapping(address serviceProvider => LinkedList.List list) public claimsLists;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,70 +22,6 @@ import { IDataService } from "./IDataService.sol";
* bugs. We may have an active bug bounty program.
*/
interface IDataServiceFees is IDataService {
/**
* @notice A stake claim, representing provisioned stake that gets locked
* to be released to a service provider.
* @dev StakeClaims are stored in linked lists by service provider, ordered by
* creation timestamp.
* @param tokens The amount of tokens to be locked in the claim
* @param createdAt The timestamp when the claim was created
* @param releasableAt The timestamp when the tokens can be released
* @param nextClaim The next claim in the linked list
*/
struct StakeClaim {
uint256 tokens;
uint256 createdAt;
uint256 releasableAt;
bytes32 nextClaim;
}

/**
* @notice Emitted when a stake claim is created and stake is locked.
* @param serviceProvider The address of the service provider
* @param claimId The id of the stake claim
* @param tokens The amount of tokens to lock in the claim
* @param unlockTimestamp The timestamp when the tokens can be released
*/
event StakeClaimLocked(
address indexed serviceProvider,
bytes32 indexed claimId,
uint256 tokens,
uint256 unlockTimestamp
);

/**
* @notice Emitted when a stake claim is released and stake is unlocked.
* @param serviceProvider The address of the service provider
* @param claimId The id of the stake claim
* @param tokens The amount of tokens released
* @param releasableAt The timestamp when the tokens were released
*/
event StakeClaimReleased(
address indexed serviceProvider,
bytes32 indexed claimId,
uint256 tokens,
uint256 releasableAt
);

/**
* @notice Emitted when a series of stake claims are released.
* @param serviceProvider The address of the service provider
* @param claimsCount The number of stake claims being released
* @param tokensReleased The total amount of tokens being released
*/
event StakeClaimsReleased(address indexed serviceProvider, uint256 claimsCount, uint256 tokensReleased);

/**
* @notice Thrown when attempting to get a stake claim that does not exist.
* @param claimId The id of the stake claim
*/
error DataServiceFeesClaimNotFound(bytes32 claimId);

/**
* @notice Emitted when trying to lock zero tokens in a stake claim
*/
error DataServiceFeesZeroTokens();

/**
* @notice Releases expired stake claims for the caller.
* @dev This function is only meant to be called if the service provider has enough
Expand Down
Loading