Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(participation): decay support #915

Merged
merged 15 commits into from
Aug 10, 2022
10 changes: 10 additions & 0 deletions proto/claim/params.proto
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,20 @@ syntax = "proto3";
package tendermint.spn.claim;

import "gogoproto/gogo.proto";
import "google/protobuf/timestamp.proto";

option go_package = "github.com/tendermint/spn/x/claim/types";

// Params defines the parameters for the module.
message Params {
DecayInformation decayInformation = 1 [(gogoproto.nullable) = false];
option (gogoproto.goproto_stringer) = false;
}

// DecayInformation defines the information about decay for the airdrop
// when claimable airdrop amount starts to decrease and when it ends
message DecayInformation {
bool enabled = 1;
google.protobuf.Timestamp decayStart = 2 [(gogoproto.nullable) = false, (gogoproto.stdtime) = true];
google.protobuf.Timestamp decayEnd = 3 [(gogoproto.nullable) = false, (gogoproto.stdtime) = true];
}
5 changes: 3 additions & 2 deletions testutil/gen_app.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,12 @@ import (
authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
banktypes "github.com/cosmos/cosmos-sdk/x/bank/types"
stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types"
spnapp "github.com/tendermint/spn/app"
"github.com/tendermint/spn/cmd"
"github.com/tendermint/tendermint/libs/log"
tmtypes "github.com/tendermint/tendermint/types"
dbm "github.com/tendermint/tm-db"

spnapp "github.com/tendermint/spn/app"
"github.com/tendermint/spn/cmd"
)

func GenApp(withGenesis bool, invCheckPeriod uint) (*spnapp.App, spnapp.GenesisState) {
Expand Down
2 changes: 1 addition & 1 deletion testutil/keeper/initializer.go
Original file line number Diff line number Diff line change
Expand Up @@ -483,7 +483,7 @@ func (i initializer) Claim(
i.StateStore.MountStoreWithDB(memStoreKey, storetypes.StoreTypeMemory, nil)

paramKeeper.Subspace(claimtypes.ModuleName)
subspace, _ := paramKeeper.GetSubspace(participationtypes.ModuleName)
subspace, _ := paramKeeper.GetSubspace(claimtypes.ModuleName)

return claimkeeper.NewKeeper(
i.Codec,
Expand Down
11 changes: 10 additions & 1 deletion x/claim/keeper/mission.go
Original file line number Diff line number Diff line change
Expand Up @@ -93,8 +93,17 @@ func (k Keeper) CompleteMission(ctx sdk.Context, missionID uint64, address strin
claimableAmount := claimRecord.ClaimableFromMission(mission)
claimable := sdk.NewCoins(sdk.NewCoin(airdropSupply.Denom, claimableAmount))

// calculate claimable after decay factor
decayInfo := k.DecayInformation(ctx)
claimable = decayInfo.ApplyDecayFactor(claimable, ctx.BlockTime())

// check final claimable non-zero
if claimable.Empty() {
return types.ErrNoClaimable
}

// decrease airdrop supply
airdropSupply.Amount = airdropSupply.Amount.Sub(claimableAmount)
airdropSupply.Amount = airdropSupply.Amount.Sub(claimable.AmountOf(airdropSupply.Denom))
if airdropSupply.Amount.IsNegative() {
return spnerrors.Critical("airdrop supply is lower than total claimable")
}
Expand Down
102 changes: 94 additions & 8 deletions x/claim/keeper/mission_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package keeper_test

import (
"testing"
"time"

sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/stretchr/testify/require"
Expand Down Expand Up @@ -70,6 +71,8 @@ func TestKeeper_CompleteMission(t *testing.T) {
airdropSupply sdk.Coin
mission types.Mission
claimRecord types.ClaimRecord
params types.Params
blockTime time.Time
}

// prepare addresses
Expand All @@ -92,6 +95,7 @@ func TestKeeper_CompleteMission(t *testing.T) {
noAirdropSupply: true,
claimRecord: sample.ClaimRecord(r),
mission: sample.Mission(r),
params: types.DefaultParams(),
},
missionID: 1,
address: sample.Address(r),
Expand All @@ -107,6 +111,7 @@ func TestKeeper_CompleteMission(t *testing.T) {
Claimable: sdk.OneInt(),
CompletedMissions: []uint64{1},
},
params: types.DefaultParams(),
},
missionID: 1,
address: addr[0],
Expand All @@ -121,6 +126,7 @@ func TestKeeper_CompleteMission(t *testing.T) {
MissionID: 1,
Weight: sdk.OneDec(),
},
params: types.DefaultParams(),
},
missionID: 1,
address: sample.Address(r),
Expand All @@ -139,6 +145,7 @@ func TestKeeper_CompleteMission(t *testing.T) {
Claimable: sdk.OneInt(),
CompletedMissions: []uint64{1},
},
params: types.DefaultParams(),
},
missionID: 1,
address: addr[1],
Expand All @@ -156,6 +163,7 @@ func TestKeeper_CompleteMission(t *testing.T) {
Address: addr[2],
Claimable: sdk.NewIntFromUint64(10000),
},
params: types.DefaultParams(),
},
missionID: 1,
address: addr[2],
Expand All @@ -173,6 +181,7 @@ func TestKeeper_CompleteMission(t *testing.T) {
Address: "invalid",
Claimable: sdk.OneInt(),
},
params: types.DefaultParams(),
},
missionID: 1,
address: "invalid",
Expand All @@ -190,13 +199,14 @@ func TestKeeper_CompleteMission(t *testing.T) {
Address: addr[3],
Claimable: sdk.NewIntFromUint64(1000),
},
params: types.DefaultParams(),
},
missionID: 1,
address: addr[3],
expectedBalance: tc.Coin(t, "1000foo"),
},
{
name: "should allow distributing no fund for mission with 0 weight",
name: "should prevent distributing fund for mission with 0 weight",
inputState: inputState{
airdropSupply: tc.Coin(t, "1000foo"),
mission: types.Mission{
Expand All @@ -207,10 +217,11 @@ func TestKeeper_CompleteMission(t *testing.T) {
Address: addr[4],
Claimable: sdk.NewIntFromUint64(1000),
},
params: types.DefaultParams(),
},
missionID: 1,
address: addr[4],
expectedBalance: tc.Coin(t, "0foo"),
missionID: 1,
address: addr[4],
err: types.ErrNoClaimable,
},
{
name: "should allow distributing half for mission with 0.5 weight",
Expand All @@ -224,6 +235,7 @@ func TestKeeper_CompleteMission(t *testing.T) {
Address: addr[5],
Claimable: sdk.NewIntFromUint64(500),
},
params: types.DefaultParams(),
},
missionID: 1,
address: addr[5],
Expand All @@ -241,13 +253,14 @@ func TestKeeper_CompleteMission(t *testing.T) {
Address: addr[6],
Claimable: sdk.NewIntFromUint64(201),
},
params: types.DefaultParams(),
},
missionID: 1,
address: addr[6],
expectedBalance: tc.Coin(t, "100foo"),
},
{
name: "should allow distributing no fund for empty claim record",
name: "should prevent distributing fund for empty claim record",
inputState: inputState{
airdropSupply: tc.Coin(t, "1000foo"),
mission: types.Mission{
Expand All @@ -258,10 +271,11 @@ func TestKeeper_CompleteMission(t *testing.T) {
Address: addr[7],
Claimable: sdk.ZeroInt(),
},
params: types.DefaultParams(),
},
missionID: 1,
address: addr[7],
expectedBalance: tc.Coin(t, "0foo"),
missionID: 1,
address: addr[7],
err: types.ErrNoClaimable,
},
{
name: "should allow distributing airdrop with other already completed missions",
Expand All @@ -276,15 +290,84 @@ func TestKeeper_CompleteMission(t *testing.T) {
Claimable: sdk.NewIntFromUint64(10000),
CompletedMissions: []uint64{0, 1, 2, 4, 5, 6},
},
params: types.DefaultParams(),
},
missionID: 3,
address: addr[8],
expectedBalance: tc.Coin(t, "3000bar"),
},
{
name: "should allow applying decay factor if enabled",
inputState: inputState{
airdropSupply: tc.Coin(t, "1000foo"),
mission: types.Mission{
MissionID: 1,
Weight: tc.Dec(t, "0.5"),
},
claimRecord: types.ClaimRecord{
Address: addr[9],
Claimable: sdk.NewIntFromUint64(1000),
},
params: types.NewParams(types.NewEnabledDecay(
time.Unix(1000, 0),
time.Unix(2000, 0),
)),
blockTime: time.Unix(1500, 0),
},
missionID: 1,
address: addr[9],
expectedBalance: tc.Coin(t, "250foo"),
},
{
name: "should allow distributing all funds if decay factor if enabled and decay not started",
inputState: inputState{
airdropSupply: tc.Coin(t, "1000foo"),
mission: types.Mission{
MissionID: 1,
Weight: tc.Dec(t, "0.5"),
},
claimRecord: types.ClaimRecord{
Address: addr[10],
Claimable: sdk.NewIntFromUint64(1000),
},
params: types.NewParams(types.NewEnabledDecay(
time.Unix(1000, 0),
time.Unix(2000, 0),
)),
blockTime: time.Unix(999, 0),
},
missionID: 1,
address: addr[10],
expectedBalance: tc.Coin(t, "500foo"),
},
{
name: "should prevent distributing funds if decay ended",
inputState: inputState{
airdropSupply: tc.Coin(t, "1000foo"),
mission: types.Mission{
MissionID: 1,
Weight: tc.Dec(t, "0.5"),
},
claimRecord: types.ClaimRecord{
Address: addr[11],
Claimable: sdk.NewIntFromUint64(1000),
},
params: types.NewParams(types.NewEnabledDecay(
time.Unix(1000, 0),
time.Unix(2000, 0),
)),
blockTime: time.Unix(2001, 0),
},
missionID: 1,
address: addr[11],
err: types.ErrNoClaimable,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
// initialize input state
require.NoError(t, tt.inputState.params.Validate())
tk.ClaimKeeper.SetParams(ctx, tt.inputState.params)
if !tt.inputState.noAirdropSupply {
err := tk.ClaimKeeper.InitializeAirdropSupply(ctx, tt.inputState.airdropSupply)
require.NoError(t, err)
Expand All @@ -295,6 +378,9 @@ func TestKeeper_CompleteMission(t *testing.T) {
if !tt.inputState.noClaimRecord {
tk.ClaimKeeper.SetClaimRecord(ctx, tt.inputState.claimRecord)
}
if !tt.inputState.blockTime.IsZero() {
ctx = ctx.WithBlockTime(tt.inputState.blockTime)
}

err := tk.ClaimKeeper.CompleteMission(ctx, tt.missionID, tt.address)
if tt.err != nil {
Expand Down
11 changes: 9 additions & 2 deletions x/claim/keeper/params.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,18 @@ import (
)

// GetParams get all parameters as types.Params
func (k Keeper) GetParams(ctx sdk.Context) types.Params {
return types.NewParams()
func (k Keeper) GetParams(ctx sdk.Context) (params types.Params) {
k.paramstore.GetParamSet(ctx, &params)
return params
}

// SetParams set the params
func (k Keeper) SetParams(ctx sdk.Context, params types.Params) {
k.paramstore.SetParamSet(ctx, &params)
}

// DecayInformation returns the param that defines decay information
func (k Keeper) DecayInformation(ctx sdk.Context) (totalSupplyRange types.DecayInformation) {
k.paramstore.Get(ctx, types.KeyDecayInformation, &totalSupplyRange)
return
}
8 changes: 5 additions & 3 deletions x/claim/keeper/params_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package keeper_test

import (
"testing"
"time"

"github.com/stretchr/testify/require"

Expand All @@ -12,9 +13,10 @@ import (
func TestGetParams(t *testing.T) {
ctx, tk, _ := testkeeper.NewTestSetup(t)

params := types.DefaultParams()

params := types.NewParams(types.NewEnabledDecay(
time.Unix(1000, 0),
time.Unix(10000, 0),
))
tk.ClaimKeeper.SetParams(ctx, params)

require.EqualValues(t, params, tk.ClaimKeeper.GetParams(ctx))
}
Loading