Skip to content

Commit

Permalink
Merge pull request #7 from sourcenetwork/iverc/tier-module-improvements
Browse files Browse the repository at this point in the history
fix: Tier module improvements
  • Loading branch information
iverc authored Nov 27, 2024
2 parents e7bd275 + 557449a commit ae12686
Show file tree
Hide file tree
Showing 19 changed files with 732 additions and 206 deletions.
3 changes: 3 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ jobs:
uses: actions/checkout@v2
with:
fetch-depth: 0
submodules: true

- name: Prepare Release Variables
id: vars
Expand All @@ -30,6 +31,8 @@ jobs:
- name: Issue Release Assets
uses: ignite/cli/actions/cli@main
if: ${{ steps.vars.outputs.should_release == 'true' }}
env:
GOFLAGS: "-buildvcs=false"
with:
args: chain build --release --release.prefix ${{ steps.vars.outputs.tarball_prefix }} -t linux:amd64 -t darwin:amd64 -t darwin:arm64

Expand Down
174 changes: 125 additions & 49 deletions api/sourcehub/tier/v1beta1/lockup.pulsar.go

Large diffs are not rendered by default.

57 changes: 31 additions & 26 deletions api/sourcehub/tier/v1beta1/params.pulsar.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

11 changes: 6 additions & 5 deletions proto/sourcehub/tier/v1beta1/lockup.proto
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,9 @@ option go_package = "github.com/sourcenetwork/sourcehub/x/tier/types";

// Lockup tracks the locked and unlocking stake of a delegator.
message Lockup {
string validator_address = 1 [(cosmos_proto.scalar) = "cosmos.ValidatorAddressString"];
string amount = 2 [
string delegator_address = 1 [(cosmos_proto.scalar) = "cosmos.AddressString"];
string validator_address = 2 [(cosmos_proto.scalar) = "cosmos.ValidatorAddressString"];
string amount = 3 [
(cosmos_proto.scalar) = "cosmos.Int",
(gogoproto.customtype) = "cosmossdk.io/math.Int",
(gogoproto.nullable) = false
Expand All @@ -19,11 +20,11 @@ message Lockup {
// The following fields are only used for unlocking lockups.
//
// The height at which the lockup was created.
int64 creation_height = 3;
int64 creation_height = 4;

// The time at which the stake undelegation will be completed.
google.protobuf.Timestamp unbond_time = 4 [(gogoproto.stdtime) = true];
google.protobuf.Timestamp unbond_time = 5 [(gogoproto.stdtime) = true];

// The time at which the stake unlocking will be completed.
google.protobuf.Timestamp unlock_time = 5 [(gogoproto.stdtime) = true];
google.protobuf.Timestamp unlock_time = 6 [(gogoproto.stdtime) = true];
}
2 changes: 1 addition & 1 deletion proto/sourcehub/tier/v1beta1/params.proto
Original file line number Diff line number Diff line change
Expand Up @@ -26,5 +26,5 @@ message Rate {
(gogoproto.customtype) = "cosmossdk.io/math.Int",
(gogoproto.nullable) = false
];
double rate = 2;
int64 rate = 2;
}
8 changes: 4 additions & 4 deletions scripts/ignite/config-tier.yml
Original file line number Diff line number Diff line change
Expand Up @@ -58,10 +58,10 @@ genesis:
unlocking_epochs: 2
reward_rates:
- amount: '300'
rate: 1.50
rate: 150
- amount: '200'
rate: 1.20
rate: 120
- amount: '100'
rate: 1.10
rate: 110
- amount: '0'
rate: 1.00
rate: 100
22 changes: 8 additions & 14 deletions x/tier/keeper/credit.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,8 @@ import (
"github.com/sourcenetwork/sourcehub/x/tier/types"
)

// Mintcredit mints a coin and sends it to the specified address.
// mintCredit mints a coin and sends it to the specified address.
func (k Keeper) mintCredit(ctx context.Context, addr sdk.AccAddress, amt math.Int) error {

coins := sdk.NewCoins(sdk.NewCoin(appparams.CreditDenom, amt))
err := k.bankKeeper.MintCoins(ctx, types.ModuleName, coins)
if err != nil {
Expand All @@ -31,7 +30,6 @@ func (k Keeper) mintCredit(ctx context.Context, addr sdk.AccAddress, amt math.In

// proratedCredit calculates the credits earned on the lockingAmt.
func (k Keeper) proratedCredit(ctx context.Context, delAddr sdk.AccAddress, lockingAmt math.Int) math.Int {

// Calculate the reward credits earned on the new lock.
rates := k.GetParams(ctx).RewardRates
lockedAmt := k.TotalAmountByAddr(ctx, delAddr)
Expand All @@ -43,7 +41,7 @@ func (k Keeper) proratedCredit(ctx context.Context, delAddr sdk.AccAddress, lock
epochDuration := epochInfo.Duration.Milliseconds()

// TODO: is this check necessary?
// Uner what condition can sinceCurrentEpoch be greater than epochDuration?
// Under what condition can sinceCurrentEpoch be greater than epochDuration?
// What happens if the chain is paused for a long time?
if sinceCurrentEpoch < epochDuration {
credit = credit.MulRaw(sinceCurrentEpoch).QuoRaw(epochDuration)
Expand All @@ -55,11 +53,9 @@ func (k Keeper) proratedCredit(ctx context.Context, delAddr sdk.AccAddress, lock
// burnAllCredits burns all the reward credits in the system.
// It is called at the end of each epoch.
func (k Keeper) burnAllCredits(ctx context.Context) error {

// Note that we can't simply iterate through the lockup records because credits
// are transferrable and can be stored in accounts that are not tracked by lockups.
// Instead, we iterate through all the balances to find and burn the credits.

var err error

cb := func(addr sdk.AccAddress, coin sdk.Coin) (stop bool) {
Expand Down Expand Up @@ -91,12 +87,10 @@ func (k Keeper) burnAllCredits(ctx context.Context) error {

// resetAllCredits resets all the credits in the system.
func (k Keeper) resetAllCredits(ctx context.Context) error {

// Reward to a delegator is calculated based on the total locked amount
// to all validators. Since each lockup entry only records locked amount
// for a single validator, we need to iterate through all the lockups to
// calculate the total locked amount for each delegator.

lockedAmts := make(map[string]math.Int)

cb := func(delAddr sdk.AccAddress, valAddr sdk.ValAddress, lockup types.Lockup) {
Expand All @@ -107,12 +101,11 @@ func (k Keeper) resetAllCredits(ctx context.Context) error {
lockedAmts[delAddr.String()] = amt.Add(lockup.Amount)
}

k.mustIterateLockups(ctx, false, cb)
k.MustIterateLockups(ctx, false, cb)

rates := k.GetParams(ctx).RewardRates

for delStrAddr, amt := range lockedAmts {

delAddr := sdk.MustAccAddressFromBech32(delStrAddr)
credit := calculateCredit(rates, math.ZeroInt(), amt)
err := k.mintCredit(ctx, delAddr, credit)
Expand All @@ -128,13 +121,11 @@ func (k Keeper) resetAllCredits(ctx context.Context) error {
// lockingAmt is stacked up on top of the lockedAmt to earn at the
// highest eligible reward.
func calculateCredit(rateList []types.Rate, lockedAmt, lockingAmt math.Int) math.Int {

credit := math.ZeroInt()
stakedAmt := lockedAmt.Add(lockingAmt)

// Iterate from the highest reward rate to the lowest.
for _, r := range rateList {

// Continue if the total lock does not reach the current rate requirement.
if stakedAmt.LT(r.Amount) {
continue
Expand All @@ -143,8 +134,11 @@ func calculateCredit(rateList []types.Rate, lockedAmt, lockingAmt math.Int) math
lower := math.MaxInt(r.Amount, lockedAmt)
diff := stakedAmt.Sub(lower)

amt := float64(diff.Int64()) * r.Rate
credit = credit.AddRaw(int64(amt))
diffDec := math.LegacyNewDecFromInt(diff)
rateDec := math.LegacyNewDec(r.Rate)

amt := diffDec.Mul(rateDec).Quo(math.LegacyNewDec(100))
credit = credit.Add(amt.TruncateInt())

// Subtract the lock that has been rewarded.
stakedAmt = stakedAmt.Sub(diff)
Expand Down
10 changes: 4 additions & 6 deletions x/tier/keeper/credit_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,11 @@ import (
)

func Test_calReward(t *testing.T) {

rateList := []types.Rate{
{Amount: math.NewInt(300), Rate: 1.50},
{Amount: math.NewInt(200), Rate: 1.20},
{Amount: math.NewInt(100), Rate: 1.10},
{Amount: math.NewInt(0), Rate: 1.00},
{Amount: math.NewInt(300), Rate: 150},
{Amount: math.NewInt(200), Rate: 120},
{Amount: math.NewInt(100), Rate: 110},
{Amount: math.NewInt(0), Rate: 100},
}

tests := []struct {
Expand Down Expand Up @@ -86,7 +85,6 @@ func Test_calReward(t *testing.T) {
},
}
for _, tt := range tests {

name := fmt.Sprintf("%d adds %d", tt.lockedAmt, tt.lockingAmt)
oldLock := math.NewInt(tt.lockedAmt)
newLock := math.NewInt(tt.lockingAmt)
Expand Down
8 changes: 4 additions & 4 deletions x/tier/keeper/grpc_query.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,9 +50,10 @@ func (q Querier) Lockup(ctx context.Context, req *types.LockupRequest) (
return nil, status.Error(codes.InvalidArgument, "invalid validator address")
}

amt := q.getLockup(ctx, delAddr, valAddr)
amt := q.GetLockup(ctx, delAddr, valAddr)

lockup := &types.Lockup{
DelegatorAddress: req.DelegatorAddress,
ValidatorAddress: req.ValidatorAddress,
Amount: amt,
}
Expand Down Expand Up @@ -99,10 +100,11 @@ func (q Querier) UnlockingLockup(ctx context.Context, req *types.UnlockingLockup

found, amt, unbondTime, unlockTime := q.getUnlockingLockup(ctx, delAddr, valAddr)
if !found {
return nil, status.Error(codes.NotFound, "not found")
return &types.UnlockingLockupResponse{Lockup: types.Lockup{ValidatorAddress: req.ValidatorAddress, Amount: amt}}, nil
}

lockup := &types.Lockup{
DelegatorAddress: req.DelegatorAddress,
ValidatorAddress: req.ValidatorAddress,
Amount: amt,
UnbondTime: &unbondTime,
Expand Down Expand Up @@ -136,10 +138,8 @@ func (q Querier) getLockupsPaginated(ctx context.Context, unlocking bool, delAdd
[]types.Lockup, *query.PageResponse, error) {

var lockups []types.Lockup

store := q.lockupStore(ctx, unlocking)
onResult := func(key []byte, value []byte) error {

if !bytes.HasPrefix(key, delAddr.Bytes()) {
return nil
}
Expand Down
Loading

0 comments on commit ae12686

Please sign in to comment.