Skip to content

Commit

Permalink
Merge pull request #201 from crescent-network/fix/liquidstaking-amm-l…
Browse files Browse the repository at this point in the history
…iquidamm

fix: update x/liquidstaking to use new modules
  • Loading branch information
kingcre authored Sep 11, 2023
2 parents c9e805d + 4aac130 commit e8c451c
Show file tree
Hide file tree
Showing 9 changed files with 282 additions and 135 deletions.
25 changes: 13 additions & 12 deletions app/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -532,18 +532,6 @@ func NewApp(
app.BankKeeper,
app.LiquidityKeeper,
)
app.LiquidStakingKeeper = liquidstakingkeeper.NewKeeper(
appCodec,
keys[liquidstakingtypes.StoreKey],
app.GetSubspace(liquidstakingtypes.ModuleName),
app.AccountKeeper,
app.BankKeeper,
app.StakingKeeper,
app.DistrKeeper,
app.LiquidityKeeper,
app.LPFarmKeeper,
app.SlashingKeeper,
)
app.ExchangeKeeper = exchangekeeper.NewKeeper(
appCodec,
keys[exchangetypes.StoreKey],
Expand Down Expand Up @@ -584,6 +572,19 @@ func NewApp(
app.BankKeeper,
app.AMMKeeper,
)
app.LiquidStakingKeeper = liquidstakingkeeper.NewKeeper(
appCodec,
keys[liquidstakingtypes.StoreKey],
app.GetSubspace(liquidstakingtypes.ModuleName),
app.AccountKeeper,
app.BankKeeper,
app.StakingKeeper,
app.DistrKeeper,
app.AMMKeeper,
app.LiquidAMMKeeper,
app.LPFarmKeeper,
app.SlashingKeeper,
)

// register the proposal types
govRouter := govtypes.NewRouter()
Expand Down
3 changes: 3 additions & 0 deletions types/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,9 @@ func DecApproxEqual(a, b sdk.Dec) bool {
if b.GT(a) {
a, b = b, a
}
if a.IsZero() && b.IsZero() {
return true
}
return a.Sub(b).Quo(a).LTE(sdk.NewDecWithPrec(1, 3))
}

Expand Down
8 changes: 5 additions & 3 deletions x/liquidstaking/keeper/keeper.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@ type Keeper struct {
bankKeeper types.BankKeeper
stakingKeeper types.StakingKeeper
distrKeeper types.DistrKeeper
liquidityKeeper types.LiquidityKeeper
ammKeeper types.AMMKeeper
liquidAMMKeeper types.LiquidAMMKeeper
lpfarmKeeper types.LPFarmKeeper
slashingKeeper types.SlashingKeeper
}
Expand All @@ -32,7 +33,7 @@ type Keeper struct {
// - minting, burning PoolCoins
func NewKeeper(cdc codec.BinaryCodec, key sdk.StoreKey, paramSpace paramtypes.Subspace,
accountKeeper types.AccountKeeper, bankKeeper types.BankKeeper, stakingKeeper types.StakingKeeper,
distrKeeper types.DistrKeeper, liquidityKeeper types.LiquidityKeeper,
distrKeeper types.DistrKeeper, ammKeeper types.AMMKeeper, liquidAMMKeeper types.LiquidAMMKeeper,
lpfarmKeeper types.LPFarmKeeper, slashingKeeper types.SlashingKeeper,
) Keeper {
// ensure liquidstaking module account is set
Expand All @@ -53,7 +54,8 @@ func NewKeeper(cdc codec.BinaryCodec, key sdk.StoreKey, paramSpace paramtypes.Su
bankKeeper: bankKeeper,
stakingKeeper: stakingKeeper,
distrKeeper: distrKeeper,
liquidityKeeper: liquidityKeeper,
ammKeeper: ammKeeper,
liquidAMMKeeper: liquidAMMKeeper,
lpfarmKeeper: lpfarmKeeper,
slashingKeeper: slashingKeeper,
}
Expand Down
66 changes: 43 additions & 23 deletions x/liquidstaking/keeper/keeper_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,15 @@ import (

chain "github.com/crescent-network/crescent/v5/app"
utils "github.com/crescent-network/crescent/v5/types"
liquiditytypes "github.com/crescent-network/crescent/v5/x/liquidity/types"
ammtypes "github.com/crescent-network/crescent/v5/x/amm/types"
exchangetypes "github.com/crescent-network/crescent/v5/x/exchange/types"
liquidammtypes "github.com/crescent-network/crescent/v5/x/liquidamm/types"
"github.com/crescent-network/crescent/v5/x/liquidstaking"
"github.com/crescent-network/crescent/v5/x/liquidstaking/keeper"
"github.com/crescent-network/crescent/v5/x/liquidstaking/types"
lpfarmtypes "github.com/crescent-network/crescent/v5/x/lpfarm/types"
"github.com/crescent-network/crescent/v5/x/mint"
minttypes "github.com/crescent-network/crescent/v5/x/mint/types"
)

var (
Expand Down Expand Up @@ -335,32 +338,49 @@ func (s *KeeperTestSuite) createContinuousVestingAccount(from sdk.AccAddress, to
}

func (s *KeeperTestSuite) fundAddr(addr sdk.AccAddress, amt sdk.Coins) {
err := s.app.BankKeeper.MintCoins(s.ctx, liquiditytypes.ModuleName, amt)
err := s.app.BankKeeper.MintCoins(s.ctx, minttypes.ModuleName, amt)
s.Require().NoError(err)
err = s.app.BankKeeper.SendCoinsFromModuleToAccount(s.ctx, liquiditytypes.ModuleName, addr, amt)
err = s.app.BankKeeper.SendCoinsFromModuleToAccount(s.ctx, minttypes.ModuleName, addr, amt)
s.Require().NoError(err)
}

// liquidity module keeper utils for liquid staking combine test
func (s *KeeperTestSuite) createMarketAndPool(baseDenom, quoteDenom string, price sdk.Dec) (market exchangetypes.Market, pool ammtypes.Pool) {
s.T().Helper()
creatorAddr := utils.TestAddress(100000)
s.fundAddr(creatorAddr, s.app.ExchangeKeeper.GetMarketCreationFee(s.ctx))
var err error
market, err = s.app.ExchangeKeeper.CreateMarket(s.ctx, creatorAddr, baseDenom, quoteDenom)
s.Require().NoError(err)
s.fundAddr(creatorAddr, s.app.AMMKeeper.GetPoolCreationFee(s.ctx))
pool, err = s.app.AMMKeeper.CreatePool(s.ctx, creatorAddr, market.Id, price)
s.Require().NoError(err)
return
}

func (s *KeeperTestSuite) createPair(creator sdk.AccAddress, baseCoinDenom, quoteCoinDenom string, fund bool) liquiditytypes.Pair {
params := s.app.LiquidityKeeper.GetParams(s.ctx)
if fund {
s.fundAddr(creator, params.PairCreationFee)
}
pair, err := s.app.LiquidityKeeper.CreatePair(s.ctx, liquiditytypes.NewMsgCreatePair(creator, baseCoinDenom, quoteCoinDenom))
func (s *KeeperTestSuite) addLiquidity(
ownerAddr sdk.AccAddress, poolId uint64, lowerPrice, upperPrice sdk.Dec, desiredAmt sdk.Coins) (position ammtypes.Position, liquidity sdk.Int, amt sdk.Coins) {
s.T().Helper()
var err error
position, liquidity, amt, err = s.app.AMMKeeper.AddLiquidity(s.ctx, ownerAddr, ownerAddr, poolId, lowerPrice, upperPrice, desiredAmt)
s.Require().NoError(err)
return pair
return
}

func (s *KeeperTestSuite) createPool(creator sdk.AccAddress, pairId uint64, depositCoins sdk.Coins, fund bool) liquiditytypes.Pool {
params := s.app.LiquidityKeeper.GetParams(s.ctx)
if fund {
s.fundAddr(creator, depositCoins.Add(params.PoolCreationFee...))
}
pool, err := s.app.LiquidityKeeper.CreatePool(s.ctx, liquiditytypes.NewMsgCreatePool(creator, pairId, depositCoins))
func (s *KeeperTestSuite) createPublicPosition(
poolId uint64, lowerPrice, upperPrice sdk.Dec, minBidAmt sdk.Int, feeRate sdk.Dec) liquidammtypes.PublicPosition {
s.T().Helper()
publicPosition, err := s.app.LiquidAMMKeeper.CreatePublicPosition(s.ctx, poolId, lowerPrice, upperPrice, minBidAmt, feeRate)
s.Require().NoError(err)
return publicPosition
}

func (s *KeeperTestSuite) mintShare(
senderAddr sdk.AccAddress, publicPositionId uint64, desiredAmt sdk.Coins) (mintedShare sdk.Coin, position ammtypes.Position, liquidity sdk.Int, amt sdk.Coins) {
s.T().Helper()
var err error
mintedShare, position, liquidity, amt, err = s.app.LiquidAMMKeeper.MintShare(s.ctx, senderAddr, publicPositionId, desiredAmt)
s.Require().NoError(err)
return pool
return
}

// farming module keeper utils for liquid staking combine test
Expand All @@ -383,13 +403,13 @@ func (s *KeeperTestSuite) farm(farmerAddr sdk.AccAddress, coin sdk.Coin) {
s.Require().NoError(err)
}

func (s *KeeperTestSuite) assertTallyResult(yes, no, vito, abstain int64, proposal govtypes.Proposal) {
func (s *KeeperTestSuite) assertTallyResult(yes, no, veto, abstain int64, proposal govtypes.Proposal) {
cachedCtx, _ := s.ctx.CacheContext()
_, _, result := s.app.GovKeeper.Tally(cachedCtx, proposal)
s.Require().Equal(sdk.NewInt(yes), result.Yes)
s.Require().Equal(sdk.NewInt(no), result.No)
s.Require().Equal(sdk.NewInt(vito), result.NoWithVeto)
s.Require().Equal(sdk.NewInt(abstain), result.Abstain)
s.Require().True(utils.DecApproxEqual(sdk.NewDec(yes), result.Yes.ToDec()))
s.Require().True(utils.DecApproxEqual(sdk.NewDec(no), result.No.ToDec()))
s.Require().True(utils.DecApproxEqual(sdk.NewDec(veto), result.NoWithVeto.ToDec()))
s.Require().True(utils.DecApproxEqual(sdk.NewDec(abstain), result.Abstain.ToDec()))
}

func (s *KeeperTestSuite) assertVotingPower(addr sdk.AccAddress, stakingVotingPower, liquidStakingVotingPower, validatorVotingPower sdk.Int) {
Expand Down
139 changes: 86 additions & 53 deletions x/liquidstaking/keeper/tally.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@ import (
stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types"

utils "github.com/crescent-network/crescent/v5/types"
liquiditytypes "github.com/crescent-network/crescent/v5/x/liquidity/types"
ammtypes "github.com/crescent-network/crescent/v5/x/amm/types"
liquidammtypes "github.com/crescent-network/crescent/v5/x/liquidamm/types"
"github.com/crescent-network/crescent/v5/x/liquidstaking/types"
lpfarmtypes "github.com/crescent-network/crescent/v5/x/lpfarm/types"
)
Expand All @@ -34,17 +35,74 @@ func (k Keeper) GetVoterBalanceByDenom(ctx sdk.Context, votes govtypes.Votes) ma
return denomAddrBalanceMap
}

// GetBTokenSharePerPoolCoinMap creates bTokenSharePerPoolCoinMap of pool coins which is containing target denom to calculate target denom value of farming positions
func (k Keeper) GetBTokenSharePerPoolCoinMap(ctx sdk.Context, targetDenom string) map[string]sdk.Dec {
bTokenSharePerPoolCoinMap := map[string]sdk.Dec{}
_ = k.liquidityKeeper.IterateAllPools(ctx, func(pool liquiditytypes.Pool) (stop bool, err error) {
bTokenSharePerPoolCoin := k.TokenSharePerPoolCoin(ctx, targetDenom, pool.PoolCoinDenom)
if bTokenSharePerPoolCoin.IsPositive() {
bTokenSharePerPoolCoinMap[pool.PoolCoinDenom] = bTokenSharePerPoolCoin
func (k Keeper) TokenAmountFromAMMPositions(ctx sdk.Context, addr sdk.AccAddress, targetDenom string) sdk.Int {
tokenAmount := utils.ZeroInt

k.ammKeeper.IteratePositionsByOwner(ctx, addr, func(position ammtypes.Position) (stop bool) {
coin0, coin1, err := k.ammKeeper.PositionAssets(ctx, position.Id)
if err != nil {
k.Logger(ctx).Error("failed to get position assets", "error", err)
return false
}
if coin0.Denom == targetDenom {
tokenAmount = tokenAmount.Add(coin0.Amount)
} else if coin1.Denom == targetDenom {
tokenAmount = tokenAmount.Add(coin1.Amount)
}
return false
})

return tokenAmount
}

// GetBTokenSharePerPublicPositionShareMap creates a map of public position
// share which contains targetDenom to calculate target denom value of public positions
func (k Keeper) GetBTokenSharePerPublicPositionShareMap(ctx sdk.Context, targetDenom string) map[string]sdk.Dec {
m := map[string]sdk.Dec{}
k.liquidAMMKeeper.IterateAllPublicPositions(ctx, func(publicPosition liquidammtypes.PublicPosition) (stop bool) {
shareDenom := liquidammtypes.ShareDenom(publicPosition.Id)
bTokenSharePerPublicPositionShare := k.BTokenSharePerPublicPositionShare(ctx, publicPosition, targetDenom)
if bTokenSharePerPublicPositionShare.IsPositive() {
m[shareDenom] = bTokenSharePerPublicPositionShare
}
return false, nil
return false
})
return bTokenSharePerPoolCoinMap
return m
}

func (k Keeper) BTokenSharePerPublicPositionShare(ctx sdk.Context, publicPosition liquidammtypes.PublicPosition, targetDenom string) sdk.Dec {
shareDenom := liquidammtypes.ShareDenom(publicPosition.Id)

ammPosition, found := k.liquidAMMKeeper.GetAMMPosition(ctx, publicPosition)
if !found {
// The public position is not initialized(gained liquidity) yet.
return utils.ZeroDec
}
coin0, coin1, err := k.ammKeeper.PositionAssets(ctx, ammPosition.Id)
if err != nil {
// TODO: panic instead?
return utils.ZeroDec
}
if coin0.Denom != targetDenom && coin1.Denom != targetDenom {
// The pool doesn't have target denom in its assets.
return utils.ZeroDec
}

publicPositionShareSupply := k.bankKeeper.GetSupply(ctx, shareDenom).Amount
if !publicPositionShareSupply.IsPositive() {
// No liquidity in the public position.
return utils.ZeroDec
}

bTokenSharePerPublicPositionShare := utils.ZeroDec
if coin0.Denom == targetDenom {
bTokenSharePerPublicPositionShare = coin0.Amount.ToDec().
QuoTruncate(publicPositionShareSupply.ToDec())
} else if coin1.Denom == targetDenom {
bTokenSharePerPublicPositionShare = coin1.Amount.ToDec().
QuoTruncate(publicPositionShareSupply.ToDec())
}
return bTokenSharePerPublicPositionShare
}

// TokenAmountFromFarmingPositions returns worth of staked tokens amount of exist farming positions including queued of the addr
Expand All @@ -64,40 +122,6 @@ func (k Keeper) TokenAmountFromFarmingPositions(ctx sdk.Context, addr sdk.AccAdd
return tokenAmount
}

// TokenSharePerPoolCoin returns token share of the target denom of a pool coin
func (k Keeper) TokenSharePerPoolCoin(ctx sdk.Context, targetDenom, poolCoinDenom string) sdk.Dec {
poolId, err := liquiditytypes.ParsePoolCoinDenom(poolCoinDenom)
if err != nil {
// If poolCoinDenom is not a valid pool coin denom, just return zero.
return sdk.ZeroDec()
}
pool, found := k.liquidityKeeper.GetPool(ctx, poolId)
if !found {
return sdk.ZeroDec()
}

pair, found := k.liquidityKeeper.GetPair(ctx, pool.PairId)
if !found {
return sdk.ZeroDec()
}

rx, ry := k.liquidityKeeper.GetPoolBalances(ctx, pool)
poolCoinSupply := k.liquidityKeeper.GetPoolCoinSupply(ctx, pool)
if !poolCoinSupply.IsPositive() {
return sdk.ZeroDec()
}
bTokenSharePerPoolCoin := sdk.ZeroDec()
if pair.QuoteCoinDenom == targetDenom {
bTokenSharePerPoolCoin = rx.Amount.ToDec().QuoTruncate(poolCoinSupply.ToDec())
} else if pair.BaseCoinDenom == targetDenom {
bTokenSharePerPoolCoin = ry.Amount.ToDec().QuoTruncate(poolCoinSupply.ToDec())
}
if !bTokenSharePerPoolCoin.IsPositive() {
return sdk.ZeroDec()
}
return bTokenSharePerPoolCoin
}

func (k Keeper) GetVotingPower(ctx sdk.Context, addr sdk.AccAddress) types.VotingPower {
val, found := k.stakingKeeper.GetValidator(ctx, addr.Bytes())
validatorVotingPower := sdk.ZeroInt()
Expand Down Expand Up @@ -157,7 +181,7 @@ func (k Keeper) CalcLiquidStakingVotingPower(ctx sdk.Context, addr sdk.AccAddres
}

bTokenAmount := sdk.ZeroInt()
bTokenSharePerPoolCoinMap := k.GetBTokenSharePerPoolCoinMap(ctx, liquidBondDenom)
bTokenSharePerPublicPositionShareMap := k.GetBTokenSharePerPublicPositionShareMap(ctx, liquidBondDenom)

balances := k.bankKeeper.SpendableCoins(ctx, addr)
for _, coin := range balances {
Expand All @@ -167,12 +191,17 @@ func (k Keeper) CalcLiquidStakingVotingPower(ctx sdk.Context, addr sdk.AccAddres
}

// check if the denom is pool coin
if bTokenSharePerPoolCoin, ok := bTokenSharePerPoolCoinMap[coin.Denom]; ok {
bTokenAmount = bTokenAmount.Add(utils.GetShareValue(coin.Amount, bTokenSharePerPoolCoin))
if bTokenSharePerPublicPositionShare, ok := bTokenSharePerPublicPositionShareMap[coin.Denom]; ok {
bTokenAmount = bTokenAmount.Add(utils.GetShareValue(coin.Amount, bTokenSharePerPublicPositionShare))
}
}

tokenAmount := k.TokenAmountFromFarmingPositions(ctx, addr, liquidBondDenom, bTokenSharePerPoolCoinMap)
tokenAmount := k.TokenAmountFromAMMPositions(ctx, addr, liquidBondDenom)
if tokenAmount.IsPositive() {
bTokenAmount = bTokenAmount.Add(tokenAmount)
}

tokenAmount = k.TokenAmountFromFarmingPositions(ctx, addr, liquidBondDenom, bTokenSharePerPublicPositionShareMap)
if tokenAmount.IsPositive() {
bTokenAmount = bTokenAmount.Add(tokenAmount)
}
Expand Down Expand Up @@ -206,7 +235,7 @@ func (k Keeper) SetLiquidStakingVotingPowers(ctx sdk.Context, votes govtypes.Vot

// get the map of balance amount of voter by denom
voterBalanceByDenom := k.GetVoterBalanceByDenom(ctx, votes)
bTokenSharePerPoolCoinMap := k.GetBTokenSharePerPoolCoinMap(ctx, liquidBondDenom)
bTokenSharePerPublicPositionShareMap := k.GetBTokenSharePerPublicPositionShareMap(ctx, liquidBondDenom)
bTokenOwnMap := make(utils.StrIntMap)

// sort denom keys of voterBalanceByDenom for deterministic iteration
Expand All @@ -227,10 +256,10 @@ func (k Keeper) SetLiquidStakingVotingPowers(ctx sdk.Context, votes govtypes.Vot
continue
}

// if the denom is pool coin, get bToken share and add owned bToken on bTokenOwnMap
if bTokenSharePerPoolCoin, ok := bTokenSharePerPoolCoinMap[denom]; ok {
// if the denom is public position share, get bToken share and add owned bToken on bTokenOwnMap
if bTokenSharePerPublicPositionShare, ok := bTokenSharePerPublicPositionShareMap[denom]; ok {
for voter, balance := range voterBalanceByDenom[denom] {
bTokenOwnMap.AddOrSet(voter, utils.GetShareValue(balance, bTokenSharePerPoolCoin))
bTokenOwnMap.AddOrSet(voter, utils.GetShareValue(balance, bTokenSharePerPublicPositionShare))
}
}
}
Expand All @@ -241,7 +270,11 @@ func (k Keeper) SetLiquidStakingVotingPowers(ctx sdk.Context, votes govtypes.Vot
if err != nil {
continue
}
tokenAmount := k.TokenAmountFromFarmingPositions(ctx, voter, liquidBondDenom, bTokenSharePerPoolCoinMap)
tokenAmount := k.TokenAmountFromAMMPositions(ctx, voter, liquidBondDenom)
if tokenAmount.IsPositive() {
bTokenOwnMap.AddOrSet(vote.Voter, tokenAmount)
}
tokenAmount = k.TokenAmountFromFarmingPositions(ctx, voter, liquidBondDenom, bTokenSharePerPublicPositionShareMap)
if tokenAmount.IsPositive() {
bTokenOwnMap.AddOrSet(vote.Voter, tokenAmount)
}
Expand Down
Loading

0 comments on commit e8c451c

Please sign in to comment.