Skip to content

Commit

Permalink
feat: update validator payout from arkeo reserve
Browse files Browse the repository at this point in the history
  • Loading branch information
shreyasbhat0 committed Oct 3, 2024
1 parent 94c5bd7 commit bea0b1f
Show file tree
Hide file tree
Showing 6 changed files with 75 additions and 104 deletions.
3 changes: 2 additions & 1 deletion app/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -210,7 +210,8 @@ var (
govtypes.ModuleName: {authtypes.Burner},
ibctransfertypes.ModuleName: {authtypes.Minter, authtypes.Burner},
claimmoduletypes.ModuleName: {authtypes.Minter},
arkeomoduletypes.ModuleName: {authtypes.Minter, authtypes.Burner},
arkeomoduletypes.ReserveName: {},
arkeomoduletypes.ModuleName: {},
arkeomoduletypes.ProviderName: {},
arkeomoduletypes.ContractName: {},
}
Expand Down
26 changes: 5 additions & 21 deletions common/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ const (
)

// GetSafeShare does the same as GetUncappedShare , but GetSafeShare will guarantee the result will not more than total
func GetSafeShare(part, total, allocation cosmos.Int) cosmos.Int {
func GetSafeShare(part, total, allocation cosmos.Dec) cosmos.Dec {
if part.GTE(total) {
part = total
}
Expand All @@ -29,33 +29,17 @@ func GetSafeShare(part, total, allocation cosmos.Int) cosmos.Int {

// GetUncappedShare this method will panic if any of the input parameter can't be convert to cosmos.Dec
// which shouldn't happen
func GetUncappedShare(part, total, allocation cosmos.Int) (share cosmos.Int) {
func GetUncappedShare(part, total, allocation cosmos.Dec) (share cosmos.Dec) {
if part.IsZero() || total.IsZero() {
return cosmos.ZeroInt()
return cosmos.ZeroDec()
}
defer func() {
if err := recover(); err != nil {
share = cosmos.ZeroInt()
share = cosmos.ZeroDec()
}
}()
// use string to convert cosmos.Int to cosmos.Dec is the only way I can find out without being constrain to uint64
// cosmos.Int can hold values way larger than uint64 , because it is using big.Int internally
aD, err := cosmos.NewDecFromStr(allocation.String())
if err != nil {
panic(fmt.Errorf("fail to convert %s to cosmos.Dec: %w", allocation.String(), err))
}

pD, err := cosmos.NewDecFromStr(part.String())
if err != nil {
panic(fmt.Errorf("fail to convert %s to cosmos.Dec: %w", part.String(), err))
}
tD, err := cosmos.NewDecFromStr(total.String())
if err != nil {
panic(fmt.Errorf("fail to convert%s to cosmos.Dec: %w", total.String(), err))
}
// A / (Total / part) == A * (part/Total) but safer when part < Totals
result := aD.Quo(tD.Quo(pD))
share = cosmos.NewIntFromBigInt(result.RoundInt().BigInt())
share = allocation.Quo(total.Quo(part))
return
}

Expand Down
10 changes: 6 additions & 4 deletions common/common_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package common

import (
"fmt"
"testing"

"github.com/stretchr/testify/require"
Expand All @@ -12,14 +13,15 @@ func TestGetUncappedShare(t *testing.T) {
part := cosmos.NewInt(149506590)
total := cosmos.NewInt(50165561086)
alloc := cosmos.NewInt(50000000)
share := GetUncappedShare(part, total, alloc)
require.True(t, share.Equal(cosmos.NewInt(149013)))
share := GetUncappedShare(part.ToLegacyDec(), total.ToLegacyDec(), alloc.ToLegacyDec())
fmt.Println(share)
require.True(t, share.RoundInt().Equal(cosmos.NewInt(149013)))
}

func TestGetSafeShare(t *testing.T) {
part := cosmos.NewInt(14950659000000000)
total := cosmos.NewInt(50165561086)
alloc := cosmos.NewInt(50000000)
share := GetSafeShare(part, total, alloc)
require.True(t, share.Equal(cosmos.NewInt(50000000)))
share := GetSafeShare(part.ToLegacyDec(), total.ToLegacyDec(), alloc.ToLegacyDec())
require.True(t, share.Equal(cosmos.NewDec(50000000)))
}
39 changes: 16 additions & 23 deletions scripts/genesis.sh
Original file line number Diff line number Diff line change
Expand Up @@ -79,28 +79,21 @@ if [ ! -f ~/.arkeo/config/genesis.json ]; then

arkeod keys add faucet --keyring-backend test
FAUCET=$(arkeod keys show faucet -a --keyring-backend test)
add_account "$FAUCET" $TOKEN 10000000000000000 # faucet, 10m
add_account "$FAUCET" $TOKEN 2900000000000000 # faucet, 29m

if [ "$NET" = "mocknet" ] || [ "$NET" = "testnet" ]; then
add_module tarkeo14tmx70mvve3u7hfmd45vle49kvylk6s2wllxny $TOKEN 30250000000000 'claimarkeo'
add_module tarkeo1d0m97ywk2y4vq58ud6q5e0r3q9khj9e3unfe4t $TOKEN 2420000000000000 'arkeo-reserve'
add_module tarkeo14tmx70mvve3u7hfmd45vle49kvylk6s2wllxny $TOKEN 3025000000000000 'claimarkeo'
add_module tarkeo1jv65s3grqf6v6jl3dp4t6c9t9rk99cd8t6gr9e $TOKEN 4840000000000000 'distribution'

echo "shoulder heavy loyal save patient deposit crew bag pull club escape eyebrow hip verify border into wire start pact faint fame festival solve shop" | arkeod keys add alice --keyring-backend test --recover
ALICE=$(arkeod keys show alice -a --keyring-backend test)
add_account "$ALICE" $TOKEN 1100000000000000 # alice, 1.1m


echo "clog swear steak glide artwork glory solution short company borrow aerobic idle corn climb believe wink forum destroy miracle oak cover solid valve make" | arkeod keys add bob --keyring-backend test --recover
BOB=$(arkeod keys show bob -a --keyring-backend test)
add_account "$BOB" $TOKEN 1000000000000000 # bob, 1m
add_claim_records "ARKEO" "$BOB" 1000 1000 1000 true


# Add Foundational Accounts
# FoundationCommunityAccount = "tarkeo124qmjmg55v6q5c5vy0vcpefrywxnxhkm7426pc"
add_account "tarkeo124qmjmg55v6q5c5vy0vcpefrywxnxhkm7426pc" $TOKEN 1048400000000000000
# Add Foundational Accounts
# FoundationDevAccount = "tarkeo1x978nttd8vgcgnv9wxut4dh7809lr0n2fhuh0q"
add_account "tarkeo1x978nttd8vgcgnv9wxut4dh7809lr0n2fhuh0q" $TOKEN 12100000000000
add_account "tarkeo1x978nttd8vgcgnv9wxut4dh7809lr0n2fhuh0q" $TOKEN 1210000000000000
# FoundationGrantsAccount = "tarkeo1a307z4a82mcyv9njdj9ajnd9xpp90kmeqwntxj"
add_account "tarkeo1a307z4a82mcyv9njdj9ajnd9xpp90kmeqwntxj" $TOKEN 6050000000000
add_account "tarkeo1a307z4a82mcyv9njdj9ajnd9xpp90kmeqwntxj" $TOKEN 605000000000000



# Thorchain derived test addresses
Expand All @@ -116,15 +109,15 @@ if [ ! -f ~/.arkeo/config/genesis.json ]; then
add_claim_records "ETHEREUM" "0x92E14917A0508Eb56C90C90619f5F9Adbf49f47d" 500000 600000 700000 true

# enable CORs on testnet/localnet
sed -i 's/enabled-unsafe-cors = false/enabled-unsafe-cors = true/g' ~/.arkeo/config/app.toml
sed -i 's/cors_allowed_origins = \[\]/cors_allowed_origins = \["*"\]/g' ~/.arkeo/config/config.toml
sed -i '' 's/enabled-unsafe-cors = false/enabled-unsafe-cors = true/g' ~/.arkeo/config/app.toml
sed -i '' 's/cors_allowed_origins = \[\]/cors_allowed_origins = \["*"\]/g' ~/.arkeo/config/config.toml
fi

sed -i 's/"stake"/"uarkeo"/g' ~/.arkeo/config/genesis.json
sed -i '/"duration_until_decay"\|"duration_of_decay"/s/"3600s"/"7884000s"/' ~/.arkeo/config/genesis.json
sed -i 's/enable = false/enable = true/g' ~/.arkeo/config/app.toml
sed -i 's/127.0.0.1:26657/0.0.0.0:26657/g' ~/.arkeo/config/config.toml
sed -i 's/address = "tcp:\/\/localhost:1317"/address = "tcp:\/\/0.0.0.0:1317"/g' ~/.arkeo/config/app.toml
sed -i '' 's/"stake"/"uarkeo"/g' ~/.arkeo/config/genesis.json
sed -i '' '/"duration_until_decay"\|"duration_of_decay"/s/"3600s"/"7884000s"/' ~/.arkeo/config/genesis.json
sed -i '' 's/enable = false/enable = true/g' ~/.arkeo/config/app.toml
sed -i '' 's/127.0.0.1:26657/0.0.0.0:26657/g' ~/.arkeo/config/config.toml
sed -i '' 's/address = "tcp:\/\/localhost:1317"/address = "tcp:\/\/0.0.0.0:1317"/g' ~/.arkeo/config/app.toml

# Update the supply field in genesis.json using jq
jq --arg DENOM "$TOKEN" --arg AMOUNT "$TOTAL_SUPPLY" '.app_state.bank.supply = [{"denom": $DENOM, "amount": $AMOUNT}]' <~/.arkeo/config/genesis.json >/tmp/genesis.json
Expand Down
9 changes: 8 additions & 1 deletion x/arkeo/keeper/keeper.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import (
bankkeeper "github.com/cosmos/cosmos-sdk/x/bank/keeper"
banktypes "github.com/cosmos/cosmos-sdk/x/bank/types"
distkeeper "github.com/cosmos/cosmos-sdk/x/distribution/keeper"
disttypes "github.com/cosmos/cosmos-sdk/x/distribution/types"
minttypes "github.com/cosmos/cosmos-sdk/x/mint/keeper"
paramtypes "github.com/cosmos/cosmos-sdk/x/params/types"
stakingkeeper "github.com/cosmos/cosmos-sdk/x/staking/keeper"
Expand Down Expand Up @@ -70,7 +71,7 @@ type Keeper interface {
GetInflationRate(ctx cosmos.Context) (math.LegacyDec, error)
MoveTokensFromDistributionToFoundationPoolAccount(ctx cosmos.Context) error
AllocateTokensToValidator(ctx context.Context, val stakingtypes.ValidatorI, tokens sdk.DecCoins) error
BurnCoins(ctx context.Context, moduleName string, coins sdk.Coins) error
SendToCommunityPool(ctx context.Context, amount sdk.Coins, sender sdk.AccAddress) error

// Query
Params(c context.Context, req *types.QueryParamsRequest) (*types.QueryParamsResponse, error)
Expand Down Expand Up @@ -411,6 +412,8 @@ func (k KVStore) GetCirculatingSupply(ctx cosmos.Context, denom string) (sdk.Dec
return sdk.NewDecCoin(denom, sdkmath.NewInt(0)), fmt.Errorf("failed to fetch foundational account %s", err)
}

k.Logger().Info(fmt.Sprintf("Community address :%s", k.GetModuleAccAddress(disttypes.ModuleName)))

// Account Address for which the circulating supply should be exempted
addressToExempt := []sdk.AccAddress{
devAccountAddress,
Expand Down Expand Up @@ -567,3 +570,7 @@ func (k KVStore) AllocateTokensToValidator(ctx context.Context, val stakingtypes
func (k KVStore) BurnCoins(ctx context.Context, moduleName string, coins sdk.Coins) error {
return k.coinKeeper.BurnCoins(ctx, moduleName, coins)
}

func (k KVStore) SendToCommunityPool(ctx context.Context, amount sdk.Coins, sender sdk.AccAddress) error {
return k.distributionKeeper.FundCommunityPool(ctx, amount, sender)
}
92 changes: 38 additions & 54 deletions x/arkeo/keeper/manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,33 +48,19 @@ func (mgr *Manager) BeginBlock(ctx cosmos.Context) error {
}
mgr.keeper.SetVersion(ctx, ver) // update stored version

// Get the circulating supply after calculating inflation
circSupply, err := mgr.circulatingSupplyAfterInflationCalc(ctx)
// Get the reserve supply
reserveSupply, err := mgr.reserveSupply(ctx)
if err != nil {
mgr.keeper.Logger().Error("unable to get supply with inflation calculation", "error", err)
return err
}

err = mgr.keeper.MoveTokensFromDistributionToFoundationPoolAccount(ctx)
if err != nil {
mgr.keeper.Logger().Error("unable to send tokens from distribution to pool account", "error", err)
}

validatorPayoutCycle := sdkmath.LegacyNewDec(mgr.FetchConfig(ctx, configs.ValidatorPayoutCycle))

emissionCurve := sdkmath.LegacyNewDec(int64(params.EmissionCurve)) // Emission curve factor
blocksPerYear := sdkmath.LegacyNewDec(int64(params.BlockPerYear))

// Distribute Minted To Pools
balanceDistribution, err := mgr.keeper.MintAndDistributeTokens(ctx, circSupply)
if err != nil {
mgr.keeper.Logger().Error("unable to mint and distribute tokens", "error", err)
}

mgr.keeper.Logger().Info(fmt.Sprintf("Circulating Supply After Funding Foundational Account %s", balanceDistribution))

// Calculate Block Rewards
blockReward := mgr.calcBlockReward(ctx, balanceDistribution.Amount, emissionCurve, blocksPerYear, validatorPayoutCycle)
blockReward := mgr.calcBlockReward(ctx, reserveSupply.Amount, emissionCurve, blocksPerYear, validatorPayoutCycle)
mgr.keeper.Logger().Info(fmt.Sprintf("Block Reward for block number %d, %v", ctx.BlockHeight(), blockReward))

var votes = []abci.VoteInfo{}
Expand Down Expand Up @@ -108,9 +94,9 @@ func (mgr Manager) EndBlock(ctx cosmos.Context) error {
if err := mgr.invariantContractModule(ctx); err != nil {
panic(err)
}
// if err := mgr.invariantMaxSupply(ctx); err != nil {
// panic(err)
// }
if err := mgr.invariantMaxSupply(ctx); err != nil {
panic(err)
}
return nil
}

Expand Down Expand Up @@ -230,7 +216,7 @@ func (mgr Manager) ValidatorPayout(ctx cosmos.Context, votes []abci.VoteInfo, bl
}

// sum tokens
total := cosmos.ZeroInt()
total := cosmos.ZeroDec()
for _, vote := range votes {
val, err := mgr.sk.ValidatorByConsAddr(ctx, vote.Validator.Address)
if err != nil {
Expand All @@ -240,12 +226,14 @@ func (mgr Manager) ValidatorPayout(ctx cosmos.Context, votes []abci.VoteInfo, bl
if !val.IsBonded() || val.IsJailed() {
continue
}
total = total.Add(val.GetDelegatorShares().RoundInt())
total = total.Add(val.GetDelegatorShares())
}
if total.IsZero() {
return nil
}

var totalRemainder cosmos.Dec = cosmos.ZeroDec()

for _, vote := range votes {
if vote.BlockIdFlag.String() == "BLOCK_ID_FLAG_ABSENT" || vote.BlockIdFlag.String() == "BLOCK_ID_FLAG_UNKNOWN" {
mgr.keeper.Logger().Info(fmt.Sprintf("validator rewards skipped due to lack of signature: %s, validator : %s ", vote.BlockIdFlag.String(), string(vote.Validator.GetAddress())))
Expand Down Expand Up @@ -274,9 +262,9 @@ func (mgr Manager) ValidatorPayout(ctx cosmos.Context, votes []abci.VoteInfo, bl
}
acc := cosmos.AccAddress(val.GetOperator())

totalReward := common.GetSafeShare(val.GetDelegatorShares().RoundInt(), total, blockReward.Amount.RoundInt())
validatorReward := cosmos.ZeroInt()
rateBasisPts := val.GetCommission().MulInt64(100).RoundInt()
totalReward := common.GetSafeShare(val.GetDelegatorShares(), total, blockReward.Amount)
validatorReward := cosmos.ZeroDec()
rateBasisPts := val.GetCommission().MulInt64(100)

delegates, err := mgr.sk.GetValidatorDelegations(ctx, valBz)
if err != nil {
Expand All @@ -289,32 +277,43 @@ func (mgr Manager) ValidatorPayout(ctx cosmos.Context, votes []abci.VoteInfo, bl
mgr.keeper.Logger().Error("unable to fetch delegate address", "delegate", delegate.DelegatorAddress, "error", err)
continue
}
delegateReward := common.GetSafeShare(delegate.GetShares().RoundInt(), val.GetDelegatorShares().RoundInt(), totalReward)
delegateReward := common.GetSafeShare(delegate.GetShares(), val.GetDelegatorShares(), totalReward)
if acc.String() != delegate.DelegatorAddress {
valFee := common.GetSafeShare(rateBasisPts, cosmos.NewInt(configs.MaxBasisPoints), delegateReward)
valFee := common.GetSafeShare(rateBasisPts, cosmos.NewDec(configs.MaxBasisPoints), delegateReward)
delegateReward = delegateReward.Sub(valFee)
validatorReward = validatorReward.Add(valFee)
}

if err := mgr.keeper.MintAndSendToAccount(ctx, delegateAcc, cosmos.NewCoin(blockReward.Denom, delegateReward)); err != nil {
intReward, remainder := delegateReward.TruncateInt(), delegateReward.Sub(cosmos.NewDec(delegateReward.TruncateInt().Int64()))
totalRemainder = totalRemainder.Add(remainder)

if err := mgr.keeper.SendFromModuleToAccount(ctx, types.ReserveName, delegateAcc, cosmos.NewCoins(cosmos.NewCoin(blockReward.Denom, intReward))); err != nil {
mgr.keeper.Logger().Error("unable to pay rewards to delegate", "delegate", delegate.DelegatorAddress, "error", err)
continue
}
mgr.keeper.Logger().Info("delegate rewarded", "delegate", delegateAcc.String(), "amount", delegateReward)
}

if !validatorReward.IsZero() {
if err := mgr.keeper.AllocateTokensToValidator(ctx, val, sdk.NewDecCoins(sdk.NewDecCoin(blockReward.Denom, validatorReward))); err != nil {
intValidatorReward, remainder := validatorReward.TruncateInt(), validatorReward.Sub(cosmos.NewDec(validatorReward.TruncateInt().Int64()))
totalRemainder = totalRemainder.Add(remainder)
if err := mgr.keeper.SendFromModuleToAccount(ctx, types.ReserveName, acc, sdk.NewCoins(sdk.NewCoin(blockReward.Denom, intValidatorReward))); err != nil {
mgr.keeper.Logger().Error("unable to pay rewards to validator", "validator", val.GetOperator(), "error", err)
continue
}
mgr.keeper.Logger().Info("validator additional rewards", "validator", acc.String(), "amount", validatorReward)
mgr.keeper.Logger().Info("validator rewards", "validator", acc.String(), "amount", validatorReward)
}

if err := mgr.EmitValidatorPayoutEvent(ctx, acc, validatorReward); err != nil {
if err := mgr.EmitValidatorPayoutEvent(ctx, acc, validatorReward.TruncateInt()); err != nil {
mgr.keeper.Logger().Error("unable to emit validator payout event", "validator", acc.String(), "error", err)
}
}
// Send remainder to the community pool
if !totalRemainder.IsZero() {
if err := mgr.keeper.SendToCommunityPool(ctx, cosmos.NewCoins(cosmos.NewCoin(blockReward.Denom, totalRemainder.RoundInt())), mgr.keeper.GetModuleAccAddress(types.ReserveName)); err != nil {
mgr.keeper.Logger().Error("unable to send remainder to community pool", "error", err)
}
}

return nil
}
Expand Down Expand Up @@ -351,8 +350,8 @@ func (mgr Manager) SettleContract(ctx cosmos.Context, contract types.Contract, n
contract.Nonce = nonce
}
totalDebt, err := mgr.contractDebt(ctx, contract)
valIncome := common.GetSafeShare(cosmos.NewInt(mgr.FetchConfig(ctx, configs.ReserveTax)), cosmos.NewInt(configs.MaxBasisPoints), totalDebt)
debt := totalDebt.Sub(valIncome)
valIncome := common.GetSafeShare(cosmos.NewDec(mgr.FetchConfig(ctx, configs.ReserveTax)), cosmos.NewDec(configs.MaxBasisPoints), totalDebt.ToLegacyDec())
debt := totalDebt.Sub(valIncome.RoundInt())
if err != nil {
return contract, err
}
Expand All @@ -364,7 +363,7 @@ func (mgr Manager) SettleContract(ctx cosmos.Context, contract types.Contract, n
if err := mgr.keeper.SendFromModuleToAccount(ctx, types.ContractName, provider, cosmos.NewCoins(cosmos.NewCoin(contract.Rate.Denom, debt))); err != nil {
return contract, err
}
if err := mgr.keeper.SendFromModuleToModule(ctx, types.ContractName, types.ModuleName, cosmos.NewCoins(cosmos.NewCoin(contract.Rate.Denom, valIncome))); err != nil {
if err := mgr.keeper.SendFromModuleToModule(ctx, types.ContractName, types.ModuleName, cosmos.NewCoins(cosmos.NewCoin(contract.Rate.Denom, valIncome.RoundInt()))); err != nil {
return contract, err
}
}
Expand Down Expand Up @@ -399,7 +398,7 @@ func (mgr Manager) SettleContract(ctx cosmos.Context, contract types.Contract, n
return contract, err
}

if err = mgr.EmitContractSettlementEvent(ctx, totalDebt, valIncome, &contract); err != nil {
if err = mgr.EmitContractSettlementEvent(ctx, totalDebt, valIncome.RoundInt(), &contract); err != nil {
return contract, err
}

Expand Down Expand Up @@ -433,25 +432,10 @@ func (mgr Manager) contractDebt(ctx cosmos.Context, contract types.Contract) (co
return debt, nil
}

func (mgr Manager) circulatingSupplyAfterInflationCalc(ctx cosmos.Context) (sdk.DecCoin, error) {
// Get the circulating supply
circulatingSupply, err := mgr.keeper.GetCirculatingSupply(ctx, configs.Denom)
if err != nil {
mgr.keeper.Logger().Error(fmt.Sprintf("failed to get circulating supply %s", err))
return sdk.NewDecCoin(configs.Denom, sdkmath.NewInt(0)), err
}

// Get the inflation rate
inflationRate, err := mgr.keeper.GetInflationRate(ctx)
if err != nil {
return sdk.NewDecCoin(configs.Denom, sdkmath.NewInt(0)), err
}
mgr.keeper.Logger().Info(fmt.Sprintf("inflation rate: %d", inflationRate))

// Multiply circulating supply by inflation rate to get the newly minted token amount
newTokenAmountMintedDec := circulatingSupply.Amount.Mul(inflationRate).QuoInt64(100)
func (mgr Manager) reserveSupply(ctx cosmos.Context) (sdk.DecCoin, error) {
reserveSupply := mgr.keeper.GetBalanceOfModule(ctx, types.ReserveName, configs.Denom)

mgr.keeper.Logger().Info(fmt.Sprintf("After Inflation Calculation: %v", newTokenAmountMintedDec))
mgr.keeper.Logger().Info(fmt.Sprintf("Reserve Supply: %v", reserveSupply))

return sdk.NewDecCoin(configs.Denom, newTokenAmountMintedDec.RoundInt()), nil
return sdk.NewDecCoin(configs.Denom, reserveSupply), nil
}

0 comments on commit bea0b1f

Please sign in to comment.