Skip to content

Commit 0eb2fa1

Browse files
committed
fix: send the right amount for fixed lambda
1 parent a6b052d commit 0eb2fa1

File tree

3 files changed

+171
-3
lines changed

3 files changed

+171
-3
lines changed

contracts/reservoir/L1Reservoir.sol

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -128,10 +128,11 @@ contract L1Reservoir is L1ReservoirV1Storage, Reservoir {
128128
graphToken().mint(address(this), tokensToMint);
129129
}
130130

131-
uint256 tokensToSendToL2 = nextL2RewardsFraction.mul(newRewardsToDistribute).div(
132-
TOKEN_DECIMALS
133-
);
131+
uint256 tokensToSendToL2 = 0;
134132
if (l2RewardsFraction != nextL2RewardsFraction) {
133+
tokensToSendToL2 = nextL2RewardsFraction.mul(newRewardsToDistribute).div(
134+
TOKEN_DECIMALS
135+
);
135136
if (mintedRewardsTotal > mintedRewardsActual) {
136137
// eps > 0, i.e. t < t1_old
137138
// Note this can fail if the old l2RewardsFraction is larger
@@ -158,6 +159,7 @@ contract L1Reservoir is L1ReservoirV1Storage, Reservoir {
158159
l2MaxSubmissionCost
159160
);
160161
} else if (l2RewardsFraction > 0) {
162+
tokensToSendToL2 = tokensToMint.mul(l2RewardsFraction).div(TOKEN_DECIMALS);
161163
_sendNewTokensAndStateToL2(
162164
tokensToSendToL2,
163165
l2MaxGas,

test/l2/l2Reservoir.test.ts

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,17 @@ describe('L2Reservoir', () => {
145145
await fixture.tearDown()
146146
})
147147

148+
describe('setNextDripNonce', async function () {
149+
it('rejects unauthorized calls', async function () {
150+
const tx = l2Reservoir.connect(testAccount1.signer).setNextDripNonce(toBN('10'))
151+
await expect(tx).revertedWith('Caller must be Controller governor')
152+
})
153+
it('sets the next expected drip nonce', async function () {
154+
const tx = l2Reservoir.connect(governor.signer).setNextDripNonce(toBN('10'))
155+
await expect(tx).emit(l2Reservoir, 'NextDripNonceUpdated').withArgs(toBN('10'))
156+
await expect(await l2Reservoir.nextDripNonce()).to.eq(toBN('10'))
157+
})
158+
})
148159
describe('receiveDrip', async function () {
149160
it('rejects the call when not called by the gateway', async function () {
150161
const tx = l2Reservoir
@@ -215,6 +226,61 @@ describe('L2Reservoir', () => {
215226
await expect(tx).emit(l2Reservoir, 'DripReceived').withArgs(dripNormalizedSupply.add(1))
216227
await expect(await grt.balanceOf(l2Reservoir.address)).to.eq(dripAmount.mul(2))
217228
})
229+
it('accepts subsequent calls without changing issuance rate', async function () {
230+
normalizedSupply = dripNormalizedSupply
231+
let receiveDripTx = await l2Reservoir.populateTransaction.receiveDrip(
232+
dripNormalizedSupply,
233+
dripIssuanceRate,
234+
toBN('0'),
235+
)
236+
let tx = await validGatewayFinalizeTransfer(receiveDripTx.data)
237+
dripBlock = await latestBlock()
238+
await expect(await l2Reservoir.normalizedTokenSupplyCache()).to.eq(dripNormalizedSupply)
239+
await expect(await l2Reservoir.issuanceRate()).to.eq(dripIssuanceRate)
240+
await expect(tx).emit(l2Reservoir, 'DripReceived').withArgs(dripNormalizedSupply)
241+
242+
receiveDripTx = await l2Reservoir.populateTransaction.receiveDrip(
243+
dripNormalizedSupply.add(1),
244+
dripIssuanceRate,
245+
toBN('1'),
246+
)
247+
tx = await gatewayFinalizeTransfer(receiveDripTx.data)
248+
dripBlock = await latestBlock()
249+
await expect(await l2Reservoir.normalizedTokenSupplyCache()).to.eq(
250+
dripNormalizedSupply.add(1),
251+
)
252+
await expect(await l2Reservoir.issuanceRate()).to.eq(dripIssuanceRate)
253+
await expect(tx).emit(l2Reservoir, 'DripReceived').withArgs(dripNormalizedSupply.add(1))
254+
await expect(await grt.balanceOf(l2Reservoir.address)).to.eq(dripAmount.mul(2))
255+
})
256+
it('accepts a different nonce set through setNextDripNonce', async function () {
257+
normalizedSupply = dripNormalizedSupply
258+
let receiveDripTx = await l2Reservoir.populateTransaction.receiveDrip(
259+
dripNormalizedSupply,
260+
dripIssuanceRate,
261+
toBN('0'),
262+
)
263+
let tx = await validGatewayFinalizeTransfer(receiveDripTx.data)
264+
dripBlock = await latestBlock()
265+
await expect(await l2Reservoir.normalizedTokenSupplyCache()).to.eq(dripNormalizedSupply)
266+
await expect(await l2Reservoir.issuanceRate()).to.eq(dripIssuanceRate)
267+
await expect(tx).emit(l2Reservoir, 'DripReceived').withArgs(dripNormalizedSupply)
268+
269+
await l2Reservoir.connect(governor.signer).setNextDripNonce(toBN('2'))
270+
receiveDripTx = await l2Reservoir.populateTransaction.receiveDrip(
271+
dripNormalizedSupply.add(1),
272+
dripIssuanceRate,
273+
toBN('2'),
274+
)
275+
tx = await gatewayFinalizeTransfer(receiveDripTx.data)
276+
dripBlock = await latestBlock()
277+
await expect(await l2Reservoir.normalizedTokenSupplyCache()).to.eq(
278+
dripNormalizedSupply.add(1),
279+
)
280+
await expect(await l2Reservoir.issuanceRate()).to.eq(dripIssuanceRate)
281+
await expect(tx).emit(l2Reservoir, 'DripReceived').withArgs(dripNormalizedSupply.add(1))
282+
await expect(await grt.balanceOf(l2Reservoir.address)).to.eq(dripAmount.mul(2))
283+
})
218284
})
219285

220286
context('calculating rewards', async function () {

test/reservoir/l1Reservoir.test.ts

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -498,6 +498,106 @@ describe('L1Reservoir', () => {
498498
.div(2)
499499
.add(expectedTotalRewards.sub(rewardsUntilSecondDripBlock).mul(8).div(10))
500500

501+
const tx2 = await l1Reservoir
502+
.connect(governor.signer)
503+
.drip(maxGas, gasPriceBid, maxSubmissionCost, { value: defaultEthValue })
504+
const newActualAmount = await grt.balanceOf(l1Reservoir.address)
505+
const newEscrowedAmount = await grt.balanceOf(bridgeEscrow.address)
506+
expect(toRound(newActualAmount)).to.eq(
507+
toRound(expectedTotalRewards.sub(expectedNewTotalSentToL2)),
508+
)
509+
expect(toRound((await grt.totalSupply()).sub(supplyBeforeDrip))).to.eq(
510+
toRound(expectedNewMintedAmount),
511+
)
512+
expect(toRound(newEscrowedAmount)).to.eq(toRound(expectedNewTotalSentToL2))
513+
normalizedTokenSupply = (await l1Reservoir.tokenSupplyCache())
514+
.mul(await l1Reservoir.l2RewardsFraction())
515+
.div(toGRT('1'))
516+
expectedCallhookData = l2ReservoirIface.encodeFunctionData('receiveDrip', [
517+
normalizedTokenSupply,
518+
issuanceRate,
519+
toBN('1'), // Incremented nonce
520+
])
521+
expectedL2Data = await l1GraphTokenGateway.getOutboundCalldata(
522+
grt.address,
523+
l1Reservoir.address,
524+
mockL2Reservoir.address,
525+
newEscrowedAmount.sub(escrowedAmount),
526+
expectedCallhookData,
527+
)
528+
await expect(tx2)
529+
.emit(l1GraphTokenGateway, 'TxToL2')
530+
.withArgs(l1Reservoir.address, mockL2Gateway.address, toBN(2), expectedL2Data)
531+
await expect(tx2)
532+
.emit(l1Reservoir, 'RewardsDripped')
533+
.withArgs(
534+
newActualAmount.add(newEscrowedAmount).sub(actualAmount.add(escrowedAmount)),
535+
newEscrowedAmount.sub(escrowedAmount),
536+
expectedNewNextDeadline,
537+
)
538+
})
539+
it('sends the outstanding amount if the L2 rewards fraction stays constant', async function () {
540+
await l1Reservoir.connect(governor.signer).setL2RewardsFraction(toGRT('0.5'))
541+
await l1Reservoir.connect(governor.signer).initialSnapshot(toBN(0))
542+
supplyBeforeDrip = await grt.totalSupply()
543+
const startAccrued = await l1Reservoir.getAccumulatedRewards(await latestBlock())
544+
expect(startAccrued).to.eq(0)
545+
const dripBlock = (await latestBlock()).add(1) // We're gonna drip in the next transaction
546+
const tracker = await RewardsTracker.create(
547+
supplyBeforeDrip,
548+
defaults.rewards.issuanceRate,
549+
dripBlock,
550+
)
551+
expect(await tracker.accRewards(dripBlock)).to.eq(0)
552+
const expectedNextDeadline = dripBlock.add(defaults.rewards.dripInterval)
553+
const expectedMintedAmount = await tracker.accRewards(expectedNextDeadline)
554+
const expectedSentToL2 = expectedMintedAmount.div(2)
555+
const tx = await l1Reservoir
556+
.connect(governor.signer)
557+
.drip(maxGas, gasPriceBid, maxSubmissionCost, { value: defaultEthValue })
558+
const actualAmount = await grt.balanceOf(l1Reservoir.address)
559+
const escrowedAmount = await grt.balanceOf(bridgeEscrow.address)
560+
expect(toRound(actualAmount)).to.eq(toRound(expectedMintedAmount.sub(expectedSentToL2)))
561+
expect(toRound((await grt.totalSupply()).sub(supplyBeforeDrip))).to.eq(
562+
toRound(expectedMintedAmount),
563+
)
564+
expect(toRound(escrowedAmount)).to.eq(toRound(expectedSentToL2))
565+
await expect(tx)
566+
.emit(l1Reservoir, 'RewardsDripped')
567+
.withArgs(actualAmount.add(escrowedAmount), escrowedAmount, expectedNextDeadline)
568+
569+
let normalizedTokenSupply = (await l1Reservoir.tokenSupplyCache())
570+
.mul(await l1Reservoir.l2RewardsFraction())
571+
.div(toGRT('1'))
572+
const issuanceRate = await l1Reservoir.issuanceRate()
573+
let expectedCallhookData = l2ReservoirIface.encodeFunctionData('receiveDrip', [
574+
normalizedTokenSupply,
575+
issuanceRate,
576+
toBN('0'),
577+
])
578+
let expectedL2Data = await l1GraphTokenGateway.getOutboundCalldata(
579+
grt.address,
580+
l1Reservoir.address,
581+
mockL2Reservoir.address,
582+
escrowedAmount,
583+
expectedCallhookData,
584+
)
585+
await expect(tx)
586+
.emit(l1GraphTokenGateway, 'TxToL2')
587+
.withArgs(l1Reservoir.address, mockL2Gateway.address, toBN(1), expectedL2Data)
588+
589+
await tracker.snapshotRewards()
590+
591+
supplyBeforeDrip = await grt.totalSupply()
592+
const secondDripBlock = (await latestBlock()).add(1)
593+
const expectedNewNextDeadline = secondDripBlock.add(defaults.rewards.dripInterval)
594+
const rewardsUntilSecondDripBlock = await tracker.accRewards(secondDripBlock)
595+
const expectedTotalRewards = await tracker.accRewards(expectedNewNextDeadline)
596+
const expectedNewMintedAmount = expectedTotalRewards.sub(expectedMintedAmount)
597+
// The amount sent to L2 should cover up to the new drip block with the old fraction,
598+
// and from then onwards with the new fraction
599+
const expectedNewTotalSentToL2 = expectedTotalRewards.div(2)
600+
501601
const tx2 = await l1Reservoir
502602
.connect(governor.signer)
503603
.drip(maxGas, gasPriceBid, maxSubmissionCost, { value: defaultEthValue })

0 commit comments

Comments
 (0)