diff --git a/staker/__TEST_staker_short_warmup_period_internal_05_change_tier_test.gno b/staker/__TEST_staker_short_warmup_period_internal_05_change_tier_test.gno new file mode 100644 index 000000000..9d39af86f --- /dev/null +++ b/staker/__TEST_staker_short_warmup_period_internal_05_change_tier_test.gno @@ -0,0 +1,236 @@ +package staker + +import ( + "math" + "std" + "testing" + + "gno.land/p/demo/uassert" + + "gno.land/r/gnoswap/v1/consts" + + en "gno.land/r/gnoswap/v1/emission" + pl "gno.land/r/gnoswap/v1/pool" + pn "gno.land/r/gnoswap/v1/position" + + "gno.land/r/gnoswap/v1/gnft" + "gno.land/r/gnoswap/v1/gns" + + "gno.land/r/onbloc/bar" + "gno.land/r/onbloc/baz" + "gno.land/r/onbloc/qux" +) + +func TestShortWarmUpChangeTier(t *testing.T) { + testInit(t) + testDoubleMint(t) + testCreatePool(t) + testMintBarQux100_1(t) + testMintBarBaz100_2(t) + testSkip100Height(t) + testStakeToken_1(t) + testSetPoolTier(t) + testStakeToken_2(t) + testNow(t) + testChangePoolTier(t) + testNow2(t) +} + +func testInit(t *testing.T) { + t.Run("init pool tiers", func(t *testing.T) { + std.TestSetRealm(adminRealm) + + // init pool tiers + // tier 1 + deletePoolTier(t, MUST_EXISTS_IN_TIER_1) + addPoolTier(t, `gno.land/r/onbloc/bar:gno.land/r/onbloc/qux:100`, 1) + std.TestSkipHeights(1) + + // override warm-up period for testing + changeWarmup(t, 0, 150) + changeWarmup(t, 1, 300) + changeWarmup(t, 2, 900) + changeWarmup(t, 3, math.MaxInt64) + + // set unstaking fee to 0 + SetUnstakingFeeByAdmin(0) + + // set pool creation fee to 0 + pl.SetPoolCreationFeeByAdmin(0) + + // set community pool distribution to 0% (give it to devOps) + en.ChangeDistributionPctByAdmin( + 1, 7500, + 2, 2500, + 3, 0, + 4, 0, + ) + }) +} + +func testDoubleMint(t *testing.T) { + t.Run("mint and distribute gns", func(t *testing.T) { + en.MintAndDistributeGns() + en.MintAndDistributeGns() + + std.TestSkipHeights(1) + }) +} + +func testCreatePool(t *testing.T) { + t.Run("create pool", func(t *testing.T) { + std.TestSetRealm(adminRealm) + + gns.Approve(a2u(consts.POOL_ADDR), pl.GetPoolCreationFee()*3) + + pl.CreatePool(barPath, quxPath, 100, "79228162514264337593543950337") + pl.CreatePool(barPath, bazPath, 3000, "79228162514264337593543950337") + + std.TestSkipHeights(1) + }) +} + +func testMintBarQux100_1(t *testing.T) { + t.Run("mint position 01, bar:qux:100", func(t *testing.T) { + std.TestSetRealm(adminRealm) + + bar.Approve(a2u(consts.POOL_ADDR), consts.UINT64_MAX) + qux.Approve(a2u(consts.POOL_ADDR), consts.UINT64_MAX) + + tokenId, liquidity, amount0, amount1 := pn.Mint( + barPath, // token0 + quxPath, // token1 + fee100, // fee + int32(-1000), // tickLower + int32(1000), // tickUpper + "50", // amount0Desired + "50", // amount1Desired + "1", // amount0Min + "1", // amount1Min + max_timeout, + adminAddr, + adminAddr, + ) + + uassert.Equal(t, tokenId, uint64(1)) + uassert.Equal(t, gnft.MustOwnerOf(tid(tokenId)), adminAddr) + + gpi := getPrintInfo(t) + // {"height":"126","time":"1234567896","gns":{"staker":"32106164","devOps":"10702053","communityPool":"0","govStaker":"0","protocolFee":"0","GnoswapAdmin":"100000000000000"},"pool":[{"poolPath":"gno.land/r/onbloc/bar:gno.land/r/onbloc/qux:100","tier":"1","numPoolSameTier":"1","position":[]}]} + + std.TestSkipHeights(1) + }) +} + +func testMintBarBaz100_2(t *testing.T) { + t.Run("mint position 02, bar:baz:3000", func(t *testing.T) { + std.TestSetRealm(adminRealm) + + bar.Approve(a2u(consts.POOL_ADDR), consts.UINT64_MAX) + baz.Approve(a2u(consts.POOL_ADDR), consts.UINT64_MAX) + + tokenId, liquidity, amount0, amount1 := pn.Mint( + barPath, // token0 + bazPath, // token1 + fee3000, // fee + int32(-1020), // tickLower + int32(1020), // tickUpper + "50", // amount0Desired + "50", // amount1Desired + "1", // amount0Min + "1", // amount1Min + max_timeout, + adminAddr, + adminAddr, + ) + + uassert.Equal(t, tokenId, uint64(2)) + uassert.Equal(t, gnft.MustOwnerOf(tid(tokenId)), adminAddr) + + gpi := getPrintInfo(t) + // {"height":"127","time":"1234567898","gns":{"staker":"42808219","devOps":"14269404","communityPool":"0","govStaker":"0","protocolFee":"0","GnoswapAdmin":"100000000000000"},"pool":[{"poolPath":"gno.land/r/onbloc/bar:gno.land/r/onbloc/qux:100","tier":"1","numPoolSameTier":"1","position":[]}]} + std.TestSkipHeights(1) + }) +} + +func testSkip100Height(t *testing.T) { + t.Run("skip 100 heights", func(t *testing.T) { + std.TestSkipHeights(100) + + gpi := getPrintInfo(t) + // {"height":"228","time":"1234568100","gns":{"staker":"1123715724","devOps":"374571905","communityPool":"0","govStaker":"0","protocolFee":"0","GnoswapAdmin":"100000000000000"},"pool":[{"poolPath":"gno.land/r/onbloc/bar:gno.land/r/onbloc/qux:100","tier":"1","numPoolSameTier":"1","position":[]}]} + + std.TestSkipHeights(1) + }) +} + +func testStakeToken_1(t *testing.T) { + t.Run("stake token 01", func(t *testing.T) { + std.TestSetRealm(adminRealm) + + gnft.Approve(consts.STAKER_ADDR, tid(1)) + StakeToken(1) + + gpi := getPrintInfo(t) + // {"height":"229","time":"1234568102","gns":{"staker":"1134417779","devOps":"378139256","communityPool":"0","govStaker":"0","protocolFee":"0","GnoswapAdmin":"100000000000000"},"pool":[{"poolPath":"gno.land/r/onbloc/bar:gno.land/r/onbloc/qux:100","tier":"1","numPoolSameTier":"1","position":[{"lpTokenId":"1","stakedHeight":"229","stakedTimestamp":"1234568102","stakedDuration":"0","fullAmount":"0","ratio":"30","warmUpAmount":"0","full30":"0","give30":"0","penalty30":"0","full50":"0","give50":"0","penalty50":"0","full70":"0","give70":"0","penalty70":"0","full100":"0","give100":"0","penalty100":"0"}]}]} + + std.TestSkipHeights(1) + }) +} + +func testSetPoolTier(t *testing.T) { + t.Run("set pool tier", func(t *testing.T) { + std.TestSkipHeights(100) // this reward should go to bar:qux:100 + + std.TestSetRealm(adminRealm) + + addPoolTier(t, `gno.land/r/onbloc/bar:gno.land/r/onbloc/baz:3000`, 2) + gpi := getPrintInfo(t) + // {"height":"330","time":"1234568304","gns":{"staker":"2215325284","devOps":"738441757","communityPool":"0","govStaker":"0","protocolFee":"0","GnoswapAdmin":"100000000000000"},"pool":[{"poolPath":"gno.land/r/onbloc/bar:gno.land/r/onbloc/baz:3000","tier":"2","numPoolSameTier":"2","position":[]},{"poolPath":"gno.land/r/onbloc/bar:gno.land/r/onbloc/qux:100","tier":"1","numPoolSameTier":"1","position":[{"lpTokenId":"1","stakedHeight":"229","stakedTimestamp":"1234568102","stakedDuration":"101","fullAmount":"1080907453","ratio":"30","warmUpAmount":"324272236","full30":"1080907453","give30":"324272236","penalty30":"756635217","full50":"0","give50":"0","penalty50":"0","full70":"0","give70":"0","penalty70":"0","full100":"0","give100":"0","penalty100":"0"}]}]} + std.TestSkipHeights(1) + }) +} + +func testStakeToken_2(t *testing.T) { + t.Run("stake token 02", func(t *testing.T) { + std.TestSetRealm(adminRealm) + + gnft.Approve(consts.STAKER_ADDR, tid(2)) + StakeToken(2) + + gpi := getPrintInfo(t) + // {"height":"331","time":"1234568306","gns":{"staker":"2226027339","devOps":"742009108","communityPool":"0","govStaker":"0","protocolFee":"0","GnoswapAdmin":"100000000000000"},"pool":[{"poolPath":"gno.land/r/onbloc/bar:gno.land/r/onbloc/baz:3000","tier":"2","numPoolSameTier":"2","position":[{"lpTokenId":"2","stakedHeight":"331","stakedTimestamp":"1234568306","stakedDuration":"0","fullAmount":"0","ratio":"30","warmUpAmount":"0","full30":"0","give30":"0","penalty30":"0","full50":"0","give50":"0","penalty50":"0","full70":"0","give70":"0","penalty70":"0","full100":"0","give100":"0","penalty100":"0"}]},{"poolPath":"gno.land/r/onbloc/bar:gno.land/r/onbloc/qux:100","tier":"1","numPoolSameTier":"1","position":[{"lpTokenId":"1","stakedHeight":"229","stakedTimestamp":"1234568102","stakedDuration":"102","fullAmount":"764126573","ratio":"30","warmUpAmount":"229237972","full30":"764126573","give30":"229237972","penalty30":"534888601","full50":"0","give50":"0","penalty50":"0","full70":"0","give70":"0","penalty70":"0","full100":"0","give100":"0","penalty100":"0"}]}]} + std.TestSkipHeights(1) + }) +} + +func testNow(t *testing.T) { + t.Run("now", func(t *testing.T) { + std.TestSetRealm(adminRealm) + + gpi := getPrintInfo(t) + // {"height":"332","time":"1234568308","gns":{"staker":"2236729394","devOps":"745576459","communityPool":"0","govStaker":"0","protocolFee":"0","GnoswapAdmin":"100000000000000"},"pool":[{"poolPath":"gno.land/r/onbloc/bar:gno.land/r/onbloc/baz:3000","tier":"2","numPoolSameTier":"2","position":[{"lpTokenId":"2","stakedHeight":"331","stakedTimestamp":"1234568306","stakedDuration":"1","fullAmount":"3210615","ratio":"30","warmUpAmount":"963184","full30":"3210615","give30":"963184","penalty30":"2247431","full50":"0","give50":"0","penalty50":"0","full70":"0","give70":"0","penalty70":"0","full100":"0","give100":"0","penalty100":"0"}]},{"poolPath":"gno.land/r/onbloc/bar:gno.land/r/onbloc/qux:100","tier":"1","numPoolSameTier":"1","position":[{"lpTokenId":"1","stakedHeight":"229","stakedTimestamp":"1234568102","stakedDuration":"103","fullAmount":"771618010","ratio":"30","warmUpAmount":"231485403","full30":"771618010","give30":"231485403","penalty30":"540132607","full50":"0","give50":"0","penalty50":"0","full70":"0","give70":"0","penalty70":"0","full100":"0","give100":"0","penalty100":"0"}]}]} + + std.TestSkipHeights(1) + }) +} + +func testChangePoolTier(t *testing.T) { + t.Run("change pool tier", func(t *testing.T) { + std.TestSetRealm(adminRealm) + + changePoolTier(t, `gno.land/r/onbloc/bar:gno.land/r/onbloc/baz:3000`, 1) + + gpi := getPrintInfo(t) + // {"height":"333","time":"1234568310","gns":{"staker":"2247431449","devOps":"749143810","communityPool":"0","govStaker":"0","protocolFee":"0","GnoswapAdmin":"100000000000000"},"pool":[{"poolPath":"gno.land/r/onbloc/bar:gno.land/r/onbloc/baz:3000","tier":"1","numPoolSameTier":"2","position":[{"lpTokenId":"2","stakedHeight":"331","stakedTimestamp":"1234568306","stakedDuration":"2","fullAmount":"6421231","ratio":"30","warmUpAmount":"1926369","full30":"6421231","give30":"1926369","penalty30":"4494862","full50":"0","give50":"0","penalty50":"0","full70":"0","give70":"0","penalty70":"0","full100":"0","give100":"0","penalty100":"0"}]},{"poolPath":"gno.land/r/onbloc/bar:gno.land/r/onbloc/qux:100","tier":"1","numPoolSameTier":"2","position":[{"lpTokenId":"1","stakedHeight":"229","stakedTimestamp":"1234568102","stakedDuration":"104","fullAmount":"779109447","ratio":"30","warmUpAmount":"233732834","full30":"779109447","give30":"233732834","penalty30":"545376613","full50":"0","give50":"0","penalty50":"0","full70":"0","give70":"0","penalty70":"0","full100":"0","give100":"0","penalty100":"0"}]}]} + + std.TestSkipHeights(1) + }) +} + +func testNow2(t *testing.T) { + std.TestSetRealm(adminRealm) + + gpi := getPrintInfo(t) + // {"height":"334","time":"1234568312","gns":{"staker":"2258133504","devOps":"752711161","communityPool":"0","govStaker":"0","protocolFee":"0","GnoswapAdmin":"100000000000000"},"pool":[{"poolPath":"gno.land/r/onbloc/bar:gno.land/r/onbloc/baz:3000","tier":"1","numPoolSameTier":"2","position":[{"lpTokenId":"2","stakedHeight":"331","stakedTimestamp":"1234568306","stakedDuration":"3","fullAmount":"5351026","ratio":"30","warmUpAmount":"1605308","full30":"5351026","give30":"1605308","penalty30":"3745718","full50":"0","give50":"0","penalty50":"0","full70":"0","give70":"0","penalty70":"0","full100":"0","give100":"0","penalty100":"0"}]},{"poolPath":"gno.land/r/onbloc/bar:gno.land/r/onbloc/qux:100","tier":"1","numPoolSameTier":"2","position":[{"lpTokenId":"1","stakedHeight":"229","stakedTimestamp":"1234568102","stakedDuration":"105","fullAmount":"399721625","ratio":"30","warmUpAmount":"119916487","full30":"399721625","give30":"119916487","penalty30":"279805138","full50":"0","give50":"0","penalty50":"0","full70":"0","give70":"0","penalty70":"0","full100":"0","give100":"0","penalty100":"0"}]}]} +} diff --git a/staker/api_calculation_base_data_test.gno b/staker/api_calculation_base_data_test.gnoB similarity index 100% rename from staker/api_calculation_base_data_test.gno rename to staker/api_calculation_base_data_test.gnoB diff --git a/staker/reward_calculation_canonical_test.gno b/staker/reward_calculation_canonical_test.gno index 8a632ceae..e5f86a16c 100644 --- a/staker/reward_calculation_canonical_test.gno +++ b/staker/reward_calculation_canonical_test.gno @@ -3,6 +3,7 @@ package staker // Evaluate against the canonical implementation import ( + "math" "std" "testing" "strings" @@ -49,6 +50,73 @@ func TestSimple(t *testing.T) { canonical.AssertCanonicalRewardOf(0, expected) } +// To check precision error +func TestLargeStakedLiquidity(t *testing.T) { + canonical := Setup(t) + + gnousdc := test_gnousdc + canonical.CreatePool(gnousdc, 1, 150) + + canonical.StakeToken( + 0, + gnousdc, + std.Address("gno1qyqszqgpqyqszqgpqyqszqgpqyqszqgp"), + 100, + 200, + q128, + ) + + canonical.NextBlock() + + canonical.AssertCanonicalInternalRewardPerPool(gnousdc, canonical.PerBlockEmission) + + expected := canonical.PerBlockEmission * 30 / 100 + canonical.AssertEmulatedRewardOf(0, expected) + canonical.AssertCanonicalRewardOf(0, expected) +} + +// To check precision error +func TestLargeStakedLiquidity_2(t *testing.T) { + canonical := Setup(t) + + gnousdc := test_gnousdc + canonical.CreatePool(gnousdc, 1, 150) + + u2_30 := uint64(1073741824) + u2_33 := uint64(17179869184/2) + + canonical.SetEmissionUpdate(math.MaxUint64/100) + + canonical.StakeToken( + 0, + gnousdc, + std.Address("gno1qyqszqgpqyqszqgpqyqszqgpqyqszqgp"), + 100, + 200, + u256.NewUint(u2_30), + ) + + canonical.StakeToken( + 1, + gnousdc, + std.Address("gno1qyqszqgpqyqszqgpqyqszqgpqyqszqgq"), + 100, + 200, + u256.NewUint(u2_33), + ) + + canonical.NextBlock() + + canonical.AssertCanonicalInternalRewardPerPool(gnousdc, canonical.PerBlockEmission) + + expected := canonical.PerBlockEmission / 100 * 30 + canonical.AssertEmulatedRewardOf(0, expected/9) + canonical.AssertCanonicalRewardOf(0, expected/9) + + canonical.AssertEmulatedRewardOf(1, expected/9*8) + canonical.AssertCanonicalRewardOf(1, expected/9*8) +} + // Tests simple case with tick crossing func TestTickCross_0(t *testing.T) { canonical := Setup(t) @@ -1036,5 +1104,4 @@ func TestTickCross_9(t *testing.T) { } canonical.AssertEmulatedRewardMap(canonicalRewardMap) } - -} +} \ No newline at end of file diff --git a/staker/reward_calculation_incentives.gno b/staker/reward_calculation_incentives.gno index 588fcc87c..b5637c9a4 100644 --- a/staker/reward_calculation_incentives.gno +++ b/staker/reward_calculation_incentives.gno @@ -7,35 +7,36 @@ import ( "gno.land/p/demo/ufmt" + i256 "gno.land/p/gnoswap/int256" u256 "gno.land/p/gnoswap/uint256" ) type IncentiveBound struct { Incentive ExternalIncentive - IsEnter bool + IsEnter bool } // per-pool incentives type Incentives struct { - byTime *avl.Tree // (startTime, endTime, creator, rewardToken) => ExternalIncentive - byHeight *avl.Tree // (startHeight, endHeight, creator, rewardToken) => ExternalIncentive + byTime *avl.Tree // (startTime, endTime, creator, rewardToken) => ExternalIncentive + byHeight *avl.Tree // (startHeight, endHeight, creator, rewardToken) => ExternalIncentive byCreator *avl.Tree // (creator, startHeight, endHeight, rewardToken) => ExternalIncentive incentiveBound *UintTree // blockNumber -> []IncentiveBound - rewardCache *RewardCacheTree // blockNumber -> map[string]*u256.Uint + rewardCache *RewardCacheTree // blockNumber -> map[string]*u256.Uint lastRewardCacheHeight *uint64 } func NewIncentives(currentHeight uint64) Incentives { return Incentives{ - byTime: avl.NewTree(), - byHeight: avl.NewTree(), + byTime: avl.NewTree(), + byHeight: avl.NewTree(), byCreator: avl.NewTree(), incentiveBound: NewUintTree(), - rewardCache: NewRewardCacheTree(), + rewardCache: NewRewardCacheTree(), lastRewardCacheHeight: ¤tHeight, } } @@ -51,7 +52,7 @@ func (self *Incentives) Get(startTime, endTime int64, creator std.Address, rewar if !ok { return nil, false } - return value.(*ExternalIncentive), true + return value.(*ExternalIncentive), true } func (self *Incentives) GetByIncentiveId(incentiveId string) (*ExternalIncentive, bool) { @@ -90,7 +91,7 @@ func (self *Incentives) CurrentReward(currentHeight uint64) map[string]*u256.Uin } func (self *Incentives) create( - creator std.Address, + creator std.Address, incentive *ExternalIncentive, ) { byTimeId := incentiveIdByTime(uint64(incentive.startTimestamp), uint64(incentive.endTimestamp), creator, incentive.rewardToken) @@ -110,7 +111,7 @@ func (self *Incentives) create( startIncentiveBound := self.GetBound(uint64(incentive.startHeight)) startIncentiveBound = append(startIncentiveBound, IncentiveBound{ Incentive: *incentive, - IsEnter: true, + IsEnter: true, }) self.incentiveBound.Set(uint64(incentive.startHeight), startIncentiveBound) @@ -118,42 +119,40 @@ func (self *Incentives) create( endIncentiveBound := self.GetBound(endHeight) endIncentiveBound = append(endIncentiveBound, IncentiveBound{ Incentive: *incentive, - IsEnter: false, + IsEnter: false, }) self.incentiveBound.Set(endHeight, endIncentiveBound) - /* - println("endIncentiveBound") - for _, bound := range endIncentiveBound { - println(bound.Incentive.rewardToken, bound.IsEnter) - } - */ + println("endIncentiveBound") + for _, bound := range endIncentiveBound { + println(bound.Incentive.rewardToken, bound.IsEnter) + } } // endHeight MUST be less than or equal to the current block height func (self *Incentives) cacheRewardPerLiquidityUnit(startHeight, endHeight uint64, stakedLiquidity *u256.Uint) { currentReward := self.CurrentReward(startHeight) - /* - delta := self.GetDelta(startHeight) - reward := make(map[string]*u256.Uint) - for token, deltaAmount := range delta { - delta := i256.NewInt(deltaAmount) - deltaQ96 := delta.Mul(delta, _iQ96) - currentRewardToken, ok := currentReward[token] - if !ok { - currentRewardToken = u256.Zero() - } - deltaApplied := liquidityMathAddDelta(currentRewardToken, deltaQ96) - if deltaApplied.IsZero() { - continue - } - reward[token] = deltaApplied - println(startHeight, token, deltaAmount, deltaQ96.ToString(), currentRewardToken.ToString(), deltaApplied.ToString()) +/* + delta := self.GetDelta(startHeight) + reward := make(map[string]*u256.Uint) + for token, deltaAmount := range delta { + delta := i256.NewInt(deltaAmount) + deltaQ96 := delta.Mul(delta, _iQ96) + currentRewardToken, ok := currentReward[token] + if !ok { + currentRewardToken = u256.Zero() + } + deltaApplied := liquidityMathAddDelta(currentRewardToken, deltaQ96) + if deltaApplied.IsZero() { + continue } + reward[token] = deltaApplied + println(startHeight, token, deltaAmount, deltaQ96.ToString(), currentRewardToken.ToString(), deltaApplied.ToString()) + } - self.rewardCache.Set(startHeight, reward) - */ + self.rewardCache.Set(startHeight, reward) +*/ self.incentiveBound.Iterate(startHeight, endHeight, func(key uint64, value interface{}) bool { bound := value.([]IncentiveBound) @@ -164,8 +163,8 @@ func (self *Incentives) cacheRewardPerLiquidityUnit(startHeight, endHeight uint6 for _, bound := range bound { if bound.IsEnter { delta := u256.NewUint(bound.Incentive.rewardPerBlock) - deltaQ128 := delta.Mul(delta, q128) - ratio := u256.Zero().Div(deltaQ128, stakedLiquidity) + deltaQ192 := delta.Mul(delta, q192) + ratio := u256.Zero().Div(deltaQ192, stakedLiquidity) reward[bound.Incentive.incentiveId] = ratio } else { delete(reward, bound.Incentive.incentiveId) @@ -175,4 +174,4 @@ func (self *Incentives) cacheRewardPerLiquidityUnit(startHeight, endHeight uint6 return false }) *self.lastRewardCacheHeight = endHeight -} +} \ No newline at end of file diff --git a/staker/reward_calculation_pool.gno b/staker/reward_calculation_pool.gno index 38e3ff4b4..a6ff07ff5 100644 --- a/staker/reward_calculation_pool.gno +++ b/staker/reward_calculation_pool.gno @@ -12,6 +12,7 @@ import ( ) var q128 = u256.MustFromDecimal(consts.Q128) +var q192 = u256.MustFromDecimal("6277101735386680763835789423207666416102355444464034512895") type Pools struct { tree *avl.Tree // string poolPath -> pool @@ -58,7 +59,7 @@ type Pool struct { stakedLiquidity *UintTree // blockNumber -> *u256.Uint // cache of the internal reward per liquidity for each pool in tier - // value: (internal reward / total staked liquidity) * Q128 + // value: (internal reward / total staked liquidity) * Q192 rewardCache *RewardCacheTree // blockNumber -> *u256.Uint lastRewardCacheHeight *uint64 @@ -78,6 +79,7 @@ func NewPool(poolPath string, currentHeight uint64) *Pool { } } +// Returns the latest staked liquidity at the height equal or before the current height func (self *Pool) CurrentStakedLiquidity(currentHeight uint64) *u256.Uint { stakedLiquidityI, ok := self.stakedLiquidity.Get(currentHeight) if ok { @@ -105,16 +107,21 @@ func (self *Pool) IsExternallyIncentivizedPool(currentHeight uint64) bool { } func (self *Pool) cacheRewardPerLiquidityUnit(startHeight, endHeight uint64, currentTierReward uint64) { + if currentTierReward == 0 { + self.rewardCache.Set(startHeight, u256.Zero()) + return + } + stakedLiquidity := self.CurrentStakedLiquidity(startHeight) ratio := u256.NewUint(currentTierReward) - ratio = u256.Zero().Mul(ratio, q128) + ratio = u256.Zero().Mul(ratio, q192) ratio = u256.Zero().Div(ratio, stakedLiquidity) self.rewardCache.Set(startHeight, ratio) self.stakedLiquidity.Iterate(startHeight, endHeight, func(height uint64, value interface{}) bool { stakedLiquidity := value.(*u256.Uint) ratio := u256.NewUint(currentTierReward) - ratio = u256.Zero().Mul(ratio, q128) + ratio = u256.Zero().Mul(ratio, q192) ratio = u256.Zero().Div(ratio, stakedLiquidity) self.rewardCache.Set(height, ratio) @@ -124,44 +131,15 @@ func (self *Pool) cacheRewardPerLiquidityUnit(startHeight, endHeight uint64, cur func (self *Pool) cacheInternalReward(poolTier *PoolTier, endHeight uint64) { startHeight := *self.lastRewardCacheHeight - currentTier := poolTier.CurrentTier(self.poolPath, startHeight) - currentTierReward := uint64(0) - poolTier.membershipOf(self.poolPath).Iterate(startHeight, endHeight, func(membershipUpdateHeight uint64, value interface{}) bool { - if currentTier == 0 { - // ignore reward updates - startHeight = membershipUpdateHeight - currentTier = value.(uint64) - return false - } + currentTierReward := poolTier.CurrentReward(poolTier.CurrentTier(self.poolPath, startHeight), startHeight) - currentTierReward = poolTier.CurrentReward(currentTier, startHeight) - - poolTier.rewardCacheOf(currentTier).Iterate(startHeight, membershipUpdateHeight, func(rewardUpdateHeight uint64, value interface{}) bool { - self.cacheRewardPerLiquidityUnit(startHeight, rewardUpdateHeight, currentTierReward) - currentTierReward = value.(uint64) - startHeight = rewardUpdateHeight - return false - }) - currentTier = value.(uint64) - if currentTier == 0 { - self.rewardCache.Set(membershipUpdateHeight, u256.Zero()) - } else { - self.cacheRewardPerLiquidityUnit(startHeight, membershipUpdateHeight, currentTierReward) - } - startHeight = membershipUpdateHeight - return false - }) + tierMembershipHeights, tierMembershipUpdates := poolTier.TierRewardUpdates(self.poolPath, startHeight, endHeight) - // take account of tier reward changes after the last tier membership change - if currentTier == 0 { - return - } - poolTier.rewardCacheOf(currentTier).Iterate(startHeight, endHeight, func(height uint64, value interface{}) bool { + for i, height := range tierMembershipHeights { self.cacheRewardPerLiquidityUnit(startHeight, height, currentTierReward) - currentTierReward = value.(uint64) + currentTierReward = tierMembershipUpdates[i] startHeight = height - return false - }) + } self.cacheRewardPerLiquidityUnit(startHeight, endHeight, currentTierReward) } @@ -225,17 +203,16 @@ func (self *ExternalRewardState) Calculate(startHeight, endHeight int64, current penalties[i][incentiveId] = penalty.Uint64() } } + return rewards, penalties } func (self *ExternalRewardState) AccumulateReward(startHeight, endHeight uint64) { self.pool.incentives.rewardCache.RewardPerInterval(startHeight, endHeight, func(blockNumber uint64, poolRewardI interface{}) { - poolRewardRatios := map[string]*u256.Uint{} if poolRewardI != nil { poolRewardRatios = poolRewardI.(map[string]*u256.Uint) } - for incentiveId, rewardRatio := range poolRewardRatios { positionReward := u256.Zero().Mul(self.deposit.liquidity, rewardRatio) positionReward = u256.Zero().Mul(positionReward, u256.NewUint(blockNumber)) @@ -261,8 +238,8 @@ func (self *ExternalRewardState) ApplyWarmup() { warmupPenalty := u256.Zero().Sub(reward, warmupReward) - warmupReward = warmupReward.Div(warmupReward, q128) - warmupPenalty = warmupPenalty.Div(warmupPenalty, q128) + warmupReward = warmupReward.Div(warmupReward, q192) + warmupPenalty = warmupPenalty.Div(warmupPenalty, q192) self.rewards[i][incentiveId] = warmupReward self.penalties[i][incentiveId] = warmupPenalty @@ -271,7 +248,7 @@ func (self *ExternalRewardState) ApplyWarmup() { } func (self *ExternalRewardState) TickCrossesToExternalReward(startHeight, endHeight int64, currentlyInRange bool, tickUpperCrosses []int64, tickLowerCrosses []int64) { - for i, warmup := range self.deposit.warmups { + for _, warmup := range self.deposit.warmups { self.currentWarmup = warmup if startHeight >= warmup.NextWarmupHeight { @@ -366,11 +343,11 @@ func (self *InternalRewardState) ApplyWarmup() { continue } reward := u256.Zero() - totalReward := u256.Zero().Div(self.rewards[i], q128) + totalReward := u256.Zero().Div(self.rewards[i], q192) reward = reward.Mul(self.rewards[i], u256.NewUint(warmup.WarmupRatio)) reward = reward.Div(reward, u256.NewUint(100)) - reward = reward.Div(reward, q128) + reward = reward.Div(reward, q192) penalty := u256.Zero().Sub(totalReward, reward) diff --git a/staker/reward_calculation_pool_tier.gno b/staker/reward_calculation_pool_tier.gno index 9f41f901c..8deb8bfed 100644 --- a/staker/reward_calculation_pool_tier.gno +++ b/staker/reward_calculation_pool_tier.gno @@ -279,66 +279,6 @@ func (self *PoolTier) TierRatioUpdates(tier uint64, startHeight, endHeight uint6 return heights, updates } -/* - func (self *PoolTier) TierDenominatorUpdates(tier uint64, startHeight, endHeight uint64) ([]uint64, []uint64) { - // count * (8400 / tierRatio) updates - currentCount := self.CurrentCount(tier, startHeight) - currentRatio := self.CurrentRatio(tier, startHeight) - heights := make([]uint64, 0) - updates := make([]uint64, 0) - - tierCountTree := self.countOf(tier) - lastHeight := startHeight - tierCountTree.Iterate(startHeight, endHeight, func(height uint64, value interface{}) bool { - self.tierRatio.Iterate(lastHeight, height, func(ratioHeight uint64, value interface{}) bool { - currentRatio = value.(*TierRatio).Get(tier) - if currentRatio == 0 { - return false - } - update := currentCount * (TierRatioLCM / currentRatio) - if ratioHeight == lastHeight && len(heights) > 0 { - heights[len(heights)-1] = ratioHeight - updates[len(updates)-1] = update - } else { - heights = append(heights, ratioHeight) - updates = append(updates, update) - } - return false - }) - - if currentRatio == 0 { - lastHeight = height - return false - } - currentCount = value.(uint64) - heights = append(heights, height) - updates = append(updates, currentCount*(TierRatioLCM/currentRatio)) - lastHeight = height - - return false - }) - - // take account of ratio changes after the last count change - self.tierRatio.Iterate(lastHeight, endHeight, func(key uint64, value interface{}) bool { - ratioHeight := key - currentRatio = value.(*TierRatio).Get(tier) - if currentRatio == 0 { - return false - } - update := currentCount * (TierRatioLCM / currentRatio) - if ratioHeight == lastHeight && len(heights) > 0 { - heights[len(heights)-1] = ratioHeight - updates[len(updates)-1] = update - } else { - heights = append(heights, ratioHeight) - updates = append(updates, update) - } - return false - }) - - return heights, updates - } -*/ func (self *PoolTier) rewardCacheOf(tier uint64) *RewardCacheTree { switch tier { case 1: @@ -354,15 +294,72 @@ func (self *PoolTier) rewardCacheOf(tier uint64) *RewardCacheTree { )) } } + +func (self *PoolTier) cachePerMembership(heights []uint64, updates []uint64, poolPath string, currentTier uint64, startHeight, endHeight uint64) ([]uint64, []uint64) { + if currentTier != 0 { + currentReward := self.rewardCacheOf(currentTier).CurrentReward(startHeight).(uint64) + self.rewardCacheOf(currentTier).Iterate(startHeight, endHeight, func(tierRewardUpdateHeight uint64, value interface{}) bool { + if len(heights) != 0 && heights[len(heights)-1] == startHeight { + heights = heights[:len(heights)-1] + updates = updates[:len(updates)-1] + } + heights = append(heights, startHeight) + updates = append(updates, currentReward) + startHeight = tierRewardUpdateHeight + currentReward = value.(uint64) + return false + }) + if len(heights) != 0 && heights[len(heights)-1] == startHeight { + heights = heights[:len(heights)-1] + updates = updates[:len(updates)-1] + } + heights = append(heights, startHeight) + updates = append(updates, currentReward) + } else { + if len(heights) != 0 && heights[len(heights)-1] == startHeight { + heights = heights[:len(heights)-1] + updates = updates[:len(updates)-1] + } + heights = append(heights, startHeight) + updates = append(updates, uint64(0)) + } + + return heights, updates +} + +// Returns list of tier membership updates between startHeight and endHeight +// 1. last update *before* the startHeight, which must include at least one update(the initial update set in NewPoolTier) +// 2. all updates between startHeight and endHeight, end exclusive +func (self *PoolTier) TierRewardUpdates(poolPath string, startHeight, endHeight uint64) ([]uint64, []uint64) { + heights := make([]uint64, 0) + updates := make([]uint64, 0) + + currentTier := uint64(0) + + self.membershipOf(poolPath).Iterate(startHeight, endHeight, func(key uint64, value interface{}) bool { + heights, updates = self.cachePerMembership(heights, updates, poolPath, currentTier, startHeight, endHeight) + + startHeight = key + currentTier = value.(uint64) + return false + }) + + if startHeight != endHeight { + heights, updates = self.cachePerMembership(heights, updates, poolPath, currentTier, startHeight, endHeight) + } + + return heights, updates +} + func nextHeightUpdateI( - emissionUpdateHeights []uint64, + emissionUpdateHeights []uint64, emissionUpdateI int, - countUpdateHeights []uint64, + countUpdateHeights []uint64, countUpdateI int, - ratioUpdateHeights []uint64, + ratioUpdateHeights []uint64, ratioUpdateI int, ) ( - nextHeight uint64, + nextHeight uint64, nextEmissionUpdateI, nextCountUpdateI, nextRatioUpdateI int, ) { // Use a sentinel so that a “finished” slice does not interfere @@ -464,23 +461,6 @@ func (self *PoolTier) cacheTierReward(tier uint64, startHeight, endHeight uint64 countUpdateHeights, countUpdates := self.TierCountUpdates(tier, startHeight, endHeight) ratioUpdateHeights, ratioUpdates := self.TierRatioUpdates(tier, startHeight, endHeight) - // println(ufmt.Sprintf("cacheTierReward() || tier(%d) || startHeight(%d) || endHeight(%d)", tier, startHeight, endHeight)) - emissionUpdateHeightsStr := "emissionUpdateHeights: " - for i, height := range emissionUpdateHeights { - emissionUpdateHeightsStr += ufmt.Sprintf("{%d, %d}, ", height, emissionUpdates[i]) - } - // println(emissionUpdateHeightsStr) - countUpdateHeightsStr := "countUpdates: " - for i, height := range countUpdateHeights { - countUpdateHeightsStr += ufmt.Sprintf("{%d, %d}, ", height, countUpdates[i]) - } - // println(countUpdateHeightsStr) - ratioUpdateHeightsStr := "ratioUpdateHeights: " - for i, height := range ratioUpdateHeights { - ratioUpdateHeightsStr += ufmt.Sprintf("{%d, %d}, ", height, ratioUpdates[i]) - } - // println(ratioUpdateHeightsStr) - emissionUpdateI := 0 countUpdateI := 0 ratioUpdateI := 0 @@ -492,13 +472,12 @@ func (self *PoolTier) cacheTierReward(tier uint64, startHeight, endHeight uint64 currentRatio := ratioUpdates[ratioUpdateI] for emissionUpdateI < len(emissionUpdateHeights) || countUpdateI < len(countUpdateHeights) || ratioUpdateI < len(ratioUpdateHeights) { - // println(ufmt.Sprintf("0 cacheTierReward() || tier(%d) || currentHeight(%d) || emissionUpdate(%d:%d) || countUpdate(%d:%d) || ratioUpdate(%d:%d)", tier, currentHeight, emissionUpdateI, currentEmission, countUpdateI, currentCount, ratioUpdateI, currentRatio)) currentHeight, emissionUpdateI, countUpdateI, ratioUpdateI = nextHeightUpdateI( - emissionUpdateHeights, + emissionUpdateHeights, emissionUpdateI, - countUpdateHeights, + countUpdateHeights, countUpdateI, - ratioUpdateHeights, + ratioUpdateHeights, ratioUpdateI, ) @@ -512,15 +491,12 @@ func (self *PoolTier) cacheTierReward(tier uint64, startHeight, endHeight uint64 currentRatio = ratioUpdates[ratioUpdateI] } - // println(ufmt.Sprintf("1 cacheTierReward() || tier(%d) || currentHeight(%d) || emissionUpdate(%d:%d) || countUpdate(%d:%d) || ratioUpdate(%d:%d)", tier, currentHeight, emissionUpdateI, currentEmission, countUpdateI, currentCount, ratioUpdateI, currentRatio)) if currentCount == 0 { rewardCache.Set(currentHeight, uint64(0)) - // println(ufmt.Sprintf("cacheTierReward() set || tier(%d) || currentHeight(%d) || count(%d) || ratio(%d) || reward(%d)", tier, currentHeight, currentCount, currentRatio, 0)) } else { reward := currentEmission * currentRatio / currentCount / 100 rewardCache.Set(currentHeight, reward) - // println(ufmt.Sprintf("cacheTierReward() set || tier(%d) || currentHeight(%d) || count(%d) || ratio(%d) || reward(%d)", tier, currentHeight, currentCount, currentRatio, reward)) } } } diff --git a/staker/reward_calculation_types.gno b/staker/reward_calculation_types.gno index ec58c89d8..f46f4ef7b 100644 --- a/staker/reward_calculation_types.gno +++ b/staker/reward_calculation_types.gno @@ -5,6 +5,8 @@ import ( "strings" "gno.land/p/demo/avl" + + ufmt "gno.land/p/demo/ufmt" ) func EncodeUint(num uint64) string { @@ -19,7 +21,7 @@ func EncodeUint(num uint64) string { func DecodeUint(s string) uint64 { num, err := strconv.ParseUint(s, 10, 64) if err != nil { - panic(err.Error()) + panic(err) } return num } @@ -66,10 +68,6 @@ func (self *UintTree) ReverseIterate(start, end uint64, fn func(key uint64, valu }) } -func (self *UintTree) Size() uint64 { - return uint64(self.tree.Size()) -} - type RewardCacheTree struct { tree *avl.Tree } @@ -102,10 +100,6 @@ func (self *RewardCacheTree) Iterate(start, end uint64, fn func(key uint64, valu }) } -func (self *RewardCacheTree) Size() uint64 { - return uint64(self.tree.Size()) -} - func (self *RewardCacheTree) CurrentReward(currentHeight uint64) interface{} { result, ok := self.tree.Get(EncodeUint(currentHeight)) if ok { @@ -118,6 +112,14 @@ func (self *RewardCacheTree) CurrentReward(currentHeight uint64) interface{} { return result } +func (self *RewardCacheTree) CurrentRewardAt(currentHeight uint64) interface{} { + result, ok := self.tree.Get(EncodeUint(currentHeight)) + if ok { + return result + } + panic(ufmt.Sprintf("RewardCacheTree.CurrentRewardAt() || currentHeight: %d, not found", currentHeight)) +} + type RewardCalculationFunc = func(blockNumbers uint64, poolReward interface{}) func (self *RewardCacheTree) RewardPerInterval(startHeight, endHeight uint64, f RewardCalculationFunc) { @@ -132,4 +134,4 @@ func (self *RewardCacheTree) RewardPerInterval(startHeight, endHeight uint64, f if endHeight > currentHeight { f(endHeight-currentHeight, currentPoolReward) } -} +} \ No newline at end of file diff --git a/staker/tests/__TEST_staker_short_warmup_period_internal_05_change_tier_test.gnoA b/staker/tests/__TEST_staker_short_warmup_period_internal_05_change_tier_test.gnoA deleted file mode 100644 index 734bcb529..000000000 --- a/staker/tests/__TEST_staker_short_warmup_period_internal_05_change_tier_test.gnoA +++ /dev/null @@ -1,255 +0,0 @@ -package staker - -import ( - "std" - "testing" - "time" - - "gno.land/p/demo/uassert" - - "gno.land/r/gnoswap/v1/consts" - - en "gno.land/r/gnoswap/v1/emission" - pl "gno.land/r/gnoswap/v1/pool" - pn "gno.land/r/gnoswap/v1/position" - - "gno.land/r/gnoswap/v1/gnft" - "gno.land/r/gnoswap/v1/gns" - - "gno.land/r/onbloc/bar" - "gno.land/r/onbloc/baz" - "gno.land/r/onbloc/qux" -) - -func TestShortWarmUpChangeTier(t *testing.T) { - testInit(t) - testDoubleMint(t) - testCreatePool(t) - testMintBarQux100_1(t) - testMintBarBaz100_2(t) - testSkip100Height(t) - testStakeToken_1(t) - testSetPoolTier(t) - testStakeToken_2(t) - testNow(t) - testChangePoolTier(t) - testNow2(t) -} - -func testInit(t *testing.T) { - t.Run("initialize", func(t *testing.T) { - // init pool tiers - // tier 1 - delete(poolTiers, MUST_EXISTS_IN_TIER_1) - - poolTiers["gno.land/r/onbloc/bar:gno.land/r/onbloc/qux:100"] = InternalTier{ - tier: 1, - startTimestamp: time.Now().Unix(), - } - - std.TestSkipHeights(1) - - // override warm-up period for testing - warmUp[100] = 901 // 30m ~ - warmUp[70] = 301 // 10m ~ 30m - warmUp[50] = 151 // 5m ~ 10m - warmUp[30] = 1 // ~ 5m - }) -} - -func testDoubleMint(t *testing.T) { - t.Run("mint and distribute gns", func(t *testing.T) { - en.MintAndDistributeGns() - en.MintAndDistributeGns() - - std.TestSkipHeights(1) - }) -} - -func testCreatePool(t *testing.T) { - t.Run("create pool", func(t *testing.T) { - std.TestSetRealm(adminRealm) - - gns.Approve(a2u(consts.POOL_ADDR), pl.GetPoolCreationFee()*3) - - pl.CreatePool(barPath, quxPath, 100, "79228162514264337593543950337") - pl.CreatePool(barPath, bazPath, 3000, "79228162514264337593543950337") - - std.TestSkipHeights(1) - }) -} - -func testMintBarQux100_1(t *testing.T) { - t.Run("mint position 01, bar:qux:100", func(t *testing.T) { - curr := getCurrentInfo() - - std.TestSetRealm(adminRealm) - - bar.Approve(a2u(consts.POOL_ADDR), consts.UINT64_MAX) - qux.Approve(a2u(consts.POOL_ADDR), consts.UINT64_MAX) - - tokenId, liquidity, amount0, amount1 := pn.Mint( - barPath, // token0 - quxPath, // token1 - fee100, // fee - int32(-1000), // tickLower - int32(1000), // tickUpper - "50", // amount0Desired - "50", // amount1Desired - "1", // amount0Min - "1", // amount1Min - max_timeout, - admin, - admin, - ) - - uassert.Equal(t, tokenId, uint64(1)) - uassert.Equal(t, gnft.OwnerOf(tid(tokenId)), admin) - - gpi := GetPrintInfo() - uassert.Equal(t, gpi, `{"height":126,"time":1234567896,"gns":{"staker":0,"devOps":8561643,"communityPool":34246574,"govStaker":0,"protocolFee":200000000,"GnoswapAdmin":99999800000000},"pool":[{"poolPath":"gno.land/r/onbloc/bar:gno.land/r/onbloc/qux:100","startTimestamp":1234567890,"tier":1,"numPoolSameTier":1,"poolReward":0,"position":[]}]}`) - printInfo(curr) - - std.TestSkipHeights(1) - }) -} - -func testMintBarBaz100_2(t *testing.T) { - t.Run("mint position 02, bar:baz:3000", func(t *testing.T) { - curr := getCurrentInfo() - - std.TestSetRealm(adminRealm) - - bar.Approve(a2u(consts.POOL_ADDR), consts.UINT64_MAX) - baz.Approve(a2u(consts.POOL_ADDR), consts.UINT64_MAX) - - tokenId, liquidity, amount0, amount1 := pn.Mint( - barPath, // token0 - bazPath, // token1 - fee3000, // fee - int32(-1020), // tickLower - int32(1020), // tickUpper - "50", // amount0Desired - "50", // amount1Desired - "1", // amount0Min - "1", // amount1Min - max_timeout, - admin, - admin, - ) - - uassert.Equal(t, tokenId, uint64(2)) - uassert.Equal(t, gnft.OwnerOf(tid(tokenId)), admin) - - gpi := GetPrintInfo() - uassert.Equal(t, gpi, `{"height":127,"time":1234567898,"gns":{"staker":0,"devOps":11415524,"communityPool":45662099,"govStaker":0,"protocolFee":200000000,"GnoswapAdmin":99999800000000},"pool":[{"poolPath":"gno.land/r/onbloc/bar:gno.land/r/onbloc/qux:100","startTimestamp":1234567890,"tier":1,"numPoolSameTier":1,"poolReward":0,"position":[]}]}`) - printInfo(curr) - - std.TestSkipHeights(1) - }) -} - -func testSkip100Height(t *testing.T) { - t.Run("skip 100 heights", func(t *testing.T) { - curr := getCurrentInfo() - - std.TestSkipHeights(100) - - gpi := GetPrintInfo() - uassert.Equal(t, gpi, `{"height":228,"time":1234568100,"gns":{"staker":0,"devOps":299657525,"communityPool":1198630104,"govStaker":0,"protocolFee":200000000,"GnoswapAdmin":99999800000000},"pool":[{"poolPath":"gno.land/r/onbloc/bar:gno.land/r/onbloc/qux:100","startTimestamp":1234567890,"tier":1,"numPoolSameTier":1,"poolReward":0,"position":[]}]}`) - printInfo(curr) - - std.TestSkipHeights(1) - }) -} - -func testStakeToken_1(t *testing.T) { - t.Run("stake token 01", func(t *testing.T) { - curr := getCurrentInfo() - - std.TestSetRealm(adminRealm) - - gnft.Approve(a2u(GetOrigPkgAddr()), tid(1)) - StakeToken(1) - - gpi := GetPrintInfo() - uassert.Equal(t, gpi, `{"height":229,"time":1234568102,"gns":{"staker":0,"devOps":302511406,"communityPool":1210045629,"govStaker":0,"protocolFee":200000000,"GnoswapAdmin":99999800000000},"pool":[{"poolPath":"gno.land/r/onbloc/bar:gno.land/r/onbloc/qux:100","startTimestamp":1234567890,"tier":1,"numPoolSameTier":1,"poolReward":0,"position":[{"lpTokenId":1,"stakedHeight":229,"stakedTimestamp":1234568102,"stakedDuration":0,"fullAmount":0,"ratio":0,"warmUpAmount":0,"full30":0,"give30":0,"full50":0,"give50":0,"full70":0,"give70":0,"full100":0}]}]}`) - printInfo(curr) - - std.TestSkipHeights(1) - }) -} - -func testSetPoolTier(t *testing.T) { - t.Run("set pool tier", func(t *testing.T) { - curr := getCurrentInfo() - - std.TestSkipHeights(100) // this reward should go to bar:qux:100 - - std.TestSetRealm(adminRealm) - SetPoolTierByAdmin("gno.land/r/onbloc/bar:gno.land/r/onbloc/baz:3000", 2) - - gpi := GetPrintInfo() - uassert.Equal(t, gpi, `{"height":330,"time":1234568304,"gns":{"staker":1080907505,"devOps":590753407,"communityPool":1282106129,"govStaker":0,"protocolFee":200000000,"GnoswapAdmin":99999800000000},"pool":[{"poolPath":"gno.land/r/onbloc/bar:gno.land/r/onbloc/qux:100","startTimestamp":1234567890,"tier":1,"numPoolSameTier":1,"poolReward":1080907505,"position":[{"lpTokenId":1,"stakedHeight":229,"stakedTimestamp":1234568102,"stakedDuration":101,"fullAmount":1080907504,"ratio":30,"warmUpAmount":324272251,"full30":1080907504,"give30":324272251,"full50":0,"give50":0,"full70":0,"give70":0,"full100":0}]},{"poolPath":"gno.land/r/onbloc/bar:gno.land/r/onbloc/baz:3000","startTimestamp":1234568304,"tier":2,"numPoolSameTier":1,"poolReward":0,"position":[]}]}`) - printInfo(curr) - - std.TestSkipHeights(1) - }) -} - -func testStakeToken_2(t *testing.T) { - t.Run("stake token 02", func(t *testing.T) { - curr := getCurrentInfo() - - std.TestSetRealm(adminRealm) - - gnft.Approve(a2u(GetOrigPkgAddr()), tid(2)) - StakeToken(2) - - gpi := GetPrintInfo() - uassert.Equal(t, gpi, `{"height":331,"time":1234568306,"gns":{"staker":1088398944,"devOps":593607288,"communityPool":1286030215,"govStaker":0,"protocolFee":200000000,"GnoswapAdmin":99999800000000},"pool":[{"poolPath":"gno.land/r/onbloc/bar:gno.land/r/onbloc/qux:100","startTimestamp":1234567890,"tier":1,"numPoolSameTier":1,"poolReward":1088398943,"position":[{"lpTokenId":1,"stakedHeight":229,"stakedTimestamp":1234568102,"stakedDuration":102,"fullAmount":1088398942,"ratio":30,"warmUpAmount":326519682,"full30":1088398942,"give30":326519682,"full50":0,"give50":0,"full70":0,"give70":0,"full100":0}]},{"poolPath":"gno.land/r/onbloc/bar:gno.land/r/onbloc/baz:3000","startTimestamp":1234568304,"tier":2,"numPoolSameTier":1,"poolReward":0,"position":[{"lpTokenId":2,"stakedHeight":331,"stakedTimestamp":1234568306,"stakedDuration":0,"fullAmount":0,"ratio":0,"warmUpAmount":0,"full30":0,"give30":0,"full50":0,"give50":0,"full70":0,"give70":0,"full100":0}]}]}`) - printInfo(curr) - - std.TestSkipHeights(1) - }) -} - -func testNow(t *testing.T) { - t.Run("now", func(t *testing.T) { - curr := getCurrentInfo() - - std.TestSetRealm(adminRealm) - - gpi := GetPrintInfo() - uassert.Equal(t, gpi, `{"height":332,"time":1234568308,"gns":{"staker":1099100999,"devOps":596461169,"communityPool":1286743685,"govStaker":0,"protocolFee":200000000,"GnoswapAdmin":99999800000000},"pool":[{"poolPath":"gno.land/r/onbloc/bar:gno.land/r/onbloc/qux:100","startTimestamp":1234567890,"tier":1,"numPoolSameTier":1,"poolReward":1095890381,"position":[{"lpTokenId":1,"stakedHeight":229,"stakedTimestamp":1234568102,"stakedDuration":103,"fullAmount":1095890380,"ratio":30,"warmUpAmount":328767113,"full30":1095890380,"give30":328767113,"full50":0,"give50":0,"full70":0,"give70":0,"full100":0}]},{"poolPath":"gno.land/r/onbloc/bar:gno.land/r/onbloc/baz:3000","startTimestamp":1234568304,"tier":2,"numPoolSameTier":1,"poolReward":3210616,"position":[{"lpTokenId":2,"stakedHeight":331,"stakedTimestamp":1234568306,"stakedDuration":1,"fullAmount":3210616,"ratio":30,"warmUpAmount":963184,"full30":3210616,"give30":963184,"full50":0,"give50":0,"full70":0,"give70":0,"full100":0}]}]}`) - printInfo(curr) - - std.TestSkipHeights(1) - }) -} - -func testChangePoolTier(t *testing.T) { - t.Run("change pool tier", func(t *testing.T) { - curr := getCurrentInfo() - - std.TestSetRealm(adminRealm) - - ChangePoolTierByAdmin("gno.land/r/onbloc/bar:gno.land/r/onbloc/baz:3000", 1) - - gpi := GetPrintInfo() - uassert.Equal(t, gpi, `{"height":333,"time":1234568310,"gns":{"staker":1109803054,"devOps":599315050,"communityPool":1287457155,"govStaker":0,"protocolFee":200000000,"GnoswapAdmin":99999800000000},"pool":[{"poolPath":"gno.land/r/onbloc/bar:gno.land/r/onbloc/qux:100","startTimestamp":1234567890,"tier":1,"numPoolSameTier":2,"poolReward":1103381819,"position":[{"lpTokenId":1,"stakedHeight":229,"stakedTimestamp":1234568102,"stakedDuration":104,"fullAmount":1103381818,"ratio":30,"warmUpAmount":331014544,"full30":1103381818,"give30":331014544,"full50":0,"give50":0,"full70":0,"give70":0,"full100":0}]},{"poolPath":"gno.land/r/onbloc/bar:gno.land/r/onbloc/baz:3000","startTimestamp":1234568304,"tier":1,"numPoolSameTier":2,"poolReward":6421232,"position":[{"lpTokenId":2,"stakedHeight":331,"stakedTimestamp":1234568306,"stakedDuration":2,"fullAmount":6421232,"ratio":30,"warmUpAmount":1926368,"full30":6421232,"give30":1926368,"full50":0,"give50":0,"full70":0,"give70":0,"full100":0}]}]}`) - printInfo(curr) - - std.TestSkipHeights(1) - }) -} - -func testNow2(t *testing.T) { - curr := getCurrentInfo() - - std.TestSetRealm(adminRealm) - - gpi := GetPrintInfo() - uassert.Equal(t, gpi, `{"height":334,"time":1234568312,"gns":{"staker":1120505109,"devOps":602168931,"communityPool":1288170625,"govStaker":0,"protocolFee":200000000,"GnoswapAdmin":99999800000000},"pool":[{"poolPath":"gno.land/r/onbloc/bar:gno.land/r/onbloc/qux:100","startTimestamp":1234567890,"tier":1,"numPoolSameTier":2,"poolReward":1108732846,"position":[{"lpTokenId":1,"stakedHeight":229,"stakedTimestamp":1234568102,"stakedDuration":105,"fullAmount":1108732845,"ratio":30,"warmUpAmount":332619852,"full30":1108732845,"give30":332619852,"full50":0,"give50":0,"full70":0,"give70":0,"full100":0}]},{"poolPath":"gno.land/r/onbloc/bar:gno.land/r/onbloc/baz:3000","startTimestamp":1234568304,"tier":1,"numPoolSameTier":2,"poolReward":11772259,"position":[{"lpTokenId":2,"stakedHeight":331,"stakedTimestamp":1234568306,"stakedDuration":3,"fullAmount":11772259,"ratio":30,"warmUpAmount":3531676,"full30":11772259,"give30":3531676,"full50":0,"give50":0,"full70":0,"give70":0,"full100":0}]}]}`) - printInfo(curr) -}