Skip to content

Commit 86b998b

Browse files
committed
fix: make getBalance return 0 if balance is less than thawing amount (TRST-L10)
Signed-off-by: Tomás Migone <[email protected]>
1 parent 65f4d68 commit 86b998b

File tree

6 files changed

+229
-112
lines changed

6 files changed

+229
-112
lines changed

packages/horizon/contracts/interfaces/IPaymentsEscrow.sol

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -199,6 +199,7 @@ interface IPaymentsEscrow {
199199

200200
/**
201201
* @notice Get the balance of a payer-collector-receiver tuple
202+
* This function will return 0 if the current balance is less than the amount of funds being thawed.
202203
* @param payer The address of the payer
203204
* @param collector The address of the collector
204205
* @param receiver The address of the receiver

packages/horizon/contracts/payments/PaymentsEscrow.sol

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,9 @@ contract PaymentsEscrow is Initializable, MulticallUpgradeable, GraphDirectory,
157157
*/
158158
function getBalance(address payer, address collector, address receiver) external view override returns (uint256) {
159159
EscrowAccount storage account = escrowAccounts[payer][collector][receiver];
160+
if (account.balance <= account.tokensThawing) {
161+
return 0;
162+
}
160163
return account.balance - account.tokensThawing;
161164
}
162165

packages/horizon/test/escrow/GraphEscrow.t.sol

Lines changed: 90 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,13 @@
22
pragma solidity 0.8.27;
33

44
import "forge-std/Test.sol";
5+
import { IPaymentsEscrow } from "../../contracts/interfaces/IPaymentsEscrow.sol";
6+
import { IGraphPayments } from "../../contracts/interfaces/IGraphPayments.sol";
57

68
import { HorizonStakingSharedTest } from "../shared/horizon-staking/HorizonStakingShared.t.sol";
79
import { PaymentsEscrowSharedTest } from "../shared/payments-escrow/PaymentsEscrowShared.t.sol";
810

911
contract GraphEscrowTest is HorizonStakingSharedTest, PaymentsEscrowSharedTest {
10-
1112
/*
1213
* MODIFIERS
1314
*/
@@ -39,4 +40,91 @@ contract GraphEscrowTest is HorizonStakingSharedTest, PaymentsEscrowSharedTest {
3940
function _approveEscrow(uint256 tokens) internal {
4041
token.approve(address(escrow), tokens);
4142
}
42-
}
43+
44+
function _thawEscrow(address collector, address receiver, uint256 amount) internal {
45+
(, address msgSender, ) = vm.readCallers();
46+
uint256 expectedThawEndTimestamp = block.timestamp + withdrawEscrowThawingPeriod;
47+
vm.expectEmit(address(escrow));
48+
emit IPaymentsEscrow.Thaw(msgSender, collector, receiver, amount, expectedThawEndTimestamp);
49+
escrow.thaw(collector, receiver, amount);
50+
51+
(, uint256 amountThawing, uint256 thawEndTimestamp) = escrow.escrowAccounts(msgSender, collector, receiver);
52+
assertEq(amountThawing, amount);
53+
assertEq(thawEndTimestamp, expectedThawEndTimestamp);
54+
}
55+
56+
struct CollectPaymentData {
57+
uint256 escrowBalance;
58+
uint256 paymentsBalance;
59+
uint256 receiverBalance;
60+
uint256 delegationPoolBalance;
61+
uint256 dataServiceBalance;
62+
}
63+
64+
function _collectEscrow(
65+
IGraphPayments.PaymentTypes _paymentType,
66+
address _payer,
67+
address _receiver,
68+
uint256 _tokens,
69+
address _dataService,
70+
uint256 _tokensDataService
71+
) internal {
72+
(, address _collector, ) = vm.readCallers();
73+
74+
// Previous balances
75+
(uint256 previousPayerEscrowBalance, , ) = escrow.escrowAccounts(_payer, _collector, _receiver);
76+
CollectPaymentData memory previousBalances = CollectPaymentData({
77+
escrowBalance: token.balanceOf(address(escrow)),
78+
paymentsBalance: token.balanceOf(address(payments)),
79+
receiverBalance: token.balanceOf(_receiver),
80+
delegationPoolBalance: staking.getDelegatedTokensAvailable(_receiver, _dataService),
81+
dataServiceBalance: token.balanceOf(_dataService)
82+
});
83+
84+
vm.expectEmit(address(escrow));
85+
emit IPaymentsEscrow.EscrowCollected(_payer, _collector, _receiver, _tokens);
86+
escrow.collect(_paymentType, _payer, _receiver, _tokens, _dataService, _tokensDataService);
87+
88+
// Calculate cuts
89+
uint256 protocolPaymentCut = payments.PROTOCOL_PAYMENT_CUT();
90+
uint256 delegatorCut = staking.getDelegationFeeCut(_receiver, _dataService, _paymentType);
91+
92+
// After balances
93+
(uint256 afterPayerEscrowBalance, , ) = escrow.escrowAccounts(_payer, _collector, _receiver);
94+
CollectPaymentData memory afterBalances = CollectPaymentData({
95+
escrowBalance: token.balanceOf(address(escrow)),
96+
paymentsBalance: token.balanceOf(address(payments)),
97+
receiverBalance: token.balanceOf(_receiver),
98+
delegationPoolBalance: staking.getDelegatedTokensAvailable(_receiver, _dataService),
99+
dataServiceBalance: token.balanceOf(_dataService)
100+
});
101+
102+
// Check receiver balance after payment
103+
uint256 receiverExpectedPayment = _tokens -
104+
_tokensDataService -
105+
(_tokens * protocolPaymentCut) /
106+
MAX_PPM -
107+
(_tokens * delegatorCut) /
108+
MAX_PPM;
109+
assertEq(afterBalances.receiverBalance - previousBalances.receiverBalance, receiverExpectedPayment);
110+
assertEq(token.balanceOf(address(payments)), 0);
111+
112+
// Check delegation pool balance after payment
113+
assertEq(
114+
afterBalances.delegationPoolBalance - previousBalances.delegationPoolBalance,
115+
(_tokens * delegatorCut) / MAX_PPM
116+
);
117+
118+
// Check that the escrow account has been updated
119+
assertEq(previousBalances.escrowBalance, afterBalances.escrowBalance + _tokens);
120+
121+
// Check that payments balance didn't change
122+
assertEq(previousBalances.paymentsBalance, afterBalances.paymentsBalance);
123+
124+
// Check data service balance after payment
125+
assertEq(afterBalances.dataServiceBalance - previousBalances.dataServiceBalance, _tokensDataService);
126+
127+
// Check payers escrow balance after payment
128+
assertEq(previousPayerEscrowBalance - _tokens, afterPayerEscrowBalance);
129+
}
130+
}

packages/horizon/test/escrow/collect.t.sol

Lines changed: 67 additions & 100 deletions
Original file line numberDiff line numberDiff line change
@@ -5,89 +5,10 @@ import "forge-std/Test.sol";
55

66
import { IHorizonStakingMain } from "../../contracts/interfaces/internal/IHorizonStakingMain.sol";
77
import { IGraphPayments } from "../../contracts/interfaces/IGraphPayments.sol";
8-
import { IPaymentsEscrow } from "../../contracts/interfaces/IPaymentsEscrow.sol";
98

109
import { GraphEscrowTest } from "./GraphEscrow.t.sol";
1110

1211
contract GraphEscrowCollectTest is GraphEscrowTest {
13-
14-
struct CollectPaymentData {
15-
uint256 escrowBalance;
16-
uint256 paymentsBalance;
17-
uint256 receiverBalance;
18-
uint256 delegationPoolBalance;
19-
uint256 dataServiceBalance;
20-
}
21-
22-
function _collect(
23-
IGraphPayments.PaymentTypes _paymentType,
24-
address _payer,
25-
address _receiver,
26-
uint256 _tokens,
27-
address _dataService,
28-
uint256 _tokensDataService
29-
) private {
30-
(, address _collector, ) = vm.readCallers();
31-
32-
// Previous balances
33-
(uint256 previousPayerEscrowBalance,,) = escrow.escrowAccounts(_payer, _collector, _receiver);
34-
CollectPaymentData memory previousBalances = CollectPaymentData({
35-
escrowBalance: token.balanceOf(address(escrow)),
36-
paymentsBalance: token.balanceOf(address(payments)),
37-
receiverBalance: token.balanceOf(_receiver),
38-
delegationPoolBalance: staking.getDelegatedTokensAvailable(
39-
_receiver,
40-
_dataService
41-
),
42-
dataServiceBalance: token.balanceOf(_dataService)
43-
});
44-
45-
vm.expectEmit(address(escrow));
46-
emit IPaymentsEscrow.EscrowCollected(_payer, _collector, _receiver, _tokens);
47-
escrow.collect(_paymentType, _payer, _receiver, _tokens, _dataService, _tokensDataService);
48-
49-
// Calculate cuts
50-
uint256 protocolPaymentCut = payments.PROTOCOL_PAYMENT_CUT();
51-
uint256 delegatorCut = staking.getDelegationFeeCut(
52-
_receiver,
53-
_dataService,
54-
_paymentType
55-
);
56-
57-
// After balances
58-
(uint256 afterPayerEscrowBalance,,) = escrow.escrowAccounts(_payer, _collector, _receiver);
59-
CollectPaymentData memory afterBalances = CollectPaymentData({
60-
escrowBalance: token.balanceOf(address(escrow)),
61-
paymentsBalance: token.balanceOf(address(payments)),
62-
receiverBalance: token.balanceOf(_receiver),
63-
delegationPoolBalance: staking.getDelegatedTokensAvailable(
64-
_receiver,
65-
_dataService
66-
),
67-
dataServiceBalance: token.balanceOf(_dataService)
68-
});
69-
70-
// Check receiver balance after payment
71-
uint256 receiverExpectedPayment = _tokens - _tokensDataService - _tokens * protocolPaymentCut / MAX_PPM - _tokens * delegatorCut / MAX_PPM;
72-
assertEq(afterBalances.receiverBalance - previousBalances.receiverBalance, receiverExpectedPayment);
73-
assertEq(token.balanceOf(address(payments)), 0);
74-
75-
// Check delegation pool balance after payment
76-
assertEq(afterBalances.delegationPoolBalance - previousBalances.delegationPoolBalance, _tokens * delegatorCut / MAX_PPM);
77-
78-
// Check that the escrow account has been updated
79-
assertEq(previousBalances.escrowBalance, afterBalances.escrowBalance + _tokens);
80-
81-
// Check that payments balance didn't change
82-
assertEq(previousBalances.paymentsBalance, afterBalances.paymentsBalance);
83-
84-
// Check data service balance after payment
85-
assertEq(afterBalances.dataServiceBalance - previousBalances.dataServiceBalance, _tokensDataService);
86-
87-
// Check payers escrow balance after payment
88-
assertEq(previousPayerEscrowBalance - _tokens, afterPayerEscrowBalance);
89-
}
90-
9112
/*
9213
* TESTS
9314
*/
@@ -96,9 +17,14 @@ contract GraphEscrowCollectTest is GraphEscrowTest {
9617
uint256 tokens,
9718
uint256 delegationTokens,
9819
uint256 tokensDataService
99-
) public useIndexer useProvision(tokens, 0, 0) useDelegationFeeCut(IGraphPayments.PaymentTypes.QueryFee, delegationFeeCut) {
100-
uint256 tokensProtocol = tokens * protocolPaymentCut / MAX_PPM;
101-
uint256 tokensDelegatoion = tokens * delegationFeeCut / MAX_PPM;
20+
)
21+
public
22+
useIndexer
23+
useProvision(tokens, 0, 0)
24+
useDelegationFeeCut(IGraphPayments.PaymentTypes.QueryFee, delegationFeeCut)
25+
{
26+
uint256 tokensProtocol = (tokens * protocolPaymentCut) / MAX_PPM;
27+
uint256 tokensDelegatoion = (tokens * delegationFeeCut) / MAX_PPM;
10228
vm.assume(tokensDataService < tokens - tokensProtocol - tokensDelegatoion);
10329

10430
delegationTokens = bound(delegationTokens, 1, MAX_STAKING_TOKENS);
@@ -109,55 +35,96 @@ contract GraphEscrowCollectTest is GraphEscrowTest {
10935
_depositTokens(users.verifier, users.indexer, tokens);
11036

11137
resetPrank(users.verifier);
112-
_collect(IGraphPayments.PaymentTypes.QueryFee, users.gateway, users.indexer, tokens, subgraphDataServiceAddress, tokensDataService);
38+
_collectEscrow(
39+
IGraphPayments.PaymentTypes.QueryFee,
40+
users.gateway,
41+
users.indexer,
42+
tokens,
43+
subgraphDataServiceAddress,
44+
tokensDataService
45+
);
11346
}
11447

11548
function testCollect_RevertWhen_SenderHasInsufficientAmountInEscrow(
116-
uint256 amount,
49+
uint256 amount,
11750
uint256 insufficientAmount
118-
) public useGateway useDeposit(insufficientAmount) {
51+
) public useGateway useDeposit(insufficientAmount) {
11952
vm.assume(amount > 0);
12053
vm.assume(insufficientAmount < amount);
12154

12255
vm.startPrank(users.verifier);
123-
bytes memory expectedError = abi.encodeWithSignature("PaymentsEscrowInsufficientBalance(uint256,uint256)", insufficientAmount, amount);
56+
bytes memory expectedError = abi.encodeWithSignature(
57+
"PaymentsEscrowInsufficientBalance(uint256,uint256)",
58+
insufficientAmount,
59+
amount
60+
);
12461
vm.expectRevert(expectedError);
125-
escrow.collect(IGraphPayments.PaymentTypes.QueryFee, users.gateway, users.indexer, amount, subgraphDataServiceAddress, 0);
62+
escrow.collect(
63+
IGraphPayments.PaymentTypes.QueryFee,
64+
users.gateway,
65+
users.indexer,
66+
amount,
67+
subgraphDataServiceAddress,
68+
0
69+
);
12670
vm.stopPrank();
12771
}
12872

12973
function testCollect_RevertWhen_InvalidPool(
13074
uint256 amount
131-
) public useIndexer useProvision(amount, 0, 0) useDelegationFeeCut(IGraphPayments.PaymentTypes.QueryFee, delegationFeeCut) {
75+
)
76+
public
77+
useIndexer
78+
useProvision(amount, 0, 0)
79+
useDelegationFeeCut(IGraphPayments.PaymentTypes.QueryFee, delegationFeeCut)
80+
{
13281
vm.assume(amount > 1 ether);
13382

13483
resetPrank(users.gateway);
13584
_depositTokens(users.verifier, users.indexer, amount);
13685

13786
resetPrank(users.verifier);
138-
vm.expectRevert(abi.encodeWithSelector(
139-
IHorizonStakingMain.HorizonStakingInvalidDelegationPool.selector,
87+
vm.expectRevert(
88+
abi.encodeWithSelector(
89+
IHorizonStakingMain.HorizonStakingInvalidDelegationPool.selector,
90+
users.indexer,
91+
subgraphDataServiceAddress
92+
)
93+
);
94+
escrow.collect(
95+
IGraphPayments.PaymentTypes.QueryFee,
96+
users.gateway,
14097
users.indexer,
141-
subgraphDataServiceAddress
142-
));
143-
escrow.collect(IGraphPayments.PaymentTypes.QueryFee, users.gateway, users.indexer, amount, subgraphDataServiceAddress, 1);
98+
amount,
99+
subgraphDataServiceAddress,
100+
1
101+
);
144102
}
145103

146104
function testCollect_RevertWhen_InvalidProvision(
147105
uint256 amount
148106
) public useIndexer useDelegationFeeCut(IGraphPayments.PaymentTypes.QueryFee, delegationFeeCut) {
149107
vm.assume(amount > 1 ether);
150108
vm.assume(amount <= MAX_STAKING_TOKENS);
151-
109+
152110
resetPrank(users.gateway);
153111
_depositTokens(users.verifier, users.indexer, amount);
154112

155113
resetPrank(users.verifier);
156-
vm.expectRevert(abi.encodeWithSelector(
157-
IHorizonStakingMain.HorizonStakingInvalidProvision.selector,
114+
vm.expectRevert(
115+
abi.encodeWithSelector(
116+
IHorizonStakingMain.HorizonStakingInvalidProvision.selector,
117+
users.indexer,
118+
subgraphDataServiceAddress
119+
)
120+
);
121+
escrow.collect(
122+
IGraphPayments.PaymentTypes.QueryFee,
123+
users.gateway,
158124
users.indexer,
159-
subgraphDataServiceAddress
160-
));
161-
escrow.collect(IGraphPayments.PaymentTypes.QueryFee, users.gateway, users.indexer, amount, subgraphDataServiceAddress, 1);
125+
amount,
126+
subgraphDataServiceAddress,
127+
1
128+
);
162129
}
163-
}
130+
}

0 commit comments

Comments
 (0)