From 994d52d4ff6a17d156c988370bcb973fb6d320fe Mon Sep 17 00:00:00 2001 From: toteki <63419657+toteki@users.noreply.github.com> Date: Fri, 30 Jun 2023 15:41:37 +0100 Subject: [PATCH 01/25] scenario test: no bonded users at program end --- x/incentive/keeper/scenario_test.go | 78 +++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/x/incentive/keeper/scenario_test.go b/x/incentive/keeper/scenario_test.go index 02c6eb5912..e2b1799ffe 100644 --- a/x/incentive/keeper/scenario_test.go +++ b/x/incentive/keeper/scenario_test.go @@ -236,3 +236,81 @@ func TestZeroBonded(t *testing.T) { program = k.getProgram(1) require.Equal(t, sdk.NewInt(5_000000), program.RemainingRewards.Amount, "half of program rewards distributed") } + +func TestZeroBondedAtProgramEnd(t *testing.T) { + t.Parallel() + k := newTestKeeper(t) + k.initCommunityFund( + coin.New(umee, 1000_000000), + ) + + // In this test case, an incentive program is started but no uTokens of the incentivized denom are + // bonded during its first quarter or last quarter of runtime. During this time, it must not distribute rewards. + // During the remaining half of the program, 2/3 rewards must be distributed (spread evenly over + // the remaining time.) It is 2/3 instead of 3/4 because upon reaching 25% duration with no bonds, the + // program can adapt to award 1/3 rewards every remaining 25% duration. However, once all users unbond + // after 75% duration and never return, the program is left with some rewards it cannot distribute. + + programStart := int64(100) + k.addIncentiveProgram(uUmee, programStart, 100, sdk.NewInt64Coin(umee, 10_000000), true) + k.advanceTimeTo(programStart) // starts program, but does not attempt rewards. Do not combine with next line. + k.advanceTimeTo(programStart + 25) // 25% duration + program := k.getProgram(1) + require.Equal(t, incentive.ProgramStatusOngoing, k.programStatus(1), "program 1 status ongoing (time 125)") + require.Equal(t, program.TotalRewards, program.RemainingRewards, "all of program's rewards remain (no bonds)") + + // now create a supplier with bonded tokens, halfway through the program + alice := k.newBondedAccount( + coin.New(uUmee, 100_000000), + ) + k.advanceTimeTo(programStart + 75) // 75% duration + program = k.getProgram(1) + require.Equal(t, incentive.ProgramStatusOngoing, k.programStatus(1), "program 1 status ongoing (time 175)") + require.Equal(t, sdk.NewInt(3_333334), program.RemainingRewards.Amount, "two thirds of program rewards distributed") + + // measure pending rewards + rewards, err := k.calculateRewards(k.ctx, alice) + require.NoError(t, err) + require.Equal( + t, + sdk.NewCoins(sdk.NewInt64Coin(umee, 6_666666)), + rewards, + "alice pending rewards at time 175", + ) + // actually claim the rewards (same amount) + rewards, err = k.UpdateAccount(k.ctx, alice) + require.NoError(k.t, err) + require.Equal( + k.t, + sdk.NewCoins(sdk.NewInt64Coin(umee, 6_666666)), + rewards, + "alice claimed rewards at time 175", + ) + // begin unbonding user at 75%, making her ineligible future rewards unless she bonds again + k.mustBeginUnbond(alice, coin.New(uUmee, 100_000000)) + + // complete the program + k.advanceTimeTo(programStart + 100) // 100% duration + program = k.getProgram(1) + require.Equal(t, incentive.ProgramStatusCompleted, k.programStatus(1), "program 1 status completed (time 200)") + require.Equal(t, sdk.NewInt(3_333334), program.RemainingRewards.Amount, "two thirds of program rewards distributed") + + // measure pending rewards (zero) + rewards, err = k.calculateRewards(k.ctx, alice) + require.NoError(t, err) + require.Equal( + t, + sdk.NewCoins(), + rewards, + "alice pending rewards at time 200", + ) + // actually claim the rewards (same amount) + rewards, err = k.UpdateAccount(k.ctx, alice) + require.NoError(k.t, err) + require.Equal( + k.t, + sdk.NewCoins(), + rewards, + "alice claimed rewards at time 200", + ) +} From 101eba93bb8cd7a7c676bd45728dfcda89b4d25f Mon Sep 17 00:00:00 2001 From: toteki <63419657+toteki@users.noreply.github.com> Date: Fri, 30 Jun 2023 15:48:59 +0100 Subject: [PATCH 02/25] additional scenario tests --- x/incentive/keeper/scenario_test.go | 45 +++++++++++++++++++++++++++-- 1 file changed, 42 insertions(+), 3 deletions(-) diff --git a/x/incentive/keeper/scenario_test.go b/x/incentive/keeper/scenario_test.go index e2b1799ffe..3790340c40 100644 --- a/x/incentive/keeper/scenario_test.go +++ b/x/incentive/keeper/scenario_test.go @@ -216,7 +216,7 @@ func TestZeroBonded(t *testing.T) { ) // In this test case, an incentive program is started but no uTokens of the incentivized denom are - // bonded during its first half of runtime. during this time, it must not distribute rewards. + // bonded during its first half of runtime. During this time, it must not distribute rewards. // During the remaining half of the program, all rewards must be distributed (spread evenly over // the remaining time.) @@ -224,17 +224,56 @@ func TestZeroBonded(t *testing.T) { k.addIncentiveProgram(uUmee, programStart, 100, sdk.NewInt64Coin(umee, 10_000000), true) k.advanceTimeTo(programStart) // starts program, but does not attempt rewards. Do not combine with next line. k.advanceTimeTo(programStart + 50) - require.Equal(t, incentive.ProgramStatusOngoing, k.programStatus(1), "program 1 status (time 150)") program := k.getProgram(1) + require.Equal(t, incentive.ProgramStatusOngoing, k.programStatus(1), "program 1 status (time 150)") require.Equal(t, program.TotalRewards, program.RemainingRewards, "all of program's rewards remain (no bonds)") // now create a supplier with bonded tokens, halfway through the program - k.newBondedAccount( + alice := k.newBondedAccount( coin.New(uUmee, 100_000000), ) k.advanceTimeTo(programStart + 75) program = k.getProgram(1) + require.Equal(t, incentive.ProgramStatusOngoing, k.programStatus(1), "program 1 status (time 175)") require.Equal(t, sdk.NewInt(5_000000), program.RemainingRewards.Amount, "half of program rewards distributed") + + // finish the program with user still bonded + k.advanceTimeTo(programStart + 100) + program = k.getProgram(1) + require.Equal(t, incentive.ProgramStatusCompleted, k.programStatus(1), "program 1 status (time 200)") + require.Equal(t, sdk.ZeroInt(), program.RemainingRewards.Amount, "all of program rewards distributed") + + // measure pending rewards (even though program has ended, user has not yet claimed) + rewards, err := k.calculateRewards(k.ctx, alice) + require.NoError(t, err) + require.Equal( + t, + sdk.NewCoins(sdk.NewInt64Coin(umee, 10_000000)), + rewards, + "alice pending rewards at time 200", + ) + + // advance time further past program end + k.advanceTimeTo(programStart + 120) + + // measure pending rewards (unchanged, as user has not yet claimed) + rewards, err = k.calculateRewards(k.ctx, alice) + require.NoError(t, err) + require.Equal( + t, + sdk.NewCoins(sdk.NewInt64Coin(umee, 10_000000)), + rewards, + "alice pending rewards at time 220", + ) + // actually claim the rewards (same amount) + rewards, err = k.UpdateAccount(k.ctx, alice) + require.NoError(k.t, err) + require.Equal( + k.t, + sdk.NewCoins(sdk.NewInt64Coin(umee, 10_000000)), + rewards, + "alice claimed rewards at time 220", + ) } func TestZeroBondedAtProgramEnd(t *testing.T) { From 166ba4b722656a70944eb2bd5be66f7ce83fb71d Mon Sep 17 00:00:00 2001 From: Ebrahim Elbagory Date: Wed, 5 Jul 2023 23:05:52 -0700 Subject: [PATCH 03/25] add test flow 3 --- x/incentive/keeper/scenario_test.go | 109 ++++++++++++++++++++++++++++ 1 file changed, 109 insertions(+) diff --git a/x/incentive/keeper/scenario_test.go b/x/incentive/keeper/scenario_test.go index 3790340c40..7b9c2f0d28 100644 --- a/x/incentive/keeper/scenario_test.go +++ b/x/incentive/keeper/scenario_test.go @@ -353,3 +353,112 @@ func TestZeroBondedAtProgramEnd(t *testing.T) { "alice claimed rewards at time 200", ) } + +func TestFlow3(t *testing.T) { + t.Parallel() + k := newTestKeeper(t) + k.initCommunityFund( + coin.New(umee, 1000_000000), + ) + + // In this test case, an incentive program is started but no uTokens of the incentivized denom are + // bonded during its first half of runtime. During this time, it must not distribute rewards. + // During the remaining half of the program, all rewards must be distributed (spread evenly over + // the remaining time.) + + programStart := int64(100) + k.addIncentiveProgram(uUmee, programStart, 100, sdk.NewInt64Coin(umee, 10_000000), true) + + // now create a supplier with bonded tokens before the time starts + k.advanceTimeTo(80) + alice := k.newBondedAccount( + coin.New(uUmee, 10_000000), + ) + + k.advanceTimeTo(programStart) // starts program, + program := k.getProgram(1) + require.Equal(t, incentive.ProgramStatusOngoing, k.programStatus(1), "program 1 status (time 150)") + require.Equal(t, program.TotalRewards, program.RemainingRewards, "all of program's rewards remain (no bonds)") + + k.advanceTimeTo(programStart + 50) // time passed half + + program = k.getProgram(1) + require.Equal(t, incentive.ProgramStatusOngoing, k.programStatus(1), "program 1 status (time 175)") + require.Equal(t, sdk.NewInt(5_000000), program.RemainingRewards.Amount, "half of program rewards distributed") + + // now creates another supplier with bonded tokens, half way through the program. + bob := k.newBondedAccount( + coin.New(uUmee, 30_000000), + ) + + // finish the program with user still bonded + k.advanceTimeTo(programStart + 100) + program = k.getProgram(1) + require.Equal(t, incentive.ProgramStatusCompleted, k.programStatus(1), "program 1 status (time 200)") + require.Equal(t, sdk.ZeroInt(), program.RemainingRewards.Amount, "all of program rewards distributed") + + // measure pending rewards (even though program has ended, user has not yet claimed) + rewards, err := k.calculateRewards(k.ctx, alice) + require.NoError(t, err) + + require.Equal( + t, + sdk.NewCoins(sdk.NewInt64Coin(umee, 62_50000)), + rewards, + "alice pending rewards at time 200", + ) + + // measure pending rewards (even though program has ended, user has not yet claimed) + rewards, err = k.calculateRewards(k.ctx, bob) + require.NoError(t, err) + + require.Equal( + t, + sdk.NewCoins(sdk.NewInt64Coin(umee, 3_750000)), + rewards, + "bobs pending rewards at time 200", + ) + + // advance time further past program end + k.advanceTimeTo(programStart + 120) + + // measure pending rewards (unchanged, as user has not yet claimed) + rewards, err = k.calculateRewards(k.ctx, alice) + require.NoError(t, err) + + require.Equal( + t, + sdk.NewCoins(sdk.NewInt64Coin(umee, 6_250000)), + rewards, + "alice pending rewards at time 220", + ) + // actually claim the rewards (same amount) + rewards, err = k.UpdateAccount(k.ctx, alice) + require.NoError(k.t, err) + require.Equal( + k.t, + sdk.NewCoins(sdk.NewInt64Coin(umee, 6_250000)), + rewards, + "alice claimed rewards at time 220", + ) + + // measure pending rewards (unchanged, as user has not yet claimed) + rewards, err = k.calculateRewards(k.ctx, bob) + require.NoError(t, err) + + require.Equal( + t, + sdk.NewCoins(sdk.NewInt64Coin(umee, 3_750000)), + rewards, + "bob pending rewards at time 220", + ) + // actually claim the rewards (second account) + rewards, err = k.UpdateAccount(k.ctx, bob) + require.NoError(k.t, err) + require.Equal( + k.t, + sdk.NewCoins(sdk.NewInt64Coin(umee, 3_750000)), + rewards, + "bob claimed rewards at time 220", + ) +} From ce9773e5e1aeeeb8266966c4fb988b1a0561bd4b Mon Sep 17 00:00:00 2001 From: Ebrahim Elbagory Date: Thu, 6 Jul 2023 15:18:08 -0700 Subject: [PATCH 04/25] add TestPartialWithdraw and TestUserSupplyBeforeAndDuring test scenarios --- x/incentive/keeper/scenario_test.go | 138 +++++++++++++++++++++++++++- 1 file changed, 133 insertions(+), 5 deletions(-) diff --git a/x/incentive/keeper/scenario_test.go b/x/incentive/keeper/scenario_test.go index 7b9c2f0d28..8bffc4e2fd 100644 --- a/x/incentive/keeper/scenario_test.go +++ b/x/incentive/keeper/scenario_test.go @@ -354,17 +354,15 @@ func TestZeroBondedAtProgramEnd(t *testing.T) { ) } -func TestFlow3(t *testing.T) { +func TestUserSupplyBeforeAndDuring(t *testing.T) { t.Parallel() k := newTestKeeper(t) k.initCommunityFund( coin.New(umee, 1000_000000), ) - // In this test case, an incentive program is started but no uTokens of the incentivized denom are - // bonded during its first half of runtime. During this time, it must not distribute rewards. - // During the remaining half of the program, all rewards must be distributed (spread evenly over - // the remaining time.) + // In this test case, A user supplies and bonds uUmee before the incentive program starts + // and another user supplies half way through the incentive program programStart := int64(100) k.addIncentiveProgram(uUmee, programStart, 100, sdk.NewInt64Coin(umee, 10_000000), true) @@ -462,3 +460,133 @@ func TestFlow3(t *testing.T) { "bob claimed rewards at time 220", ) } + +func TestPartialWithdraw(t *testing.T) { + t.Parallel() + k := newTestKeeper(t) + k.initCommunityFund( + coin.New(umee, 1000_000000), + ) + + // In this test case, A user supplies and bonds uUmee before the incentive program starts + // and another user supplies half way through the incentive program. The second user then withdraws ~3/4 into the incentive program. + + programStart := int64(100) + k.addIncentiveProgram(uUmee, programStart, 100, sdk.NewInt64Coin(umee, 10_000000), true) + + // now create a supplier with bonded tokens before the time starts + k.advanceTimeTo(80) + alice := k.newBondedAccount( + coin.New(uUmee, 10_000000), + ) + + k.advanceTimeTo(programStart) // starts program, + program := k.getProgram(1) + require.Equal(t, incentive.ProgramStatusOngoing, k.programStatus(1), "program 1 status (time 150)") + require.Equal(t, program.TotalRewards, program.RemainingRewards, "all of program's rewards remain (no bonds)") + + k.advanceTimeTo(programStart + 50) // time passed half + + program = k.getProgram(1) + require.Equal(t, incentive.ProgramStatusOngoing, k.programStatus(1), "program 1 status (time 175)") + require.Equal(t, sdk.NewInt(5_000000), program.RemainingRewards.Amount, "half of program rewards distributed") + + // now creates another supplier with bonded tokens, half way through the program. + bob := k.newBondedAccount( + coin.New(uUmee, 30_000000), + ) + + k.advanceTimeTo(programStart + 70) // more time has passed + + // measure pending rewards (unchanged, as user has not yet claimed) + rewards, err := k.calculateRewards(k.ctx, bob) + require.NoError(t, err) + + require.Equal( + t, + sdk.NewCoins(sdk.NewInt64Coin(umee, 1_500000)), + rewards, + "bob pending rewards at time 220", + ) + // unbonds 20 tokens - still has 10 bonded + k.mustBeginUnbond(bob, coin.New(uUmee, 20_000000)) + + require.NoError(k.t, err) + require.Equal( + k.t, + sdk.NewCoins(sdk.NewInt64Coin(umee, 1_500000)), + rewards, + "bob claimed rewards at time 220", + ) + + // finish the program with user still bonded + k.advanceTimeTo(programStart + 100) + program = k.getProgram(1) + require.Equal(t, incentive.ProgramStatusCompleted, k.programStatus(1), "program 1 status (time 200)") + require.Equal(t, sdk.ZeroInt(), program.RemainingRewards.Amount, "all of program rewards distributed") + + // measure pending rewards (even though program has ended, user has not yet claimed) + rewards, err = k.calculateRewards(k.ctx, alice) + require.NoError(t, err) + + require.Equal( + t, + sdk.NewCoins(sdk.NewInt64Coin(umee, 7_000000)), + rewards, + "alice pending rewards at time 200", + ) + + // measure pending rewards (even though program has ended, user has not yet claimed) + rewards, err = k.calculateRewards(k.ctx, bob) + require.NoError(t, err) + + require.Equal( + t, + sdk.NewCoins(sdk.NewInt64Coin(umee, 1_500000)), + rewards, + "bobs pending rewards at time 200", + ) + + // advance time further past program end + k.advanceTimeTo(programStart + 120) + + // measure pending rewards (unchanged, as user has not yet claimed) + rewards, err = k.calculateRewards(k.ctx, alice) + require.NoError(t, err) + + require.Equal( + t, + sdk.NewCoins(sdk.NewInt64Coin(umee, 7_000000)), + rewards, + "alice pending rewards at time 220", + ) + // actually claim the rewards (same amount) + rewards, err = k.UpdateAccount(k.ctx, alice) + require.NoError(k.t, err) + require.Equal( + k.t, + sdk.NewCoins(sdk.NewInt64Coin(umee, 7_000000)), + rewards, + "alice claimed rewards at time 220", + ) + + // measure pending rewards (unchanged, as user has not yet claimed) + rewards, err = k.calculateRewards(k.ctx, bob) + require.NoError(t, err) + + require.Equal( + t, + sdk.NewCoins(sdk.NewInt64Coin(umee, 1_500000)), + rewards, + "bob pending rewards at time 220", + ) + // actually claim the rewards (second account) + rewards, err = k.UpdateAccount(k.ctx, bob) + require.NoError(k.t, err) + require.Equal( + k.t, + sdk.NewCoins(sdk.NewInt64Coin(umee, 1_500000)), + rewards, + "bob claimed rewards at time 220", + ) +} From 190161af4b8f95fe1f4ec11616937ae7f6316dda Mon Sep 17 00:00:00 2001 From: toteki <63419657+toteki@users.noreply.github.com> Date: Thu, 6 Jul 2023 17:37:45 -0600 Subject: [PATCH 05/25] suggestion for TestZeroBondedAtProgramEnd --- x/incentive/keeper/scenario_test.go | 30 ++++++----------------------- 1 file changed, 6 insertions(+), 24 deletions(-) diff --git a/x/incentive/keeper/scenario_test.go b/x/incentive/keeper/scenario_test.go index 8bffc4e2fd..e29810a1a5 100644 --- a/x/incentive/keeper/scenario_test.go +++ b/x/incentive/keeper/scenario_test.go @@ -308,23 +308,14 @@ func TestZeroBondedAtProgramEnd(t *testing.T) { require.Equal(t, sdk.NewInt(3_333334), program.RemainingRewards.Amount, "two thirds of program rewards distributed") // measure pending rewards + aliceReward := sdk.NewCoins(sdk.NewInt64Coin(umee, 6_666666)) rewards, err := k.calculateRewards(k.ctx, alice) require.NoError(t, err) - require.Equal( - t, - sdk.NewCoins(sdk.NewInt64Coin(umee, 6_666666)), - rewards, - "alice pending rewards at time 175", - ) + require.Equal(t, aliceReward, rewards, "alice pending rewards at time 175") // actually claim the rewards (same amount) rewards, err = k.UpdateAccount(k.ctx, alice) require.NoError(k.t, err) - require.Equal( - k.t, - sdk.NewCoins(sdk.NewInt64Coin(umee, 6_666666)), - rewards, - "alice claimed rewards at time 175", - ) + require.Equal(k.t, aliceReward, rewards, "alice claimed rewards at time 175") // begin unbonding user at 75%, making her ineligible future rewards unless she bonds again k.mustBeginUnbond(alice, coin.New(uUmee, 100_000000)) @@ -335,23 +326,14 @@ func TestZeroBondedAtProgramEnd(t *testing.T) { require.Equal(t, sdk.NewInt(3_333334), program.RemainingRewards.Amount, "two thirds of program rewards distributed") // measure pending rewards (zero) + noRewards := sdk.NewCoins() rewards, err = k.calculateRewards(k.ctx, alice) require.NoError(t, err) - require.Equal( - t, - sdk.NewCoins(), - rewards, - "alice pending rewards at time 200", - ) + require.Equal(t, noRewards, rewards, "alice pending rewards at time 200") // actually claim the rewards (same amount) rewards, err = k.UpdateAccount(k.ctx, alice) require.NoError(k.t, err) - require.Equal( - k.t, - sdk.NewCoins(), - rewards, - "alice claimed rewards at time 200", - ) + require.Equal(k.t, noRewards, rewards, "alice claimed rewards at time 200") } func TestUserSupplyBeforeAndDuring(t *testing.T) { From 70bcd4b465f7bda20ee1657801d86d5ed1ab4160 Mon Sep 17 00:00:00 2001 From: Adam Moser <63419657+toteki@users.noreply.github.com> Date: Thu, 6 Jul 2023 17:38:29 -0600 Subject: [PATCH 06/25] Update x/incentive/keeper/scenario_test.go Co-authored-by: Robert Zaremba --- x/incentive/keeper/scenario_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x/incentive/keeper/scenario_test.go b/x/incentive/keeper/scenario_test.go index e29810a1a5..70bd25dd59 100644 --- a/x/incentive/keeper/scenario_test.go +++ b/x/incentive/keeper/scenario_test.go @@ -284,7 +284,7 @@ func TestZeroBondedAtProgramEnd(t *testing.T) { ) // In this test case, an incentive program is started but no uTokens of the incentivized denom are - // bonded during its first quarter or last quarter of runtime. During this time, it must not distribute rewards. + // bonded during its first quarter nor last quarter of the program. It must not distribute rewards when no tokens are bonded. // During the remaining half of the program, 2/3 rewards must be distributed (spread evenly over // the remaining time.) It is 2/3 instead of 3/4 because upon reaching 25% duration with no bonds, the // program can adapt to award 1/3 rewards every remaining 25% duration. However, once all users unbond From a6a52040bba75668467773343b47fde5093543da Mon Sep 17 00:00:00 2001 From: toteki <63419657+toteki@users.noreply.github.com> Date: Thu, 6 Jul 2023 17:39:47 -0600 Subject: [PATCH 07/25] spacing --- x/incentive/keeper/scenario_test.go | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/x/incentive/keeper/scenario_test.go b/x/incentive/keeper/scenario_test.go index 70bd25dd59..6c2d6d3bd4 100644 --- a/x/incentive/keeper/scenario_test.go +++ b/x/incentive/keeper/scenario_test.go @@ -284,11 +284,12 @@ func TestZeroBondedAtProgramEnd(t *testing.T) { ) // In this test case, an incentive program is started but no uTokens of the incentivized denom are - // bonded during its first quarter nor last quarter of the program. It must not distribute rewards when no tokens are bonded. - // During the remaining half of the program, 2/3 rewards must be distributed (spread evenly over - // the remaining time.) It is 2/3 instead of 3/4 because upon reaching 25% duration with no bonds, the - // program can adapt to award 1/3 rewards every remaining 25% duration. However, once all users unbond - // after 75% duration and never return, the program is left with some rewards it cannot distribute. + // bonded during its first quarter nor last quarter of the program. It must not distribute rewards + // when no tokens are bonded. During the remaining half of the program, 2/3 rewards must be distributed + // (spread evenly over the remaining time.) It is 2/3 instead of 3/4 because upon reaching 25% duration + // with no bonds, the program can adapt to award 1/3 rewards every remaining 25% duration. However, + // once all users unbond after 75% duration and never return, the program is left with some rewards + // it cannot distribute. programStart := int64(100) k.addIncentiveProgram(uUmee, programStart, 100, sdk.NewInt64Coin(umee, 10_000000), true) From 1810126186b051306b4b7af5ee79321f27711152 Mon Sep 17 00:00:00 2001 From: toteki <63419657+toteki@users.noreply.github.com> Date: Thu, 6 Jul 2023 17:43:24 -0600 Subject: [PATCH 08/25] suggestions implemented for TestZeroBonded --- x/incentive/keeper/scenario_test.go | 32 +++++++++++++---------------- 1 file changed, 14 insertions(+), 18 deletions(-) diff --git a/x/incentive/keeper/scenario_test.go b/x/incentive/keeper/scenario_test.go index 6c2d6d3bd4..02bfba2b7d 100644 --- a/x/incentive/keeper/scenario_test.go +++ b/x/incentive/keeper/scenario_test.go @@ -246,12 +246,8 @@ func TestZeroBonded(t *testing.T) { // measure pending rewards (even though program has ended, user has not yet claimed) rewards, err := k.calculateRewards(k.ctx, alice) require.NoError(t, err) - require.Equal( - t, - sdk.NewCoins(sdk.NewInt64Coin(umee, 10_000000)), - rewards, - "alice pending rewards at time 200", - ) + aliceRewards := sdk.NewCoins(sdk.NewInt64Coin(umee, 10_000000)) + require.Equal(t, aliceRewards, rewards, "alice pending rewards at time 200") // advance time further past program end k.advanceTimeTo(programStart + 120) @@ -259,21 +255,21 @@ func TestZeroBonded(t *testing.T) { // measure pending rewards (unchanged, as user has not yet claimed) rewards, err = k.calculateRewards(k.ctx, alice) require.NoError(t, err) - require.Equal( - t, - sdk.NewCoins(sdk.NewInt64Coin(umee, 10_000000)), - rewards, - "alice pending rewards at time 220", - ) + require.Equal(t, aliceRewards, rewards, "alice pending rewards at time 220") // actually claim the rewards (same amount) rewards, err = k.UpdateAccount(k.ctx, alice) require.NoError(k.t, err) - require.Equal( - k.t, - sdk.NewCoins(sdk.NewInt64Coin(umee, 10_000000)), - rewards, - "alice claimed rewards at time 220", - ) + require.Equal(k.t, aliceRewards, rewards, "alice claimed rewards at time 220") + + // no more pending rewards after claiming + rewards, err = k.calculateRewards(k.ctx, alice) + require.NoError(k.t, err) + require.Equal(k.t, sdk.NewCoins(), rewards, "alice pending rewards after claim") + + // actually claim the rewards (same amount) + rewards, err = k.UpdateAccount(k.ctx, alice) + require.NoError(k.t, err) + require.Equal(k.t, sdk.NewCoins(), rewards, "alice claimed rewards after claim") } func TestZeroBondedAtProgramEnd(t *testing.T) { From 9d182030ba88713a4d62b801e4037f649a8956dd Mon Sep 17 00:00:00 2001 From: toteki <63419657+toteki@users.noreply.github.com> Date: Thu, 6 Jul 2023 18:06:55 -0600 Subject: [PATCH 09/25] adjust whitespace and shorted sdk.Coins vars --- util/coin/fixtures.go | 5 + x/incentive/keeper/scenario_test.go | 163 ++++++++-------------------- 2 files changed, 49 insertions(+), 119 deletions(-) diff --git a/util/coin/fixtures.go b/util/coin/fixtures.go index 0b4acb8966..94754874e8 100644 --- a/util/coin/fixtures.go +++ b/util/coin/fixtures.go @@ -38,6 +38,11 @@ var ( Dollar = "dollar" ) +// UmeeCoins creates an Umee (uumee) sdk.Coins with given amount +func UmeeCoins(amount int64) sdk.Coins { + return sdk.NewCoins(sdk.NewInt64Coin(appparams.BondDenom, amount)) +} + // UmeeDec creates a Umee (uumee) DecCoin with given amount func UmeeDec(amount string) sdk.DecCoin { return Dec(appparams.BondDenom, amount) diff --git a/x/incentive/keeper/scenario_test.go b/x/incentive/keeper/scenario_test.go index 02bfba2b7d..ad6162dea7 100644 --- a/x/incentive/keeper/scenario_test.go +++ b/x/incentive/keeper/scenario_test.go @@ -97,7 +97,7 @@ func TestBasicIncentivePrograms(t *testing.T) { require.NoError(t, err) require.Equal( t, - sdk.NewCoins(sdk.NewInt64Coin(umee, 100000)), + coin.UmeeCoins(100000), rewards, "alice pending rewards at time 101", ) @@ -129,7 +129,7 @@ func TestBasicIncentivePrograms(t *testing.T) { require.NoError(t, err) require.Equal( t, - sdk.NewCoins(sdk.NewInt64Coin(umee, 179999)), + coin.UmeeCoins(179999), rewards, "alice pending rewards at time 102", ) @@ -137,7 +137,7 @@ func TestBasicIncentivePrograms(t *testing.T) { require.NoError(t, err) require.Equal( t, - sdk.NewCoins(sdk.NewInt64Coin(umee, 19999)), + coin.UmeeCoins(19999), rewards, "bob pending rewards at time 102", ) @@ -154,6 +154,7 @@ func TestBasicIncentivePrograms(t *testing.T) { require.Equal(t, incentive.ProgramStatusCompleted, k.programStatus(1), "program 1 status (time 300)") require.Equal(t, incentive.ProgramStatusCompleted, k.programStatus(2), "program 2 status (time 300)") require.Equal(t, incentive.ProgramStatusCompleted, k.programStatus(3), "program 3 status (time 300)") + // Remaining rewards should be exactly zero. program1 = k.getProgram(1) program2 := k.getProgram(2) @@ -171,41 +172,33 @@ func TestBasicIncentivePrograms(t *testing.T) { require.Equal(k.t, 0, len(programs)) // a small amount from before bob joined, then 80% of the rest of program 1, and 80% of program 3 - aliceRewards := int64(100000 + 7_920000 + 8_000000) + aliceRewards := coin.UmeeCoins(100000 + 7_920000 + 8_000000) // 20% of the rest of program 1 (missing the first block), and 20% of program 3 - bobRewards := int64(1_980000 + 2_000000) + bobRewards := coin.UmeeCoins(1_980000 + 2_000000) // These are the final pending rewards observed. rewards, err = k.calculateRewards(k.ctx, alice) require.NoError(t, err) - require.Equal( - t, - sdk.NewCoins(sdk.NewInt64Coin(umee, aliceRewards)), - rewards, - "alice pending rewards at time 300", - ) + require.Equal(t, aliceRewards, rewards, "alice pending rewards at time 300") + rewards, err = k.calculateRewards(k.ctx, bob) + require.NoError(t, err) + require.Equal(t, bobRewards, rewards, "bob pending rewards at time 300") + // actually claim the rewards (same amount) rewards, err = k.UpdateAccount(k.ctx, alice) require.NoError(k.t, err) - require.Equal( - k.t, - sdk.NewCoins(sdk.NewInt64Coin(umee, aliceRewards)), - rewards, - "alice claimed rewards at time 300", - ) + require.Equal(k.t, aliceRewards, rewards, "alice claimed rewards at time 300") + rewards, err = k.UpdateAccount(k.ctx, bob) + require.NoError(k.t, err) + require.Equal(k.t, bobRewards, rewards, "bob claimed rewards at time 300") + // no more pending rewards after claiming rewards, err = k.calculateRewards(k.ctx, alice) require.NoError(k.t, err) require.Equal(k.t, sdk.NewCoins(), rewards, "alice pending rewards after claim") - rewards, err = k.calculateRewards(k.ctx, bob) - require.NoError(t, err) - require.Equal( - t, - sdk.NewCoins(sdk.NewInt64Coin(umee, bobRewards)), - rewards, - "bob pending rewards at time 300", - ) + require.NoError(k.t, err) + require.Equal(k.t, sdk.NewCoins(), rewards, "bob pending rewards after claim") } func TestZeroBonded(t *testing.T) { @@ -246,7 +239,7 @@ func TestZeroBonded(t *testing.T) { // measure pending rewards (even though program has ended, user has not yet claimed) rewards, err := k.calculateRewards(k.ctx, alice) require.NoError(t, err) - aliceRewards := sdk.NewCoins(sdk.NewInt64Coin(umee, 10_000000)) + aliceRewards := coin.UmeeCoins(10_000000) require.Equal(t, aliceRewards, rewards, "alice pending rewards at time 200") // advance time further past program end @@ -256,6 +249,7 @@ func TestZeroBonded(t *testing.T) { rewards, err = k.calculateRewards(k.ctx, alice) require.NoError(t, err) require.Equal(t, aliceRewards, rewards, "alice pending rewards at time 220") + // actually claim the rewards (same amount) rewards, err = k.UpdateAccount(k.ctx, alice) require.NoError(k.t, err) @@ -305,14 +299,16 @@ func TestZeroBondedAtProgramEnd(t *testing.T) { require.Equal(t, sdk.NewInt(3_333334), program.RemainingRewards.Amount, "two thirds of program rewards distributed") // measure pending rewards - aliceReward := sdk.NewCoins(sdk.NewInt64Coin(umee, 6_666666)) + aliceReward := coin.UmeeCoins(6_666666) rewards, err := k.calculateRewards(k.ctx, alice) require.NoError(t, err) require.Equal(t, aliceReward, rewards, "alice pending rewards at time 175") + // actually claim the rewards (same amount) rewards, err = k.UpdateAccount(k.ctx, alice) require.NoError(k.t, err) require.Equal(k.t, aliceReward, rewards, "alice claimed rewards at time 175") + // begin unbonding user at 75%, making her ineligible future rewards unless she bonds again k.mustBeginUnbond(alice, coin.New(uUmee, 100_000000)) @@ -327,6 +323,7 @@ func TestZeroBondedAtProgramEnd(t *testing.T) { rewards, err = k.calculateRewards(k.ctx, alice) require.NoError(t, err) require.Equal(t, noRewards, rewards, "alice pending rewards at time 200") + // actually claim the rewards (same amount) rewards, err = k.UpdateAccount(k.ctx, alice) require.NoError(k.t, err) @@ -377,24 +374,14 @@ func TestUserSupplyBeforeAndDuring(t *testing.T) { // measure pending rewards (even though program has ended, user has not yet claimed) rewards, err := k.calculateRewards(k.ctx, alice) require.NoError(t, err) - - require.Equal( - t, - sdk.NewCoins(sdk.NewInt64Coin(umee, 62_50000)), - rewards, - "alice pending rewards at time 200", - ) + aliceRewards := coin.UmeeCoins(6_250000) + require.Equal(t, aliceRewards, rewards, "alice pending rewards at time 200") // measure pending rewards (even though program has ended, user has not yet claimed) rewards, err = k.calculateRewards(k.ctx, bob) require.NoError(t, err) - - require.Equal( - t, - sdk.NewCoins(sdk.NewInt64Coin(umee, 3_750000)), - rewards, - "bobs pending rewards at time 200", - ) + bobRewards := coin.UmeeCoins(3_750000) + require.Equal(t, bobRewards, rewards, "bobs pending rewards at time 200") // advance time further past program end k.advanceTimeTo(programStart + 120) @@ -402,42 +389,22 @@ func TestUserSupplyBeforeAndDuring(t *testing.T) { // measure pending rewards (unchanged, as user has not yet claimed) rewards, err = k.calculateRewards(k.ctx, alice) require.NoError(t, err) + require.Equal(t, aliceRewards, rewards, "alice pending rewards at time 220") - require.Equal( - t, - sdk.NewCoins(sdk.NewInt64Coin(umee, 6_250000)), - rewards, - "alice pending rewards at time 220", - ) // actually claim the rewards (same amount) rewards, err = k.UpdateAccount(k.ctx, alice) require.NoError(k.t, err) - require.Equal( - k.t, - sdk.NewCoins(sdk.NewInt64Coin(umee, 6_250000)), - rewards, - "alice claimed rewards at time 220", - ) + require.Equal(k.t, aliceRewards, rewards, "alice claimed rewards at time 220") // measure pending rewards (unchanged, as user has not yet claimed) rewards, err = k.calculateRewards(k.ctx, bob) require.NoError(t, err) + require.Equal(t, bobRewards, rewards, "bob pending rewards at time 220") - require.Equal( - t, - sdk.NewCoins(sdk.NewInt64Coin(umee, 3_750000)), - rewards, - "bob pending rewards at time 220", - ) // actually claim the rewards (second account) rewards, err = k.UpdateAccount(k.ctx, bob) require.NoError(k.t, err) - require.Equal( - k.t, - sdk.NewCoins(sdk.NewInt64Coin(umee, 3_750000)), - rewards, - "bob claimed rewards at time 220", - ) + require.Equal(k.t, bobRewards, rewards, "bob claimed rewards at time 220") } func TestPartialWithdraw(t *testing.T) { @@ -448,7 +415,8 @@ func TestPartialWithdraw(t *testing.T) { ) // In this test case, A user supplies and bonds uUmee before the incentive program starts - // and another user supplies half way through the incentive program. The second user then withdraws ~3/4 into the incentive program. + // and another user supplies half way through the incentive program. The second user then + // withdraws ~3/4 into the incentive program. programStart := int64(100) k.addIncentiveProgram(uUmee, programStart, 100, sdk.NewInt64Coin(umee, 10_000000), true) @@ -480,24 +448,12 @@ func TestPartialWithdraw(t *testing.T) { // measure pending rewards (unchanged, as user has not yet claimed) rewards, err := k.calculateRewards(k.ctx, bob) require.NoError(t, err) + bobRewards := coin.UmeeCoins(1_500000) + require.Equal(t, bobRewards, rewards, "bob pending rewards at time 220") - require.Equal( - t, - sdk.NewCoins(sdk.NewInt64Coin(umee, 1_500000)), - rewards, - "bob pending rewards at time 220", - ) - // unbonds 20 tokens - still has 10 bonded + // unbonds 20 tokens - still has 10 bonded. this also claims pending rewards. k.mustBeginUnbond(bob, coin.New(uUmee, 20_000000)) - require.NoError(k.t, err) - require.Equal( - k.t, - sdk.NewCoins(sdk.NewInt64Coin(umee, 1_500000)), - rewards, - "bob claimed rewards at time 220", - ) - // finish the program with user still bonded k.advanceTimeTo(programStart + 100) program = k.getProgram(1) @@ -507,24 +463,13 @@ func TestPartialWithdraw(t *testing.T) { // measure pending rewards (even though program has ended, user has not yet claimed) rewards, err = k.calculateRewards(k.ctx, alice) require.NoError(t, err) - - require.Equal( - t, - sdk.NewCoins(sdk.NewInt64Coin(umee, 7_000000)), - rewards, - "alice pending rewards at time 200", - ) + aliceRewards := coin.UmeeCoins(7_000000) + require.Equal(t, aliceRewards, rewards, "alice pending rewards at time 200") // measure pending rewards (even though program has ended, user has not yet claimed) rewards, err = k.calculateRewards(k.ctx, bob) require.NoError(t, err) - - require.Equal( - t, - sdk.NewCoins(sdk.NewInt64Coin(umee, 1_500000)), - rewards, - "bobs pending rewards at time 200", - ) + require.Equal(t, bobRewards, rewards, "bobs pending rewards at time 200") // advance time further past program end k.advanceTimeTo(programStart + 120) @@ -532,40 +477,20 @@ func TestPartialWithdraw(t *testing.T) { // measure pending rewards (unchanged, as user has not yet claimed) rewards, err = k.calculateRewards(k.ctx, alice) require.NoError(t, err) + require.Equal(t, aliceRewards, rewards, "alice pending rewards at time 220") - require.Equal( - t, - sdk.NewCoins(sdk.NewInt64Coin(umee, 7_000000)), - rewards, - "alice pending rewards at time 220", - ) // actually claim the rewards (same amount) rewards, err = k.UpdateAccount(k.ctx, alice) require.NoError(k.t, err) - require.Equal( - k.t, - sdk.NewCoins(sdk.NewInt64Coin(umee, 7_000000)), - rewards, - "alice claimed rewards at time 220", - ) + require.Equal(k.t, aliceRewards, rewards, "alice claimed rewards at time 220") // measure pending rewards (unchanged, as user has not yet claimed) rewards, err = k.calculateRewards(k.ctx, bob) require.NoError(t, err) + require.Equal(t, bobRewards, rewards, "bob pending rewards at time 220") - require.Equal( - t, - sdk.NewCoins(sdk.NewInt64Coin(umee, 1_500000)), - rewards, - "bob pending rewards at time 220", - ) // actually claim the rewards (second account) rewards, err = k.UpdateAccount(k.ctx, bob) require.NoError(k.t, err) - require.Equal( - k.t, - sdk.NewCoins(sdk.NewInt64Coin(umee, 1_500000)), - rewards, - "bob claimed rewards at time 220", - ) + require.Equal(k.t, bobRewards, rewards, "bob claimed rewards at time 220") } From 18e9a5de45a98288a475082e8c147f7c393c694a Mon Sep 17 00:00:00 2001 From: toteki <63419657+toteki@users.noreply.github.com> Date: Thu, 6 Jul 2023 18:17:49 -0600 Subject: [PATCH 10/25] godoc all test scenarios --- x/incentive/keeper/scenario_test.go | 46 ++++++++++++++++------------- 1 file changed, 26 insertions(+), 20 deletions(-) diff --git a/x/incentive/keeper/scenario_test.go b/x/incentive/keeper/scenario_test.go index ad6162dea7..9912515f26 100644 --- a/x/incentive/keeper/scenario_test.go +++ b/x/incentive/keeper/scenario_test.go @@ -19,6 +19,12 @@ const ( uAtom = leveragetypes.UTokenPrefix + fixtures.AtomDenom ) +// TestBasicIncentivePrograms runs an incentive program test scenario. +// In this scenario, three separate incentive programs with varying start times +// and funding amounts are run, with two users bonding at various times. +// Actual reward amounts are compared to expected values, and the status of +// the programs and their remaining rewards are tracked from creation to after +// their end times. func TestBasicIncentivePrograms(t *testing.T) { t.Parallel() k := newTestKeeper(t) @@ -201,6 +207,11 @@ func TestBasicIncentivePrograms(t *testing.T) { require.Equal(k.t, sdk.NewCoins(), rewards, "bob pending rewards after claim") } +// TestZeroBonded runs an incentive program test scenario. +// In this test case, an incentive program is started but no uTokens of the incentivized denom are +// bonded during its first half of runtime. During this time, it must not distribute rewards. +// During the remaining half of the program, all rewards must be distributed (spread evenly over +// the remaining time.) func TestZeroBonded(t *testing.T) { t.Parallel() k := newTestKeeper(t) @@ -208,11 +219,6 @@ func TestZeroBonded(t *testing.T) { coin.New(umee, 1000_000000), ) - // In this test case, an incentive program is started but no uTokens of the incentivized denom are - // bonded during its first half of runtime. During this time, it must not distribute rewards. - // During the remaining half of the program, all rewards must be distributed (spread evenly over - // the remaining time.) - programStart := int64(100) k.addIncentiveProgram(uUmee, programStart, 100, sdk.NewInt64Coin(umee, 10_000000), true) k.advanceTimeTo(programStart) // starts program, but does not attempt rewards. Do not combine with next line. @@ -266,6 +272,14 @@ func TestZeroBonded(t *testing.T) { require.Equal(k.t, sdk.NewCoins(), rewards, "alice claimed rewards after claim") } +// TestZeroBondedAtProgramEnd runs an incentive program test scenario. +// In this test case, an incentive program is started but no uTokens of the incentivized denom are +// bonded during its first quarter nor last quarter of the program. It must not distribute rewards +// when no tokens are bonded. During the remaining half of the program, 2/3 rewards must be distributed +// (spread evenly over the remaining time.) It is 2/3 instead of 3/4 because upon reaching 25% duration +// with no bonds, the program can adapt to award 1/3 rewards every remaining 25% duration. However, +// once all users unbond after 75% duration and never return, the program is left with some rewards +// it cannot distribute. func TestZeroBondedAtProgramEnd(t *testing.T) { t.Parallel() k := newTestKeeper(t) @@ -273,14 +287,6 @@ func TestZeroBondedAtProgramEnd(t *testing.T) { coin.New(umee, 1000_000000), ) - // In this test case, an incentive program is started but no uTokens of the incentivized denom are - // bonded during its first quarter nor last quarter of the program. It must not distribute rewards - // when no tokens are bonded. During the remaining half of the program, 2/3 rewards must be distributed - // (spread evenly over the remaining time.) It is 2/3 instead of 3/4 because upon reaching 25% duration - // with no bonds, the program can adapt to award 1/3 rewards every remaining 25% duration. However, - // once all users unbond after 75% duration and never return, the program is left with some rewards - // it cannot distribute. - programStart := int64(100) k.addIncentiveProgram(uUmee, programStart, 100, sdk.NewInt64Coin(umee, 10_000000), true) k.advanceTimeTo(programStart) // starts program, but does not attempt rewards. Do not combine with next line. @@ -330,6 +336,9 @@ func TestZeroBondedAtProgramEnd(t *testing.T) { require.Equal(k.t, noRewards, rewards, "alice claimed rewards at time 200") } +// TestUserSupplyBeforeAndDuring runs an incentive program test scenario. +// In this test case, A user supplies and bonds uUmee before the incentive program starts +// and another user supplies half way through the incentive program. func TestUserSupplyBeforeAndDuring(t *testing.T) { t.Parallel() k := newTestKeeper(t) @@ -337,9 +346,6 @@ func TestUserSupplyBeforeAndDuring(t *testing.T) { coin.New(umee, 1000_000000), ) - // In this test case, A user supplies and bonds uUmee before the incentive program starts - // and another user supplies half way through the incentive program - programStart := int64(100) k.addIncentiveProgram(uUmee, programStart, 100, sdk.NewInt64Coin(umee, 10_000000), true) @@ -407,6 +413,10 @@ func TestUserSupplyBeforeAndDuring(t *testing.T) { require.Equal(k.t, bobRewards, rewards, "bob claimed rewards at time 220") } +// TestPartialWithdraw runs an incentive program test scenario. +// In this test case, A user supplies and bonds uUmee before the incentive program starts +// and another user supplies half way through the incentive program. The second user then +// withdraws ~3/4 into the incentive program. func TestPartialWithdraw(t *testing.T) { t.Parallel() k := newTestKeeper(t) @@ -414,10 +424,6 @@ func TestPartialWithdraw(t *testing.T) { coin.New(umee, 1000_000000), ) - // In this test case, A user supplies and bonds uUmee before the incentive program starts - // and another user supplies half way through the incentive program. The second user then - // withdraws ~3/4 into the incentive program. - programStart := int64(100) k.addIncentiveProgram(uUmee, programStart, 100, sdk.NewInt64Coin(umee, 10_000000), true) From 98500d0a7faef7d970e0a7ce753c68d429d21fd6 Mon Sep 17 00:00:00 2001 From: Adam Moser <63419657+toteki@users.noreply.github.com> Date: Fri, 7 Jul 2023 10:12:41 -0600 Subject: [PATCH 11/25] Update util/coin/fixtures.go Co-authored-by: Robert Zaremba --- util/coin/fixtures.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/util/coin/fixtures.go b/util/coin/fixtures.go index 94754874e8..d1cac138a3 100644 --- a/util/coin/fixtures.go +++ b/util/coin/fixtures.go @@ -40,7 +40,7 @@ var ( // UmeeCoins creates an Umee (uumee) sdk.Coins with given amount func UmeeCoins(amount int64) sdk.Coins { - return sdk.NewCoins(sdk.NewInt64Coin(appparams.BondDenom, amount)) + return sdk.NewCoins(sdk.NewInt64Coin(umee, amount)) } // UmeeDec creates a Umee (uumee) DecCoin with given amount From fe35840e107becf2aaa365fc0aa11eb2c2849470 Mon Sep 17 00:00:00 2001 From: toteki <63419657+toteki@users.noreply.github.com> Date: Fri, 7 Jul 2023 10:39:07 -0600 Subject: [PATCH 12/25] comment++ --- x/incentive/keeper/update.go | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/x/incentive/keeper/update.go b/x/incentive/keeper/update.go index 649b93420b..7a9587bb6b 100644 --- a/x/incentive/keeper/update.go +++ b/x/incentive/keeper/update.go @@ -30,7 +30,8 @@ func (k Keeper) UpdateAccount(ctx sdk.Context, addr sdk.AccAddress) (sdk.Coins, // updateRewards updates any rewardAccumulators associated with ongoing incentive programs // based on the time elapsed between LastRewardTime and block time. It decreases active // incentive programs' RemainingRewards by the amount of coins distributed. -// Also sets module's LastRewardsTime to block time. +// Also sets module's LastRewardsTime to block time. If no uTokens are currently bonded, +// does nothing. func (k Keeper) updateRewards(ctx sdk.Context, blockTime int64) error { prevTime := k.GetLastRewardsTime(ctx) if prevTime > blockTime { @@ -123,6 +124,10 @@ func (k Keeper) updatePrograms(ctx sdk.Context) error { for _, op := range ongoingPrograms { // if an ongoing program is ending, move it to completed programs without modifying any fields if blockTime >= op.Duration+op.StartTime { + // if remaining rewards are nonzero, it means no eligible collateral was + // bonded during the last available block for rewards. Those remaining + // rewards remain in the module account (in the same place as unclaimed + // and upcoming rewards.) if err := k.moveIncentiveProgram(ctx, op.ID, incentive.ProgramStatusCompleted); err != nil { return err } From 1bc86a0b0cf8b69050b61c7688f86019655d66d4 Mon Sep 17 00:00:00 2001 From: Adam Moser <63419657+toteki@users.noreply.github.com> Date: Fri, 7 Jul 2023 10:42:08 -0600 Subject: [PATCH 13/25] Update x/incentive/keeper/scenario_test.go Co-authored-by: Robert Zaremba --- x/incentive/keeper/scenario_test.go | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/x/incentive/keeper/scenario_test.go b/x/incentive/keeper/scenario_test.go index 9912515f26..56765a2112 100644 --- a/x/incentive/keeper/scenario_test.go +++ b/x/incentive/keeper/scenario_test.go @@ -261,12 +261,10 @@ func TestZeroBonded(t *testing.T) { require.NoError(k.t, err) require.Equal(k.t, aliceRewards, rewards, "alice claimed rewards at time 220") - // no more pending rewards after claiming + // try to make another claim. The rewards should be zero rewards, err = k.calculateRewards(k.ctx, alice) require.NoError(k.t, err) require.Equal(k.t, sdk.NewCoins(), rewards, "alice pending rewards after claim") - - // actually claim the rewards (same amount) rewards, err = k.UpdateAccount(k.ctx, alice) require.NoError(k.t, err) require.Equal(k.t, sdk.NewCoins(), rewards, "alice claimed rewards after claim") From 65efef3d96c12b083fa8152dfe9ff0387ee0c776 Mon Sep 17 00:00:00 2001 From: toteki <63419657+toteki@users.noreply.github.com> Date: Fri, 7 Jul 2023 15:41:38 -0600 Subject: [PATCH 14/25] final scenario --- x/incentive/keeper/scenario_test.go | 68 +++++++++++++++++++++++++++++ 1 file changed, 68 insertions(+) diff --git a/x/incentive/keeper/scenario_test.go b/x/incentive/keeper/scenario_test.go index 56765a2112..1cce6fc150 100644 --- a/x/incentive/keeper/scenario_test.go +++ b/x/incentive/keeper/scenario_test.go @@ -498,3 +498,71 @@ func TestPartialWithdraw(t *testing.T) { require.NoError(k.t, err) require.Equal(k.t, bobRewards, rewards, "bob claimed rewards at time 220") } + +// TestRejoinScenario runs a scenario whe +func TestRejoinScenario(t *testing.T) { + t.Parallel() + k := newTestKeeper(t) + k.initCommunityFund( + coin.New(umee, 1000_000000), + ) + + // an incentive program with 10 UMEE rewards will run from t=100 to t=200 + programStart := int64(100) + k.addIncentiveProgram(uUmee, programStart, 100, sdk.NewInt64Coin(umee, 10_000000), true) + + // create two bonded accounts before the program starts. Alice bonds 3x what bob does. + k.advanceTimeTo(80) + alice := k.newBondedAccount( + coin.New(uUmee, 30_000000), + ) + bob := k.newBondedAccount( + coin.New(uUmee, 10_000000), + ) + + k.advanceTimeTo(programStart) // starts program, + k.advanceTimeTo(programStart + 20) // time passed 20% + + // alice unbonds, losing reward eligibility + k.mustBeginUnbond(alice, coin.New(uUmee, 30_000000)) + + k.advanceTimeTo(programStart + 30) // time passed 30% + + // bob unbonds, losing reward eligibility + k.mustBeginUnbond(bob, coin.New(uUmee, 10_000000)) + + k.advanceTimeTo(programStart + 50) // time passed 50% + + // bob bonds once more at 50% time elapsed + k.mustBond(bob, coin.New(uUmee, 10_000000)) + + k.advanceTimeTo(programStart + 100) // time passed 100% + + // confirm program ended + program := k.getProgram(1) + require.Equal(t, incentive.ProgramStatusCompleted, k.programStatus(1), "program 1 status (time 200)") + require.Equal(t, sdk.ZeroInt(), program.RemainingRewards.Amount, "all of program rewards distributed") + + // measure pending rewards and wallet balance (alice claimed rewards, as part of the beginUnbonding transaction) + rewards, err := k.calculateRewards(k.ctx, alice) + require.NoError(t, err) + aliceBalance := coin.UmeeCoins(1_500000) + require.Equal(t, sdk.NewCoins(), rewards, "alice pending rewards at time 200") + require.Equal(t, aliceBalance, k.bankKeeper.SpendableCoins(k.ctx, alice), "alice balance at time 200") + + // measure pending rewards (bob claimed his rewards from before unbond, but not after second bond) + rewards, err = k.calculateRewards(k.ctx, bob) + require.NoError(t, err) + bobRewards := coin.UmeeCoins(7_000000) + bobBalance := coin.UmeeCoins(1_500000) + require.Equal(t, bobRewards, rewards, "bob pending rewards at time 200") + require.Equal(t, bobBalance, k.bankKeeper.SpendableCoins(k.ctx, bob), "bob balance at time 200") + + // claim the rewards (same amounts) + rewards, err = k.UpdateAccount(k.ctx, alice) + require.NoError(k.t, err) + require.Equal(k.t, sdk.NewCoins(), rewards, "alice claimed rewards at time 200") + rewards, err = k.UpdateAccount(k.ctx, bob) + require.NoError(k.t, err) + require.Equal(k.t, bobRewards, rewards, "bob claimed rewards at time 200") +} From 6b8394c151e13a9ed07214097a594549b4b4dbe9 Mon Sep 17 00:00:00 2001 From: toteki <63419657+toteki@users.noreply.github.com> Date: Fri, 7 Jul 2023 16:17:02 -0600 Subject: [PATCH 15/25] readme++ --- x/incentive/README.md | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/x/incentive/README.md b/x/incentive/README.md index 698f443a01..00a78cd76b 100644 --- a/x/incentive/README.md +++ b/x/incentive/README.md @@ -30,7 +30,7 @@ A user can bond their `x/leverage` collaterized `uTokens` in a `x/incentive` mod Bonding prevents the user from using any `leverage.MsgDecollateralize` or `leverage.MsgWithdraw` which would reduce the user's collateral below the bonded amount. -**Example:** a user has `100 u/UMEE` and `50 u/UMEE` collateral in the leverage module. `40 u/UMEE` from that `50 u/UMEE` is bonded in the incentive module. Their maximum `leverage.MsgDecollateralize` allowed by their bond is `10 u/UMEE` and their maximum `leverage.MsgWithdraw` is `110u/UMEE`. +**Example:** a user has `100 u/UMEE` in their wallet and `50 u/UMEE` collateral in the leverage module. `40 u/UMEE` from that `50 u/UMEE` is bonded in the incentive module. Their maximum `leverage.MsgDecollateralize` allowed by their bond is `10 u/UMEE` and their maximum `leverage.MsgWithdraw` is `110u/UMEE`. Bonded collateral is eligible for incentive program rewards as long as it is not currently unbonding. @@ -67,6 +67,12 @@ Reward distribution math is When multiple incentive programs are active simultaneously, they compute their rewards independently. +Additionally, when no users are eligible for an incentive program's rewards while it is active, it refrains from distributing any rewards until eligible bonds exist. + +For example, an incentive program which saw no bonded users for the first 25% of its duration would distribute 100% of its rewards over the remaining 75% duration. + +If no users are bonded at the end of an incentive program, it will end with nonzero `RemainingRewards` and those rewards will stay in the module balance. + ### Claiming Rewards A user can claim rewards for all of their bonded uTokens at once using `MsgClaim`. When a user claims rewards, an appropriate amount of tokens are sent from the `x/incentive` module account to their wallet. From 5c70b204ce89d1a028f342d5b08c58129cd101eb Mon Sep 17 00:00:00 2001 From: toteki <63419657+toteki@users.noreply.github.com> Date: Tue, 11 Jul 2023 12:07:42 -0600 Subject: [PATCH 16/25] zeroCoins --- x/incentive/keeper/scenario_test.go | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/x/incentive/keeper/scenario_test.go b/x/incentive/keeper/scenario_test.go index 1cce6fc150..7b8f4b78f5 100644 --- a/x/incentive/keeper/scenario_test.go +++ b/x/incentive/keeper/scenario_test.go @@ -19,6 +19,8 @@ const ( uAtom = leveragetypes.UTokenPrefix + fixtures.AtomDenom ) +var zeroCoins = sdk.NewCoins() + // TestBasicIncentivePrograms runs an incentive program test scenario. // In this scenario, three separate incentive programs with varying start times // and funding amounts are run, with two users bonding at various times. @@ -111,7 +113,7 @@ func TestBasicIncentivePrograms(t *testing.T) { require.NoError(t, err) require.Equal( t, - sdk.NewCoins(), + zeroCoins, rewards, "bob pending rewards at time 101", ) @@ -201,10 +203,10 @@ func TestBasicIncentivePrograms(t *testing.T) { // no more pending rewards after claiming rewards, err = k.calculateRewards(k.ctx, alice) require.NoError(k.t, err) - require.Equal(k.t, sdk.NewCoins(), rewards, "alice pending rewards after claim") + require.Equal(k.t, zeroCoins, rewards, "alice pending rewards after claim") rewards, err = k.calculateRewards(k.ctx, bob) require.NoError(k.t, err) - require.Equal(k.t, sdk.NewCoins(), rewards, "bob pending rewards after claim") + require.Equal(k.t, zeroCoins, rewards, "bob pending rewards after claim") } // TestZeroBonded runs an incentive program test scenario. @@ -264,10 +266,10 @@ func TestZeroBonded(t *testing.T) { // try to make another claim. The rewards should be zero rewards, err = k.calculateRewards(k.ctx, alice) require.NoError(k.t, err) - require.Equal(k.t, sdk.NewCoins(), rewards, "alice pending rewards after claim") + require.Equal(k.t, zeroCoins, rewards, "alice pending rewards after claim") rewards, err = k.UpdateAccount(k.ctx, alice) require.NoError(k.t, err) - require.Equal(k.t, sdk.NewCoins(), rewards, "alice claimed rewards after claim") + require.Equal(k.t, zeroCoins, rewards, "alice claimed rewards after claim") } // TestZeroBondedAtProgramEnd runs an incentive program test scenario. @@ -323,15 +325,14 @@ func TestZeroBondedAtProgramEnd(t *testing.T) { require.Equal(t, sdk.NewInt(3_333334), program.RemainingRewards.Amount, "two thirds of program rewards distributed") // measure pending rewards (zero) - noRewards := sdk.NewCoins() rewards, err = k.calculateRewards(k.ctx, alice) require.NoError(t, err) - require.Equal(t, noRewards, rewards, "alice pending rewards at time 200") + require.Equal(t, zeroCoins, rewards, "alice pending rewards at time 200") // actually claim the rewards (same amount) rewards, err = k.UpdateAccount(k.ctx, alice) require.NoError(k.t, err) - require.Equal(k.t, noRewards, rewards, "alice claimed rewards at time 200") + require.Equal(k.t, zeroCoins, rewards, "alice claimed rewards at time 200") } // TestUserSupplyBeforeAndDuring runs an incentive program test scenario. @@ -547,7 +548,7 @@ func TestRejoinScenario(t *testing.T) { rewards, err := k.calculateRewards(k.ctx, alice) require.NoError(t, err) aliceBalance := coin.UmeeCoins(1_500000) - require.Equal(t, sdk.NewCoins(), rewards, "alice pending rewards at time 200") + require.Equal(t, zeroCoins, rewards, "alice pending rewards at time 200") require.Equal(t, aliceBalance, k.bankKeeper.SpendableCoins(k.ctx, alice), "alice balance at time 200") // measure pending rewards (bob claimed his rewards from before unbond, but not after second bond) @@ -561,7 +562,7 @@ func TestRejoinScenario(t *testing.T) { // claim the rewards (same amounts) rewards, err = k.UpdateAccount(k.ctx, alice) require.NoError(k.t, err) - require.Equal(k.t, sdk.NewCoins(), rewards, "alice claimed rewards at time 200") + require.Equal(k.t, zeroCoins, rewards, "alice claimed rewards at time 200") rewards, err = k.UpdateAccount(k.ctx, bob) require.NoError(k.t, err) require.Equal(k.t, bobRewards, rewards, "bob claimed rewards at time 200") From 0c0cf21fa047a43d82c43316fc0772b9b608ab08 Mon Sep 17 00:00:00 2001 From: toteki <63419657+toteki@users.noreply.github.com> Date: Tue, 11 Jul 2023 12:08:55 -0600 Subject: [PATCH 17/25] readme --- x/incentive/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x/incentive/README.md b/x/incentive/README.md index 00a78cd76b..1bdd3b56b5 100644 --- a/x/incentive/README.md +++ b/x/incentive/README.md @@ -67,7 +67,7 @@ Reward distribution math is When multiple incentive programs are active simultaneously, they compute their rewards independently. -Additionally, when no users are eligible for an incentive program's rewards while it is active, it refrains from distributing any rewards until eligible bonds exist. +Additionally, if no users are bonded while it is active, a program refrains from distributing any rewards until bonded users exist. For example, an incentive program which saw no bonded users for the first 25% of its duration would distribute 100% of its rewards over the remaining 75% duration. From 85c54067f5fe06544424946acef926ff0bdb0498 Mon Sep 17 00:00:00 2001 From: toteki <63419657+toteki@users.noreply.github.com> Date: Tue, 11 Jul 2023 12:20:31 -0600 Subject: [PATCH 18/25] suggestions lines 283-344 and test fix --- x/incentive/keeper/scenario_test.go | 30 ++++++++++++++++++----------- x/incentive/keeper/unit_test.go | 12 ++++++++++++ 2 files changed, 31 insertions(+), 11 deletions(-) diff --git a/x/incentive/keeper/scenario_test.go b/x/incentive/keeper/scenario_test.go index 7b8f4b78f5..9cf73ae9c3 100644 --- a/x/incentive/keeper/scenario_test.go +++ b/x/incentive/keeper/scenario_test.go @@ -295,17 +295,25 @@ func TestZeroBondedAtProgramEnd(t *testing.T) { require.Equal(t, incentive.ProgramStatusOngoing, k.programStatus(1), "program 1 status ongoing (time 125)") require.Equal(t, program.TotalRewards, program.RemainingRewards, "all of program's rewards remain (no bonds)") - // now create a supplier with bonded tokens, halfway through the program - alice := k.newBondedAccount( - coin.New(uUmee, 100_000000), - ) + // now bond first tokens (at 25% of the progrum duration) + alice := k.newBondedAccount(coin.New(uUmee, 100_000000)) + + k.advanceTimeTo(programStart + 50) // 50% duration + program = k.getProgram(1) + require.Equal(t, incentive.ProgramStatusOngoing, k.programStatus(1), "program 1 status ongoing (time 150)") + require.Equal(t, sdk.NewInt(6_666667), program.RemainingRewards.Amount, "one third of program rewards distributed") + + // unbond half of the supply. Since Alice is is the only supplier, this should not change reward distribution + // also, alice claims rewards when unbonding + k.mustBeginUnbond(alice, coin.New(uUmee, 50_000000)) + k.advanceTimeTo(programStart + 75) // 75% duration program = k.getProgram(1) require.Equal(t, incentive.ProgramStatusOngoing, k.programStatus(1), "program 1 status ongoing (time 175)") require.Equal(t, sdk.NewInt(3_333334), program.RemainingRewards.Amount, "two thirds of program rewards distributed") // measure pending rewards - aliceReward := coin.UmeeCoins(6_666666) + aliceReward := coin.UmeeCoins(3_333333) rewards, err := k.calculateRewards(k.ctx, alice) require.NoError(t, err) require.Equal(t, aliceReward, rewards, "alice pending rewards at time 175") @@ -315,24 +323,24 @@ func TestZeroBondedAtProgramEnd(t *testing.T) { require.NoError(k.t, err) require.Equal(k.t, aliceReward, rewards, "alice claimed rewards at time 175") - // begin unbonding user at 75%, making her ineligible future rewards unless she bonds again - k.mustBeginUnbond(alice, coin.New(uUmee, 100_000000)) + // fully unbond user at 75%, making her ineligible future rewards unless she bonds again + k.mustUnbond(alice, coin.New(uUmee, 100_000000)) // complete the program - k.advanceTimeTo(programStart + 100) // 100% duration + k.advanceTimeTo(programStart + 110) // a bit past 100% duration program = k.getProgram(1) - require.Equal(t, incentive.ProgramStatusCompleted, k.programStatus(1), "program 1 status completed (time 200)") + require.Equal(t, incentive.ProgramStatusCompleted, k.programStatus(1), "program 1 status completed (time 210)") require.Equal(t, sdk.NewInt(3_333334), program.RemainingRewards.Amount, "two thirds of program rewards distributed") // measure pending rewards (zero) rewards, err = k.calculateRewards(k.ctx, alice) require.NoError(t, err) - require.Equal(t, zeroCoins, rewards, "alice pending rewards at time 200") + require.Equal(t, zeroCoins, rewards, "alice pending rewards at time 210") // actually claim the rewards (same amount) rewards, err = k.UpdateAccount(k.ctx, alice) require.NoError(k.t, err) - require.Equal(k.t, zeroCoins, rewards, "alice claimed rewards at time 200") + require.Equal(k.t, zeroCoins, rewards, "alice claimed rewards at time 210") } // TestUserSupplyBeforeAndDuring runs an incentive program test scenario. diff --git a/x/incentive/keeper/unit_test.go b/x/incentive/keeper/unit_test.go index 4951835fea..ce176625e9 100644 --- a/x/incentive/keeper/unit_test.go +++ b/x/incentive/keeper/unit_test.go @@ -111,6 +111,18 @@ func (k *testKeeper) mustBeginUnbond(addr sdk.AccAddress, coins ...sdk.Coin) { } } +// mustUnbond immediately unbonds utokens from an account and requires no errors. Use when setting up incentive scenarios. +func (k *testKeeper) mustUnbond(addr sdk.AccAddress, coins ...sdk.Coin) { + for _, coin := range coins { + msg := &incentive.MsgEmergencyUnbond{ + Account: addr.String(), + UToken: coin, + } + _, err := k.msrv.EmergencyUnbond(k.ctx, msg) + require.NoError(k.t, err, "emergency unbonding") + } +} + // initCommunityFund funds the mock bank keeper's distribution module account with some tokens func (k *testKeeper) initCommunityFund(funds ...sdk.Coin) { k.bk.FundModule(disttypes.ModuleName, funds) From 091e0601165cde04781d9ab557d26d99b846cd8e Mon Sep 17 00:00:00 2001 From: toteki <63419657+toteki@users.noreply.github.com> Date: Tue, 11 Jul 2023 12:24:45 -0600 Subject: [PATCH 19/25] defaultSetup --- x/incentive/keeper/scenario_test.go | 49 +++++++++-------------------- 1 file changed, 14 insertions(+), 35 deletions(-) diff --git a/x/incentive/keeper/scenario_test.go b/x/incentive/keeper/scenario_test.go index 9cf73ae9c3..72558d5309 100644 --- a/x/incentive/keeper/scenario_test.go +++ b/x/incentive/keeper/scenario_test.go @@ -215,14 +215,8 @@ func TestBasicIncentivePrograms(t *testing.T) { // During the remaining half of the program, all rewards must be distributed (spread evenly over // the remaining time.) func TestZeroBonded(t *testing.T) { - t.Parallel() - k := newTestKeeper(t) - k.initCommunityFund( - coin.New(umee, 1000_000000), - ) + k, programStart := defaultSetup(t) - programStart := int64(100) - k.addIncentiveProgram(uUmee, programStart, 100, sdk.NewInt64Coin(umee, 10_000000), true) k.advanceTimeTo(programStart) // starts program, but does not attempt rewards. Do not combine with next line. k.advanceTimeTo(programStart + 50) program := k.getProgram(1) @@ -281,14 +275,8 @@ func TestZeroBonded(t *testing.T) { // once all users unbond after 75% duration and never return, the program is left with some rewards // it cannot distribute. func TestZeroBondedAtProgramEnd(t *testing.T) { - t.Parallel() - k := newTestKeeper(t) - k.initCommunityFund( - coin.New(umee, 1000_000000), - ) + k, programStart := defaultSetup(t) - programStart := int64(100) - k.addIncentiveProgram(uUmee, programStart, 100, sdk.NewInt64Coin(umee, 10_000000), true) k.advanceTimeTo(programStart) // starts program, but does not attempt rewards. Do not combine with next line. k.advanceTimeTo(programStart + 25) // 25% duration program := k.getProgram(1) @@ -343,10 +331,8 @@ func TestZeroBondedAtProgramEnd(t *testing.T) { require.Equal(k.t, zeroCoins, rewards, "alice claimed rewards at time 210") } -// TestUserSupplyBeforeAndDuring runs an incentive program test scenario. -// In this test case, A user supplies and bonds uUmee before the incentive program starts -// and another user supplies half way through the incentive program. -func TestUserSupplyBeforeAndDuring(t *testing.T) { +// defaultSetup creates a parallel test with a basic 10 UMEE incentive program already funded. +func defaultSetup(t *testing.T) (testKeeper, int64) { t.Parallel() k := newTestKeeper(t) k.initCommunityFund( @@ -355,6 +341,14 @@ func TestUserSupplyBeforeAndDuring(t *testing.T) { programStart := int64(100) k.addIncentiveProgram(uUmee, programStart, 100, sdk.NewInt64Coin(umee, 10_000000), true) + return k, programStart +} + +// TestUserSupplyBeforeAndDuring runs an incentive program test scenario. +// In this test case, A user supplies and bonds uUmee before the incentive program starts +// and another user supplies half way through the incentive program. +func TestUserSupplyBeforeAndDuring(t *testing.T) { + k, programStart := defaultSetup(t) // now create a supplier with bonded tokens before the time starts k.advanceTimeTo(80) @@ -425,14 +419,7 @@ func TestUserSupplyBeforeAndDuring(t *testing.T) { // and another user supplies half way through the incentive program. The second user then // withdraws ~3/4 into the incentive program. func TestPartialWithdraw(t *testing.T) { - t.Parallel() - k := newTestKeeper(t) - k.initCommunityFund( - coin.New(umee, 1000_000000), - ) - - programStart := int64(100) - k.addIncentiveProgram(uUmee, programStart, 100, sdk.NewInt64Coin(umee, 10_000000), true) + k, programStart := defaultSetup(t) // now create a supplier with bonded tokens before the time starts k.advanceTimeTo(80) @@ -510,15 +497,7 @@ func TestPartialWithdraw(t *testing.T) { // TestRejoinScenario runs a scenario whe func TestRejoinScenario(t *testing.T) { - t.Parallel() - k := newTestKeeper(t) - k.initCommunityFund( - coin.New(umee, 1000_000000), - ) - - // an incentive program with 10 UMEE rewards will run from t=100 to t=200 - programStart := int64(100) - k.addIncentiveProgram(uUmee, programStart, 100, sdk.NewInt64Coin(umee, 10_000000), true) + k, programStart := defaultSetup(t) // create two bonded accounts before the program starts. Alice bonds 3x what bob does. k.advanceTimeTo(80) From b571459248dd7fc006620953fe8bef846afafe5d Mon Sep 17 00:00:00 2001 From: toteki <63419657+toteki@users.noreply.github.com> Date: Tue, 11 Jul 2023 12:27:24 -0600 Subject: [PATCH 20/25] move function --- x/incentive/keeper/scenario_test.go | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/x/incentive/keeper/scenario_test.go b/x/incentive/keeper/scenario_test.go index 72558d5309..f49e24bffb 100644 --- a/x/incentive/keeper/scenario_test.go +++ b/x/incentive/keeper/scenario_test.go @@ -21,6 +21,19 @@ const ( var zeroCoins = sdk.NewCoins() +// defaultSetup creates a parallel test with a basic 10 UMEE incentive program already funded. +func defaultSetup(t *testing.T) (testKeeper, int64) { + t.Parallel() + k := newTestKeeper(t) + k.initCommunityFund( + coin.New(umee, 1000_000000), + ) + + programStart := int64(100) + k.addIncentiveProgram(uUmee, programStart, 100, sdk.NewInt64Coin(umee, 10_000000), true) + return k, programStart +} + // TestBasicIncentivePrograms runs an incentive program test scenario. // In this scenario, three separate incentive programs with varying start times // and funding amounts are run, with two users bonding at various times. @@ -331,19 +344,6 @@ func TestZeroBondedAtProgramEnd(t *testing.T) { require.Equal(k.t, zeroCoins, rewards, "alice claimed rewards at time 210") } -// defaultSetup creates a parallel test with a basic 10 UMEE incentive program already funded. -func defaultSetup(t *testing.T) (testKeeper, int64) { - t.Parallel() - k := newTestKeeper(t) - k.initCommunityFund( - coin.New(umee, 1000_000000), - ) - - programStart := int64(100) - k.addIncentiveProgram(uUmee, programStart, 100, sdk.NewInt64Coin(umee, 10_000000), true) - return k, programStart -} - // TestUserSupplyBeforeAndDuring runs an incentive program test scenario. // In this test case, A user supplies and bonds uUmee before the incentive program starts // and another user supplies half way through the incentive program. From 647b38a9cc0d77241ead10446f840cd410085288 Mon Sep 17 00:00:00 2001 From: toteki <63419657+toteki@users.noreply.github.com> Date: Tue, 11 Jul 2023 12:28:20 -0600 Subject: [PATCH 21/25] comment++ --- x/incentive/keeper/scenario_test.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/x/incentive/keeper/scenario_test.go b/x/incentive/keeper/scenario_test.go index f49e24bffb..2a06926d2d 100644 --- a/x/incentive/keeper/scenario_test.go +++ b/x/incentive/keeper/scenario_test.go @@ -495,7 +495,8 @@ func TestPartialWithdraw(t *testing.T) { require.Equal(k.t, bobRewards, rewards, "bob claimed rewards at time 220") } -// TestRejoinScenario runs a scenario whe +// TestRejoinScenario runs a scenario where two users start a program bonded, then both leave +// and one rejoins to earn remaining rewards before the program ends. func TestRejoinScenario(t *testing.T) { k, programStart := defaultSetup(t) From 92a1c576924d0ab33cea57bafa4b1df8dd7fe1d9 Mon Sep 17 00:00:00 2001 From: Adam Moser <63419657+toteki@users.noreply.github.com> Date: Tue, 11 Jul 2023 12:29:01 -0600 Subject: [PATCH 22/25] Update x/incentive/keeper/scenario_test.go Co-authored-by: Robert Zaremba --- x/incentive/keeper/scenario_test.go | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/x/incentive/keeper/scenario_test.go b/x/incentive/keeper/scenario_test.go index 2a06926d2d..e08f7ec045 100644 --- a/x/incentive/keeper/scenario_test.go +++ b/x/incentive/keeper/scenario_test.go @@ -502,12 +502,10 @@ func TestRejoinScenario(t *testing.T) { // create two bonded accounts before the program starts. Alice bonds 3x what bob does. k.advanceTimeTo(80) - alice := k.newBondedAccount( - coin.New(uUmee, 30_000000), - ) - bob := k.newBondedAccount( - coin.New(uUmee, 10_000000), - ) + aliceSupply = coin.New(uUmee, 30_000000) + alice := k.newBondedAccount(aliceSupply) + bobSupply = coin.New(uUmee, 30_000000) + bob := k.newBondedAccount(bobSupply) k.advanceTimeTo(programStart) // starts program, k.advanceTimeTo(programStart + 20) // time passed 20% From a8b175b33baddd043fd9c84218dde5dab4e73c95 Mon Sep 17 00:00:00 2001 From: toteki <63419657+toteki@users.noreply.github.com> Date: Tue, 11 Jul 2023 12:30:09 -0600 Subject: [PATCH 23/25] fix --- x/incentive/keeper/scenario_test.go | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/x/incentive/keeper/scenario_test.go b/x/incentive/keeper/scenario_test.go index e08f7ec045..75fefb6a44 100644 --- a/x/incentive/keeper/scenario_test.go +++ b/x/incentive/keeper/scenario_test.go @@ -502,26 +502,26 @@ func TestRejoinScenario(t *testing.T) { // create two bonded accounts before the program starts. Alice bonds 3x what bob does. k.advanceTimeTo(80) - aliceSupply = coin.New(uUmee, 30_000000) + aliceSupply := coin.New(uUmee, 30_000000) alice := k.newBondedAccount(aliceSupply) - bobSupply = coin.New(uUmee, 30_000000) + bobSupply := coin.New(uUmee, 10_000000) bob := k.newBondedAccount(bobSupply) k.advanceTimeTo(programStart) // starts program, k.advanceTimeTo(programStart + 20) // time passed 20% // alice unbonds, losing reward eligibility - k.mustBeginUnbond(alice, coin.New(uUmee, 30_000000)) + k.mustBeginUnbond(alice, aliceSupply) k.advanceTimeTo(programStart + 30) // time passed 30% // bob unbonds, losing reward eligibility - k.mustBeginUnbond(bob, coin.New(uUmee, 10_000000)) + k.mustBeginUnbond(bob, bobSupply) k.advanceTimeTo(programStart + 50) // time passed 50% // bob bonds once more at 50% time elapsed - k.mustBond(bob, coin.New(uUmee, 10_000000)) + k.mustBond(bob, bobSupply) k.advanceTimeTo(programStart + 100) // time passed 100% From d1dddf5f0e64615bfd98522ec6393fab85406824 Mon Sep 17 00:00:00 2001 From: toteki <63419657+toteki@users.noreply.github.com> Date: Tue, 11 Jul 2023 12:31:46 -0600 Subject: [PATCH 24/25] check bob rewards --- x/incentive/keeper/scenario_test.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/x/incentive/keeper/scenario_test.go b/x/incentive/keeper/scenario_test.go index 75fefb6a44..ffd3781d36 100644 --- a/x/incentive/keeper/scenario_test.go +++ b/x/incentive/keeper/scenario_test.go @@ -521,6 +521,8 @@ func TestRejoinScenario(t *testing.T) { k.advanceTimeTo(programStart + 50) // time passed 50% // bob bonds once more at 50% time elapsed + rewards, err := k.calculateRewards(k.ctx, bob) + require.Equal(t, zeroCoins, rewards, "bob pending rewards at time 150 (zero after unbond)") k.mustBond(bob, bobSupply) k.advanceTimeTo(programStart + 100) // time passed 100% @@ -531,7 +533,7 @@ func TestRejoinScenario(t *testing.T) { require.Equal(t, sdk.ZeroInt(), program.RemainingRewards.Amount, "all of program rewards distributed") // measure pending rewards and wallet balance (alice claimed rewards, as part of the beginUnbonding transaction) - rewards, err := k.calculateRewards(k.ctx, alice) + rewards, err = k.calculateRewards(k.ctx, alice) require.NoError(t, err) aliceBalance := coin.UmeeCoins(1_500000) require.Equal(t, zeroCoins, rewards, "alice pending rewards at time 200") From 866ad3102f95a3595870e48c988eb321c80cc056 Mon Sep 17 00:00:00 2001 From: toteki <63419657+toteki@users.noreply.github.com> Date: Tue, 11 Jul 2023 12:37:55 -0600 Subject: [PATCH 25/25] calculations --- x/incentive/keeper/scenario_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/x/incentive/keeper/scenario_test.go b/x/incentive/keeper/scenario_test.go index ffd3781d36..14c0cac47c 100644 --- a/x/incentive/keeper/scenario_test.go +++ b/x/incentive/keeper/scenario_test.go @@ -535,14 +535,14 @@ func TestRejoinScenario(t *testing.T) { // measure pending rewards and wallet balance (alice claimed rewards, as part of the beginUnbonding transaction) rewards, err = k.calculateRewards(k.ctx, alice) require.NoError(t, err) - aliceBalance := coin.UmeeCoins(1_500000) + aliceBalance := coin.UmeeCoins(10_000000 * 3 / 4 * 2 / 10) require.Equal(t, zeroCoins, rewards, "alice pending rewards at time 200") require.Equal(t, aliceBalance, k.bankKeeper.SpendableCoins(k.ctx, alice), "alice balance at time 200") // measure pending rewards (bob claimed his rewards from before unbond, but not after second bond) rewards, err = k.calculateRewards(k.ctx, bob) require.NoError(t, err) - bobRewards := coin.UmeeCoins(7_000000) + bobRewards := coin.UmeeCoins(10_000000 * 7 / 10) bobBalance := coin.UmeeCoins(1_500000) require.Equal(t, bobRewards, rewards, "bob pending rewards at time 200") require.Equal(t, bobBalance, k.bankKeeper.SpendableCoins(k.ctx, bob), "bob balance at time 200")