Skip to content

Commit 975e6db

Browse files
committed
test: improve coverage for GraphPayments
Signed-off-by: Tomás Migone <[email protected]>
1 parent 5d5a47a commit 975e6db

File tree

7 files changed

+226
-159
lines changed

7 files changed

+226
-159
lines changed

.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

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

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,

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

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

104104
// GraphPayments predict address
105-
bytes memory paymentsParameters = abi.encode(address(controller), protocolPaymentCut);
106-
bytes memory paymentsBytecode = abi.encodePacked(
105+
bytes memory paymentsImplementationParameters = abi.encode(address(controller), protocolPaymentCut);
106+
bytes memory paymentsImplementationBytecode = abi.encodePacked(
107107
type(GraphPayments).creationCode,
108-
paymentsParameters
108+
paymentsImplementationParameters
109109
);
110-
address predictedPaymentsAddress = _computeAddress(
110+
address predictedPaymentsImplementationAddress = _computeAddress(
111111
"GraphPayments",
112-
paymentsBytecode,
112+
paymentsImplementationBytecode,
113113
users.deployer
114114
);
115-
116-
// PaymentsEscrow
117-
bytes memory escrowImplementationParameters = abi.encode(
118-
address(controller),
119-
withdrawEscrowThawingPeriod
115+
116+
bytes memory paymentsProxyParameters = abi.encode(
117+
predictedPaymentsImplementationAddress,
118+
users.governor,
119+
abi.encodeCall(GraphPayments.initialize, ())
120+
);
121+
bytes memory paymentsProxyBytecode = abi.encodePacked(
122+
type(TransparentUpgradeableProxy).creationCode,
123+
paymentsProxyParameters
124+
);
125+
address predictedPaymentsProxyAddress = _computeAddress(
126+
"TransparentUpgradeableProxy",
127+
paymentsProxyBytecode,
128+
users.deployer
120129
);
130+
131+
// PaymentsEscrow
132+
bytes memory escrowImplementationParameters = abi.encode(address(controller), withdrawEscrowThawingPeriod);
121133
bytes memory escrowImplementationBytecode = abi.encodePacked(
122134
type(PaymentsEscrow).creationCode,
123135
escrowImplementationParameters
@@ -156,29 +168,32 @@ abstract contract GraphBaseTest is IHorizonStakingTypes, Utils, Constants {
156168
resetPrank(users.governor);
157169
controller.setContractProxy(keccak256("GraphToken"), address(token));
158170
controller.setContractProxy(keccak256("PaymentsEscrow"), predictedEscrowProxyAddress);
159-
controller.setContractProxy(keccak256("GraphPayments"), predictedPaymentsAddress);
171+
controller.setContractProxy(keccak256("GraphPayments"), predictedPaymentsProxyAddress);
160172
controller.setContractProxy(keccak256("Staking"), address(stakingProxy));
161173
controller.setContractProxy(keccak256("EpochManager"), address(epochManager));
162174
controller.setContractProxy(keccak256("RewardsManager"), address(rewardsManager));
163175
controller.setContractProxy(keccak256("Curation"), address(curation));
164176
controller.setContractProxy(keccak256("GraphTokenGateway"), graphTokenGatewayAddress);
165177
controller.setContractProxy(keccak256("GraphProxyAdmin"), address(proxyAdmin));
166-
167-
resetPrank(users.deployer);
168-
address paymentsAddress = _deployContract("GraphPayments", paymentsBytecode);
169-
assertEq(paymentsAddress, predictedPaymentsAddress);
170-
payments = GraphPayments(paymentsAddress);
171-
172-
address escrowImplementationAddress = _deployContract("PaymentsEscrow", escrowImplementationBytecode);
173-
address escrowProxyAddress = _deployContract("TransparentUpgradeableProxy", escrowProxyBytecode);
174-
assertEq(escrowImplementationAddress, predictedEscrowImplementationAddress);
175-
assertEq(escrowProxyAddress, predictedEscrowProxyAddress);
176-
escrow = PaymentsEscrow(escrowProxyAddress);
177178

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

231-
function _computeAddress(string memory contractName, bytes memory bytecode, address deployer) private pure returns (address) {
246+
function _computeAddress(
247+
string memory contractName,
248+
bytes memory bytecode,
249+
address deployer
250+
) private pure returns (address) {
232251
bytes32 salt = keccak256(abi.encodePacked(contractName, "Salt"));
233252
return Create2.computeAddress(salt, keccak256(bytecode), deployer);
234253
}
@@ -237,4 +256,4 @@ abstract contract GraphBaseTest is IHorizonStakingTypes, Utils, Constants {
237256
bytes32 salt = keccak256(abi.encodePacked(contractName, "Salt"));
238257
return Create2.deploy(0, salt, bytecode);
239258
}
240-
}
259+
}

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 -

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, 1, 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)