Skip to content

Commit 689b6a8

Browse files
committed
test: improve coverage for GraphPayments
Signed-off-by: Tomás Migone <[email protected]>
1 parent cf17550 commit 689b6a8

File tree

7 files changed

+226
-158
lines changed

7 files changed

+226
-158
lines changed

Diff for: .gitignore

+2
Original file line numberDiff line numberDiff line change
@@ -34,8 +34,10 @@ bin/
3434
.vscode
3535

3636
# Coverage and other reports
37+
coverage/
3738
reports/
3839
coverage.json
40+
lcov.info
3941

4042
# Local test files
4143
addresses-local.json

Diff for: packages/horizon/contracts/interfaces/IGraphPayments.sol

+2-8
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ interface IGraphPayments {
2020

2121
/**
2222
* @notice Emitted when a payment is collected
23+
* @param paymentType The type of payment as defined in {IGraphPayments}
2324
* @param payer The address of the payer
2425
* @param receiver The address of the receiver
2526
* @param dataService The address of the data service
@@ -30,6 +31,7 @@ interface IGraphPayments {
3031
* @param tokensReceiver Amount of tokens for the receiver
3132
*/
3233
event GraphPaymentCollected(
34+
PaymentTypes paymentType,
3335
address indexed payer,
3436
address indexed receiver,
3537
address indexed dataService,
@@ -40,14 +42,6 @@ interface IGraphPayments {
4042
uint256 tokensReceiver
4143
);
4244

43-
/**
44-
* @notice Thrown when the calculated amount of tokens to be paid out to all parties is
45-
* not the same as the amount of tokens being collected
46-
* @param tokens The amount of tokens being collected
47-
* @param tokensCalculated The sum of all the tokens to be paid out
48-
*/
49-
error GraphPaymentsBadAccounting(uint256 tokens, uint256 tokensCalculated);
50-
5145
/**
5246
* @notice Thrown when the protocol payment cut is invalid
5347
* @param protocolPaymentCut The protocol payment cut

Diff for: packages/horizon/contracts/payments/GraphPayments.sol

+10-8
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ pragma solidity 0.8.27;
33

44
import { IGraphToken } from "@graphprotocol/contracts/contracts/token/IGraphToken.sol";
55
import { IGraphPayments } from "../interfaces/IGraphPayments.sol";
6+
import { IHorizonStakingTypes } from "../interfaces/internal/IHorizonStakingTypes.sol";
67

78
import { Initializable } from "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
89
import { MulticallUpgradeable } from "@openzeppelin/contracts-upgradeable/utils/MulticallUpgradeable.sol";
@@ -70,14 +71,14 @@ contract GraphPayments is Initializable, MulticallUpgradeable, GraphDirectory, I
7071
uint256 tokensDataService = tokensRemaining.mulPPMRoundUp(dataServiceCut);
7172
tokensRemaining = tokensRemaining - tokensDataService;
7273

73-
uint256 tokensDelegationPool = tokensRemaining.mulPPMRoundUp(
74-
_graphStaking().getDelegationFeeCut(receiver, dataService, paymentType)
75-
);
76-
tokensRemaining = tokensRemaining - tokensDelegationPool;
77-
78-
// Ensure accounting is correct
79-
uint256 tokensTotal = tokensProtocol + tokensDataService + tokensDelegationPool + tokensRemaining;
80-
require(tokens == tokensTotal, GraphPaymentsBadAccounting(tokens, tokensTotal));
74+
uint256 tokensDelegationPool = 0;
75+
IHorizonStakingTypes.DelegationPool memory pool = _graphStaking().getDelegationPool(receiver, dataService);
76+
if (pool.shares > 0) {
77+
tokensDelegationPool = tokensRemaining.mulPPMRoundUp(
78+
_graphStaking().getDelegationFeeCut(receiver, dataService, paymentType)
79+
);
80+
tokensRemaining = tokensRemaining - tokensDelegationPool;
81+
}
8182

8283
// Pay all parties
8384
_graphToken().burnTokens(tokensProtocol);
@@ -92,6 +93,7 @@ contract GraphPayments is Initializable, MulticallUpgradeable, GraphDirectory, I
9293
_graphToken().pushTokens(receiver, tokensRemaining);
9394

9495
emit GraphPaymentCollected(
96+
paymentType,
9597
msg.sender,
9698
receiver,
9799
dataService,

Diff for: packages/horizon/test/GraphBase.t.sol

+48-29
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ abstract contract GraphBaseTest is IHorizonStakingTypes, Utils, Constants {
4141
RewardsManagerMock public rewardsManager;
4242
CurationMock public curation;
4343
TAPCollector tapCollector;
44-
44+
4545
HorizonStaking private stakingBase;
4646
HorizonStakingExtension private stakingExtension;
4747

@@ -103,22 +103,34 @@ abstract contract GraphBaseTest is IHorizonStakingTypes, Utils, Constants {
103103
GraphProxy stakingProxy = new GraphProxy(address(0), address(proxyAdmin));
104104

105105
// GraphPayments predict address
106-
bytes memory paymentsParameters = abi.encode(address(controller), protocolPaymentCut);
107-
bytes memory paymentsBytecode = abi.encodePacked(
106+
bytes memory paymentsImplementationParameters = abi.encode(address(controller), protocolPaymentCut);
107+
bytes memory paymentsImplementationBytecode = abi.encodePacked(
108108
type(GraphPayments).creationCode,
109-
paymentsParameters
109+
paymentsImplementationParameters
110110
);
111-
address predictedPaymentsAddress = _computeAddress(
111+
address predictedPaymentsImplementationAddress = _computeAddress(
112112
"GraphPayments",
113-
paymentsBytecode,
113+
paymentsImplementationBytecode,
114114
users.deployer
115115
);
116-
117-
// PaymentsEscrow
118-
bytes memory escrowImplementationParameters = abi.encode(
119-
address(controller),
120-
withdrawEscrowThawingPeriod
116+
117+
bytes memory paymentsProxyParameters = abi.encode(
118+
predictedPaymentsImplementationAddress,
119+
users.governor,
120+
abi.encodeCall(GraphPayments.initialize, ())
121+
);
122+
bytes memory paymentsProxyBytecode = abi.encodePacked(
123+
type(TransparentUpgradeableProxy).creationCode,
124+
paymentsProxyParameters
125+
);
126+
address predictedPaymentsProxyAddress = _computeAddress(
127+
"TransparentUpgradeableProxy",
128+
paymentsProxyBytecode,
129+
users.deployer
121130
);
131+
132+
// PaymentsEscrow
133+
bytes memory escrowImplementationParameters = abi.encode(address(controller), withdrawEscrowThawingPeriod);
122134
bytes memory escrowImplementationBytecode = abi.encodePacked(
123135
type(PaymentsEscrow).creationCode,
124136
escrowImplementationParameters
@@ -157,29 +169,32 @@ abstract contract GraphBaseTest is IHorizonStakingTypes, Utils, Constants {
157169
resetPrank(users.governor);
158170
controller.setContractProxy(keccak256("GraphToken"), address(token));
159171
controller.setContractProxy(keccak256("PaymentsEscrow"), predictedEscrowProxyAddress);
160-
controller.setContractProxy(keccak256("GraphPayments"), predictedPaymentsAddress);
172+
controller.setContractProxy(keccak256("GraphPayments"), predictedPaymentsProxyAddress);
161173
controller.setContractProxy(keccak256("Staking"), address(stakingProxy));
162174
controller.setContractProxy(keccak256("EpochManager"), address(epochManager));
163175
controller.setContractProxy(keccak256("RewardsManager"), address(rewardsManager));
164176
controller.setContractProxy(keccak256("Curation"), address(curation));
165177
controller.setContractProxy(keccak256("GraphTokenGateway"), graphTokenGatewayAddress);
166178
controller.setContractProxy(keccak256("GraphProxyAdmin"), address(proxyAdmin));
167-
168-
resetPrank(users.deployer);
169-
address paymentsAddress = _deployContract("GraphPayments", paymentsBytecode);
170-
assertEq(paymentsAddress, predictedPaymentsAddress);
171-
payments = GraphPayments(paymentsAddress);
172-
173-
address escrowImplementationAddress = _deployContract("PaymentsEscrow", escrowImplementationBytecode);
174-
address escrowProxyAddress = _deployContract("TransparentUpgradeableProxy", escrowProxyBytecode);
175-
assertEq(escrowImplementationAddress, predictedEscrowImplementationAddress);
176-
assertEq(escrowProxyAddress, predictedEscrowProxyAddress);
177-
escrow = PaymentsEscrow(escrowProxyAddress);
178179

179-
stakingExtension = new HorizonStakingExtension(
180-
address(controller),
181-
subgraphDataServiceLegacyAddress
182-
);
180+
resetPrank(users.deployer);
181+
{
182+
address paymentsImplementationAddress = _deployContract("GraphPayments", paymentsImplementationBytecode);
183+
address paymentsProxyAddress = _deployContract("TransparentUpgradeableProxy", paymentsProxyBytecode);
184+
assertEq(paymentsImplementationAddress, predictedPaymentsImplementationAddress);
185+
assertEq(paymentsProxyAddress, predictedPaymentsProxyAddress);
186+
payments = GraphPayments(paymentsProxyAddress);
187+
}
188+
189+
{
190+
address escrowImplementationAddress = _deployContract("PaymentsEscrow", escrowImplementationBytecode);
191+
address escrowProxyAddress = _deployContract("TransparentUpgradeableProxy", escrowProxyBytecode);
192+
assertEq(escrowImplementationAddress, predictedEscrowImplementationAddress);
193+
assertEq(escrowProxyAddress, predictedEscrowProxyAddress);
194+
escrow = PaymentsEscrow(escrowProxyAddress);
195+
}
196+
197+
stakingExtension = new HorizonStakingExtension(address(controller), subgraphDataServiceLegacyAddress);
183198
stakingBase = new HorizonStaking(
184199
address(controller),
185200
address(stakingExtension),
@@ -229,7 +244,11 @@ abstract contract GraphBaseTest is IHorizonStakingTypes, Utils, Constants {
229244
* PRIVATE
230245
*/
231246

232-
function _computeAddress(string memory contractName, bytes memory bytecode, address deployer) private pure returns (address) {
247+
function _computeAddress(
248+
string memory contractName,
249+
bytes memory bytecode,
250+
address deployer
251+
) private pure returns (address) {
233252
bytes32 salt = keccak256(abi.encodePacked(contractName, "Salt"));
234253
return Create2.computeAddress(salt, keccak256(bytecode), deployer);
235254
}
@@ -238,4 +257,4 @@ abstract contract GraphBaseTest is IHorizonStakingTypes, Utils, Constants {
238257
bytes32 salt = keccak256(abi.encodePacked(contractName, "Salt"));
239258
return Create2.deploy(0, salt, bytecode);
240259
}
241-
}
260+
}

Diff for: packages/horizon/test/escrow/GraphEscrow.t.sol

+7-3
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ pragma solidity 0.8.27;
44
import "forge-std/Test.sol";
55
import { IPaymentsEscrow } from "../../contracts/interfaces/IPaymentsEscrow.sol";
66
import { IGraphPayments } from "../../contracts/interfaces/IGraphPayments.sol";
7+
import { IHorizonStakingTypes } from "../../contracts/interfaces/internal/IHorizonStakingTypes.sol";
78

89
import { HorizonStakingSharedTest } from "../shared/horizon-staking/HorizonStakingShared.t.sol";
910
import { PaymentsEscrowSharedTest } from "../shared/payments-escrow/PaymentsEscrowShared.t.sol";
@@ -112,9 +113,12 @@ contract GraphEscrowTest is HorizonStakingSharedTest, PaymentsEscrowSharedTest {
112113
uint256 tokensDataService = (_tokens - _tokens.mulPPMRoundUp(payments.PROTOCOL_PAYMENT_CUT())).mulPPMRoundUp(
113114
_dataServiceCut
114115
);
115-
uint256 tokensDelegation = (_tokens -
116-
_tokens.mulPPMRoundUp(payments.PROTOCOL_PAYMENT_CUT()) -
117-
tokensDataService).mulPPMRoundUp(staking.getDelegationFeeCut(_receiver, _dataService, _paymentType));
116+
uint256 tokensDelegation = 0;
117+
IHorizonStakingTypes.DelegationPool memory pool = staking.getDelegationPool(_receiver, _dataService);
118+
if (pool.shares > 0) {
119+
tokensDelegation = (_tokens - _tokens.mulPPMRoundUp(payments.PROTOCOL_PAYMENT_CUT()) - tokensDataService)
120+
.mulPPMRoundUp(staking.getDelegationFeeCut(_receiver, _dataService, _paymentType));
121+
}
118122
uint256 receiverExpectedPayment = _tokens -
119123
_tokens.mulPPMRoundUp(payments.PROTOCOL_PAYMENT_CUT()) -
120124
tokensDataService -

Diff for: packages/horizon/test/escrow/collect.t.sol

+31-58
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ contract GraphEscrowCollectTest is GraphEscrowTest {
1515

1616
function testCollect_Tokens(
1717
uint256 tokens,
18+
uint256 tokensToCollect,
1819
uint256 delegationTokens,
1920
uint256 dataServiceCut
2021
)
@@ -25,13 +26,43 @@ contract GraphEscrowCollectTest is GraphEscrowTest {
2526
{
2627
dataServiceCut = bound(dataServiceCut, 0, MAX_PPM);
2728
delegationTokens = bound(delegationTokens, MIN_DELEGATION, MAX_STAKING_TOKENS);
29+
tokensToCollect = bound(tokensToCollect, 1, MAX_STAKING_TOKENS);
2830

2931
resetPrank(users.delegator);
3032
_delegate(users.indexer, subgraphDataServiceAddress, delegationTokens, 0);
3133

34+
resetPrank(users.gateway);
35+
_depositTokens(users.verifier, users.indexer, tokensToCollect);
36+
37+
// burn some tokens to prevent overflow
38+
resetPrank(users.indexer);
39+
token.burn(MAX_STAKING_TOKENS);
40+
41+
resetPrank(users.verifier);
42+
_collectEscrow(
43+
IGraphPayments.PaymentTypes.QueryFee,
44+
users.gateway,
45+
users.indexer,
46+
tokensToCollect,
47+
subgraphDataServiceAddress,
48+
dataServiceCut
49+
);
50+
}
51+
52+
function testCollect_Tokens_NoProvision(
53+
uint256 tokens,
54+
uint256 dataServiceCut
55+
) public useIndexer useDelegationFeeCut(IGraphPayments.PaymentTypes.QueryFee, delegationFeeCut) {
56+
dataServiceCut = bound(dataServiceCut, 0, MAX_PPM);
57+
tokens = bound(tokens, 1, MAX_STAKING_TOKENS);
58+
3259
resetPrank(users.gateway);
3360
_depositTokens(users.verifier, users.indexer, tokens);
3461

62+
// burn some tokens to prevent overflow
63+
resetPrank(users.indexer);
64+
token.burn(MAX_STAKING_TOKENS);
65+
3566
resetPrank(users.verifier);
3667
_collectEscrow(
3768
IGraphPayments.PaymentTypes.QueryFee,
@@ -67,62 +98,4 @@ contract GraphEscrowCollectTest is GraphEscrowTest {
6798
);
6899
vm.stopPrank();
69100
}
70-
71-
function testCollect_RevertWhen_InvalidPool(
72-
uint256 amount
73-
)
74-
public
75-
useIndexer
76-
useProvision(amount, 0, 0)
77-
useDelegationFeeCut(IGraphPayments.PaymentTypes.QueryFee, delegationFeeCut)
78-
{
79-
vm.assume(amount > 1 ether);
80-
81-
resetPrank(users.gateway);
82-
_depositTokens(users.verifier, users.indexer, amount);
83-
84-
resetPrank(users.verifier);
85-
vm.expectRevert(
86-
abi.encodeWithSelector(
87-
IHorizonStakingMain.HorizonStakingInvalidDelegationPool.selector,
88-
users.indexer,
89-
subgraphDataServiceAddress
90-
)
91-
);
92-
escrow.collect(
93-
IGraphPayments.PaymentTypes.QueryFee,
94-
users.gateway,
95-
users.indexer,
96-
amount,
97-
subgraphDataServiceAddress,
98-
1
99-
);
100-
}
101-
102-
function testCollect_RevertWhen_InvalidProvision(
103-
uint256 amount
104-
) public useIndexer useDelegationFeeCut(IGraphPayments.PaymentTypes.QueryFee, delegationFeeCut) {
105-
vm.assume(amount > 1 ether);
106-
vm.assume(amount <= MAX_STAKING_TOKENS);
107-
108-
resetPrank(users.gateway);
109-
_depositTokens(users.verifier, users.indexer, amount);
110-
111-
resetPrank(users.verifier);
112-
vm.expectRevert(
113-
abi.encodeWithSelector(
114-
IHorizonStakingMain.HorizonStakingInvalidProvision.selector,
115-
users.indexer,
116-
subgraphDataServiceAddress
117-
)
118-
);
119-
escrow.collect(
120-
IGraphPayments.PaymentTypes.QueryFee,
121-
users.gateway,
122-
users.indexer,
123-
amount,
124-
subgraphDataServiceAddress,
125-
1
126-
);
127-
}
128101
}

0 commit comments

Comments
 (0)