Skip to content

Commit 4bbbc66

Browse files
committed
staking: fix edge rebate when all allocations collected zero fees
1 parent c26e2fd commit 4bbbc66

File tree

3 files changed

+61
-77
lines changed

3 files changed

+61
-77
lines changed

contracts/staking/libs/Rebates.sol

Lines changed: 17 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -81,22 +81,25 @@ library Rebates {
8181
uint256 _indexerFees,
8282
uint256 _indexerEffectiveAllocatedStake
8383
) internal returns (uint256) {
84+
uint256 rebateReward = 0;
85+
8486
// Calculate the rebate rewards for the indexer
85-
uint256 totalRewards = pool.fees;
86-
uint256 rebateReward = LibCobbDouglas.cobbDouglas(
87-
totalRewards,
88-
_indexerFees,
89-
pool.fees,
90-
_indexerEffectiveAllocatedStake,
91-
pool.effectiveAllocatedStake,
92-
pool.alphaNumerator,
93-
pool.alphaDenominator
94-
);
87+
if (pool.fees > 0) {
88+
rebateReward = LibCobbDouglas.cobbDouglas(
89+
pool.fees, // totalRewards
90+
_indexerFees,
91+
pool.fees,
92+
_indexerEffectiveAllocatedStake,
93+
pool.effectiveAllocatedStake,
94+
pool.alphaNumerator,
95+
pool.alphaDenominator
96+
);
9597

96-
// Under NO circumstance we will reward more than total fees in the pool
97-
uint256 _unclaimedFees = pool.fees.sub(pool.claimedRewards);
98-
if (rebateReward > _unclaimedFees) {
99-
rebateReward = _unclaimedFees;
98+
// Under NO circumstance we will reward more than total fees in the pool
99+
uint256 _unclaimedFees = pool.fees.sub(pool.claimedRewards);
100+
if (rebateReward > _unclaimedFees) {
101+
rebateReward = _unclaimedFees;
102+
}
100103
}
101104

102105
// Update pool state

contracts/tests/RebatePoolMock.sol

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,10 @@ contract RebatePoolMock {
4646
uint32 _alphaNumerator,
4747
uint32 _alphaDenominator
4848
) external pure returns (uint256) {
49+
if (_totalFees == 0) {
50+
return 0;
51+
}
52+
4953
return
5054
LibCobbDouglas.cobbDouglas(
5155
_totalRewards,

test/staking/rebate.test.ts

Lines changed: 40 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,14 @@ describe('Staking:Rebate', () => {
3737
{ totalRewards: 1400, fees: 0, totalFees: 1400, stake: 1200, totalStake: 7300 },
3838
]
3939

40+
// Edge case #1 - No closed allocations any query fees
41+
const edgeCases1: RebateTestCase[] = [
42+
{ totalRewards: 0, fees: 0, totalFees: 0, stake: 5000, totalStake: 7300 },
43+
{ totalRewards: 0, fees: 0, totalFees: 0, stake: 600, totalStake: 7300 },
44+
{ totalRewards: 0, fees: 0, totalFees: 0, stake: 500, totalStake: 7300 },
45+
{ totalRewards: 0, fees: 0, totalFees: 0, stake: 1200, totalStake: 7300 },
46+
]
47+
4048
// This function calculates the Cobb-Douglas formula in Typescript so we can compare against
4149
// the Solidity implementation
4250
// TODO: consider using bignumber.js to get extra precision
@@ -49,6 +57,9 @@ describe('Staking:Rebate', () => {
4957
alphaNumerator: number,
5058
alphaDenominator: number,
5159
) {
60+
if (totalFees === 0) {
61+
return 0
62+
}
5263
const feeRatio = fees / totalFees
5364
const stakeRatio = stake / totalStake
5465
const alpha = alphaNumerator / alphaDenominator
@@ -143,107 +154,73 @@ describe('Staking:Rebate', () => {
143154
return rx.events[0].args[0]
144155
}
145156

146-
beforeEach(async function () {
147-
;[deployer, other] = await getAccounts()
148-
rebatePoolMock = ((await deployContract(
149-
'RebatePoolMock',
150-
deployer.signer,
151-
)) as unknown) as RebatePoolMock
152-
})
153-
154-
describe('should match cobb-douglas Solidity implementation', function () {
157+
async function testAlphas(fn, testCases) {
155158
// Typical alpha
156159
it('alpha 0.90', async function () {
157160
const alpha: RebateRatio = [90, 100]
158-
await shouldMatchFormulas(testCases, alpha)
161+
await fn(testCases, alpha)
159162
})
160163

161164
// Typical alpha
162165
it('alpha 0.25', async function () {
163166
const alpha: RebateRatio = [1, 4]
164-
await shouldMatchFormulas(testCases, alpha)
167+
await fn(testCases, alpha)
165168
})
166169

167170
// Periodic alpha
168171
it('alpha 0.33~', async function () {
169172
const alpha: RebateRatio = [1, 3]
170-
await shouldMatchFormulas(testCases, alpha)
173+
await fn(testCases, alpha)
171174
})
172175

173176
// Small alpha
174177
it('alpha 0.005', async function () {
175178
const alpha: RebateRatio = [1, 200]
176-
await shouldMatchFormulas(testCases, alpha)
179+
await fn(testCases, alpha)
177180
})
178181

179182
// Edge alpha
180183
it('alpha 1', async function () {
181184
const alpha: RebateRatio = [1, 1]
182-
await shouldMatchFormulas(testCases, alpha)
185+
await fn(testCases, alpha)
183186
})
184-
})
187+
}
185188

186-
describe('should match rewards out from rebates', function () {
187-
// Typical alpha
188-
it('alpha 0.90', async function () {
189-
const alpha: RebateRatio = [90, 100]
190-
await shouldMatchOut(testCases, alpha)
191-
})
189+
beforeEach(async function () {
190+
;[deployer, other] = await getAccounts()
191+
rebatePoolMock = ((await deployContract(
192+
'RebatePoolMock',
193+
deployer.signer,
194+
)) as unknown) as RebatePoolMock
195+
})
192196

193-
// Typical alpha
194-
it('alpha 0.25', async function () {
195-
const alpha: RebateRatio = [1, 4]
196-
await shouldMatchOut(testCases, alpha)
197+
describe('should match cobb-douglas Solidity implementation', function () {
198+
describe('normal test case', function () {
199+
testAlphas(shouldMatchFormulas, testCases)
197200
})
198201

199-
// Periodic alpha
200-
it('alpha 0.33~', async function () {
201-
const alpha: RebateRatio = [1, 3]
202-
await shouldMatchOut(testCases, alpha)
202+
describe('edge #1 test case', function () {
203+
testAlphas(shouldMatchFormulas, edgeCases1)
203204
})
205+
})
204206

205-
// Small alpha
206-
it('alpha 0.005', async function () {
207-
const alpha: RebateRatio = [1, 200]
208-
await shouldMatchOut(testCases, alpha)
207+
describe('should match rewards out from rebates', function () {
208+
describe('normal test case', function () {
209+
testAlphas(shouldMatchOut, testCases)
209210
})
210211

211-
// Edge alpha
212-
it('alpha 1', async function () {
213-
const alpha: RebateRatio = [1, 1]
214-
await shouldMatchOut(testCases, alpha)
212+
describe('edge #1 test case', function () {
213+
testAlphas(shouldMatchOut, edgeCases1)
215214
})
216215
})
217216

218217
describe('should always be that sum of rebate rewards obtained <= to total rewards', function () {
219-
// Typical alpha
220-
it('alpha 0.90', async function () {
221-
const alpha: RebateRatio = [90, 100]
222-
await shouldConserveBalances(testCases, alpha)
218+
describe('normal test case', function () {
219+
testAlphas(shouldConserveBalances, testCases)
223220
})
224221

225-
// Typical alpha
226-
it('alpha 0.25', async function () {
227-
const alpha: RebateRatio = [1, 4]
228-
await shouldConserveBalances(testCases, alpha)
229-
})
230-
231-
// Periodic alpha
232-
it('alpha 0.33~', async function () {
233-
const alpha: RebateRatio = [1, 3]
234-
await shouldConserveBalances(testCases, alpha)
235-
})
236-
237-
// Small alpha
238-
it('alpha 0.005', async function () {
239-
const alpha: RebateRatio = [1, 200]
240-
await shouldConserveBalances(testCases, alpha)
241-
})
242-
243-
// Edge alpha
244-
it('alpha 1', async function () {
245-
const alpha: RebateRatio = [1, 1]
246-
await shouldConserveBalances(testCases, alpha)
222+
describe('edge #1 test case', function () {
223+
testAlphas(shouldConserveBalances, edgeCases1)
247224
})
248225
})
249226
})

0 commit comments

Comments
 (0)