diff --git a/staker/__TEST_staker_short_warmup_period_internal_01_test.gno b/staker/__TEST_staker_short_warmup_period_internal_01_test.gno new file mode 100644 index 000000000..79465cd93 --- /dev/null +++ b/staker/__TEST_staker_short_warmup_period_internal_01_test.gno @@ -0,0 +1,221 @@ +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" +) + +var testGnsBalanceTracker gnsBalanceTracker + +func TestShortWarmUpInternal(t *testing.T) { + testInit(t) + testDoubleMint(t) + testCreatePool(t) + testMintBarQux100_1(t) + testMintBarBaz100_2(t) + testStakeToken_1(t) + testSetPoolTier(t) + testStakeToken_2(t) +} + +func testInit(t *testing.T) { + t.Run("init pool tiers", func(t *testing.T) { + // init pool tiers + // tier 1 + // delete(poolTiers, MUST_EXISTS_IN_TIER_1) + deletePoolTier(t, MUST_EXISTS_IN_TIER_1) + + // poolTiers["gno.land/r/onbloc/bar:gno.land/r/onbloc/qux:100"] = InternalTier{ + // tier: 1, + // startTimestamp: time.Now().Unix(), + // } + addPoolTier(t, `gno.land/r/onbloc/bar:gno.land/r/onbloc/qux:100`, 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 + changeWarmup(t, 0, 150) + changeWarmup(t, 1, 300) + changeWarmup(t, 2, 900) + changeWarmup(t, 3, math.MaxInt64) + }) +} + +func testDoubleMint(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 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() + // 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":[]}]}`) + + std.TestSkipHeights(1) + }) +} + +func testMintBarBaz100_2(t *testing.T) { + t.Run("mint bar baz 100", 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) + + testGnsBalanceTracker = gnsBalanceCheck(t, testGnsBalanceTracker, false) + + 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, + ) + std.TestSkipHeights(1) + + uassert.Equal(t, tokenId, uint64(2)) + uassert.Equal(t, gnft.MustOwnerOf(tid(tokenId)), adminAddr) + + // 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":[]}]}`) + testGnsBalanceTracker = gnsBalanceCheck(t, testGnsBalanceTracker, true) + + }) +} + +func testStakeToken_1(t *testing.T) { + t.Run("stake token 1", func(t *testing.T) { + std.TestSetRealm(adminRealm) + + testGnsBalanceTracker = gnsBalanceCheck(t, testGnsBalanceTracker, false) + gnft.Approve(consts.STAKER_ADDR, tid(1)) + StakeToken(1) + std.TestSkipHeights(1) + + testGnsBalanceTracker = gnsBalanceCheck(t, testGnsBalanceTracker, true) + + // 1 block passed + positionRewards := calcPositionReward(uint64(std.GetHeight()), 1) + rewardPerWarmUp, penaltyPerWarmUp, rewardPerIncentivePerWarmUp, penaltyPertIncentivePerWarmUp := positionRewards.Internal, positionRewards.InternalPenalty, positionRewards.External, positionRewards.ExternalPenalty + + // uassert.Equal(t, uint64(6421232), rewardPerWarmUp[0]) + + println("rewardPerWarmUp") + println(rewardPerWarmUp) + println() + println("penaltyPerWarmUp") + println(penaltyPerWarmUp) + println() + println("rewardPerIncentivePerWarmUp") + println(rewardPerIncentivePerWarmUp) + println() + println("penaltyPertIncentivePerWarmUp") + println(penaltyPertIncentivePerWarmUp) + println() + }) +} + +func testSetPoolTier(t *testing.T) { + t.Run("set pool tier", func(t *testing.T) { + + std.TestSetRealm(adminRealm) + addPoolTier(t, "gno.land/r/onbloc/bar:gno.land/r/onbloc/baz:3000", 2) + // gpi := GetPrintInfo() + // uassert.Equal(t, gpi, `{"height":129,"time":1234567902,"gns":{"staker":10702055,"devOps":17123286,"communityPool":57791094,"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":10702055,"position":[{"lpTokenId":1,"stakedHeight":128,"stakedTimestamp":1234567900,"stakedDuration":1,"fullAmount":10702055,"ratio":30,"warmUpAmount":3210616,"full30":10702055,"give30":3210616,"full50":0,"give50":0,"full70":0,"give70":0,"full100":0}]},{"poolPath":"gno.land/r/onbloc/bar:gno.land/r/onbloc/baz:3000","startTimestamp":1234567902,"tier":2,"numPoolSameTier":1,"poolReward":0,"position":[]}]}`) + + std.TestSkipHeights(1) + }) +} + +func testStakeToken_2(t *testing.T) { + t.Run("stake token 2", func(t *testing.T) { + + std.TestSetRealm(adminRealm) + + gnft.Approve(consts.STAKER_ADDR, tid(2)) + StakeToken(2) + + // gpi := GetPrintInfo() + // uassert.Equal(t, gpi, `{"height":130,"time":1234567904,"gns":{"staker":18193494,"devOps":19977167,"communityPool":61715180,"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":18193493,"position":[{"lpTokenId":1,"stakedHeight":128,"stakedTimestamp":1234567900,"stakedDuration":2,"fullAmount":18193493,"ratio":30,"warmUpAmount":5458047,"full30":18193493,"give30":5458047,"full50":0,"give50":0,"full70":0,"give70":0,"full100":0}]},{"poolPath":"gno.land/r/onbloc/bar:gno.land/r/onbloc/baz:3000","startTimestamp":1234567902,"tier":2,"numPoolSameTier":1,"poolReward":0,"position":[{"lpTokenId":2,"stakedHeight":130,"stakedTimestamp":1234567904,"stakedDuration":0,"fullAmount":0,"ratio":0,"warmUpAmount":0,"full30":0,"give30":0,"full50":0,"give50":0,"full70":0,"give70":0,"full100":0}]}]}`) + + std.TestSkipHeights(1) + + /* + 소문자 + calcPositionReward + + 웜업별 + - 리워드 + - 페널티 + - 익스터널 리워드 + - 익스터널 페널티 + - 마지막 시점부터 + */ + + // std.TestSkipHeights(1) + // calcPositionReward(uint64(std.GetHeight()), 1) + }) +} diff --git a/staker/__TEST_staker_short_warmup_period_internal_external_90_test.gnoA b/staker/__TEST_staker_short_warmup_period_internal_external_90_test.gnoA new file mode 100644 index 000000000..594c5702b --- /dev/null +++ b/staker/__TEST_staker_short_warmup_period_internal_external_90_test.gnoA @@ -0,0 +1,236 @@ +// internal and external incentive + warm up period testing +// with two external incentives for same pool +// bar + gns +// with internal incentive for same pool + +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/qux" +) + +func TestShortWarmUpInternalAndExternalAllPositionInRange(t *testing.T) { + testInit(t) + testCreatePool(t) + testMintBarQux100_1(t) + testCreateExternalIncentiveBar(t) + testCreateExternalIncentiveGns(t) + testStakeToken_1(t) + // testBeforeActive(t) + // testAfterActive(t) + testDuration200(t) + testCollectReward(t) + testCollectRewardSameBlockNoReward(t) + testCollectRewardSingleBlock(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) + deletePoolTier(t, MUST_EXISTS_IN_TIER_1) + + // poolTiers["gno.land/r/onbloc/bar:gno.land/r/onbloc/qux:100"] = InternalTier{ + // tier: 1, + // startTimestamp: time.Now().Unix(), + // } + addPoolTier(t, `gno.land/r/onbloc/bar:gno.land/r/onbloc/qux:100`, 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 + changeWarmup(t, 0, 150) + changeWarmup(t, 1, 300) + changeWarmup(t, 2, 900) + changeWarmup(t, 3, math.MaxInt64) + }) +} + +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)), admin) + + std.TestSkipHeights(1) + }) +} + +func testCreateExternalIncentiveBar(t *testing.T) { + t.Run("create external incentive bar", func(t *testing.T) { + std.TestSetRealm(adminRealm) + + bar.Approve(a2u(consts.STAKER_ADDR), consts.UINT64_MAX) + gns.Approve(a2u(consts.STAKER_ADDR), depositGnsAmount) + + CreateExternalIncentive( + "gno.land/r/onbloc/bar:gno.land/r/onbloc/qux:100", // targetPoolPath string, + barPath, // rewardToken string, // token path should be registered + 20000000, + 1234569600, + 1234569600+TIMESTAMP_90DAYS, + ) + + // after + // printExternalInfo() + + std.TestSkipHeights(1) + }) +} + +func testCreateExternalIncentiveGns(t *testing.T) { + t.Run("create external incentive gns", func(t *testing.T) { + std.TestSetRealm(adminRealm) + + gns.Approve(a2u(consts.STAKER_ADDR), consts.UINT64_MAX) // this includes depositGnsAmount + + CreateExternalIncentive( + "gno.land/r/onbloc/bar:gno.land/r/onbloc/qux:100", // targetPoolPath string, + consts.GNS_PATH, // rewardToken string, // token path should be registered + 20000000, + 1234569600, + 1234569600+TIMESTAMP_90DAYS, + ) + + // after + // printExternalInfo() + + 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) + + std.TestSkipHeights(1) + }) +} + +func testAfterActive(t *testing.T) { + t.Run("after active", func(t *testing.T) { + std.TestSkipHeights(849) // in active + std.TestSkipHeights(1) // active // but no block passed since active + std.TestSkipHeights(50) // skip 50 more block + + std.TestSkipHeights(1) + }) +} + +func testDuration200(t *testing.T) { + t.Run("duration 200", func(t *testing.T) { + std.TestSkipHeights(199) // skip 1 + 199 = 200 more block + en.MintAndDistributeGns() + + // printExternalInfo() + }) +} + +func testCollectReward(t *testing.T) { + t.Run("collect reward", func(t *testing.T) { + std.TestSetRealm(adminRealm) + + oldBar := bar.BalanceOf(admin) + oldGns := gns.BalanceOf(admin) + + CollectReward(1, false) + + newBar := bar.BalanceOf(admin) + newGns := gns.BalanceOf(admin) + + uassert.Equal(t, newBar-oldBar, uint64(485)) + uassert.Equal(t, newGns-oldGns, uint64(7861515679)) // external 485 + internal 7861515194 + }) +} + +func testCollectRewardSameBlockNoReward(t *testing.T) { + t.Run("collect reward same block no reward", func(t *testing.T) { + std.TestSetRealm(adminRealm) + + oldBar := bar.BalanceOf(admin) + oldGns := gns.BalanceOf(admin) + + CollectReward(1, false) + + newBar := bar.BalanceOf(admin) + newGns := gns.BalanceOf(admin) + + // same block no change + uassert.Equal(t, newBar-oldBar, uint64(0)) + uassert.Equal(t, newGns-oldGns, uint64(0)) + }) +} + +func testCollectRewardSingleBlock(t *testing.T) { + t.Run("collect reward single block", func(t *testing.T) { + std.TestSkipHeights(1) + + std.TestSetRealm(adminRealm) + + oldBar := bar.BalanceOf(admin) + oldGns := gns.BalanceOf(admin) + + CollectReward(1, false) + + newBar := bar.BalanceOf(admin) + newGns := gns.BalanceOf(admin) + + uassert.Equal(t, newBar-oldBar, uint64(2)) // 2 bar from external incentive + uassert.Equal(t, newGns-oldGns, uint64(10595037)) // 10595035 gns from internal + 2 gns from external + }) +} diff --git a/staker/_helper_test.gno b/staker/_helper_test.gno index 79d58ccca..8f6fc18f2 100644 --- a/staker/_helper_test.gno +++ b/staker/_helper_test.gno @@ -5,6 +5,7 @@ import ( "testing" "gno.land/p/demo/testutils" + "gno.land/p/demo/uassert" pusers "gno.land/p/demo/users" "gno.land/r/demo/users" @@ -164,6 +165,7 @@ func init() { } var ( + adminAddr = consts.ADMIN admin = pusers.AddressOrName(consts.ADMIN) alice = pusers.AddressOrName(testutils.TestAddress("alice")) pool = pusers.AddressOrName(consts.POOL_ADDR) @@ -493,6 +495,7 @@ func burnAllNFT(t *testing.T) { } func TestBeforeResetObject(t *testing.T) { + t.Skip("can not be run with other tests") // make actual data to test resetting not only position's state but also pool's state std.TestSetRealm(adminRealm) @@ -540,3 +543,141 @@ func TestBeforeResetObject(t *testing.T) { // burnAllNFT(t) // uassert.Equal(t, gnft.TotalSupply(), uint64(0), "gnft total supply should be 0") // } + +func deletePoolTier(t *testing.T, poolPath string) { + poolTier.changeTier(uint64(std.GetHeight()), poolPath, 0) +} + +func addPoolTier(t *testing.T, poolPath string, tier uint64) { + poolTier.changeTier(uint64(std.GetHeight()), poolPath, tier) + pools.GetOrCreate(poolPath) +} + +func changeWarmup(t *testing.T, index int, blockDuration int64) { + modifyWarmup(index, blockDuration) +} + +func getNumPoolTiers(t *testing.T) (uint64, uint64, uint64) { + tier1Num := poolTier.count[1].tree.Size() + tier2Num := poolTier.count[2].tree.Size() + tier3Num := poolTier.count[3].tree.Size() + + return uint64(tier1Num), uint64(tier2Num), uint64(tier3Num) +} + +func GetPrintInfo() string { + // emissionDebug := ApiEmissionDebugInfo{} + // emissionDebug.Height = std.GetHeight() + // emissionDebug.Time = time.Now().Unix() + // emissionDebug.GnsStaker = gns.BalanceOf(a2u(consts.STAKER_ADDR)) + // emissionDebug.GnsDevOps = gns.BalanceOf(a2u(consts.DEV_OPS)) + // emissionDebug.GnsCommunityPool = gns.BalanceOf(a2u(consts.COMMUNITY_POOL_ADDR)) + // emissionDebug.GnsGovStaker = gns.BalanceOf(a2u(consts.GOV_STAKER_ADDR)) + // emissionDebug.GnsProtocolFee = gns.BalanceOf(a2u(consts.PROTOCOL_FEE_ADDR)) + // emissionDebug.GnsADMIN = gns.BalanceOf(a2u(consts.ADMIN)) + + // poolTiers.Iter(func(poolPath string, internalTier InternalTier) { + // tier := internalTier.tier + // pool := ApiEmissionDebugPool{} + // pool.PoolPath = poolPath + // pool.Tier = tier + + // numTier1, numTier2, numTier3 := getNumPoolTiers() // FIX + // if tier == 1 { + // pool.NumPoolInSameTier = numTier1 + // } else if tier == 2 { + // pool.NumPoolInSameTier = numTier2 + // } else if tier == 3 { + // pool.NumPoolInSameTier = numTier3 + // } + + // pool.PoolReward = poolGns[poolPath] // FIX + + // for lpTokenId, deposit := range deposits { + // if deposit.targetPoolPath == poolPath { + // position := ApiEmissionDebugPosition{} + // position.LpTokenId = lpTokenId + // position.StakedHeight = deposit.stakeHeight + // position.StakedTimestamp = deposit.stakeTimestamp + // position.StakedDuration = emissionDebug.Height - deposit.stakeHeight + + // position.FullAmount = positionGns[lpTokenId] // FIX + // position.Ratio = getRewardRatio(position.StakedDuration) // FIX + // position.RatioAmount = (position.FullAmount * position.Ratio) / 100 + + // pool.Position = append(pool.Position, position) + // } + // } + + // emissionDebug.Pool = append(emissionDebug.Pool, pool) + // }) + + // node := json.ObjectNode("", map[string]*json.Node{ + // "height": json.NumberNode("", float64(emissionDebug.Height)), + // "time": json.NumberNode("", float64(emissionDebug.Time)), + // "gns": json.ObjectNode("", map[string]*json.Node{ + // "staker": json.NumberNode("", float64(emissionDebug.GnsStaker)), + // "devOps": json.NumberNode("", float64(emissionDebug.GnsDevOps)), + // "communityPool": json.NumberNode("", float64(emissionDebug.GnsCommunityPool)), + // "govStaker": json.NumberNode("", float64(emissionDebug.GnsGovStaker)), + // "protocolFee": json.NumberNode("", float64(emissionDebug.GnsProtocolFee)), + // "GnoswapAdmin": json.NumberNode("", float64(emissionDebug.GnsADMIN)), + // }), + // "pool": json.ArrayNode("", makePoolsNode(emissionDebug.Pool)), + // }) + + // b, err := json.Marshal(node) + // if err != nil { + // return "JSON MARSHAL ERROR" + // } + + // return string(b) + + return "" +} + +type gnsBalanceTracker struct { + height uint64 + stakerBalance uint64 + devOpsBalance uint64 + communityPoolBalance uint64 + govStakerBalance uint64 + protocolFeeBalance uint64 + callerBalance uint64 +} + +func gnsBalanceCheck(t *testing.T, beforeBalance gnsBalanceTracker, printChange bool) gnsBalanceTracker { + t.Helper() + + caller := std.PrevRealm().Addr() + height := uint64(std.GetHeight()) + stakerBalance := gns.BalanceOf(a2u(consts.STAKER_ADDR)) + devOpsBalance := gns.BalanceOf(a2u(consts.DEV_OPS)) + communityPoolBalance := gns.BalanceOf(a2u(consts.COMMUNITY_POOL_ADDR)) + govStakerBalance := gns.BalanceOf(a2u(consts.GOV_STAKER_ADDR)) + protocolFeeBalance := gns.BalanceOf(a2u(consts.PROTOCOL_FEE_ADDR)) + callerBalance := gns.BalanceOf(a2u(caller)) + + if printChange { + println("[START] gnsBalanceCheck") + println("height", beforeBalance.height, "->", height, "|| diff", height-beforeBalance.height) + println("stakerBalance", beforeBalance.stakerBalance, "->", stakerBalance, "|| diff", stakerBalance-beforeBalance.stakerBalance) + println("devOpsBalance", beforeBalance.devOpsBalance, "->", devOpsBalance, "|| diff", devOpsBalance-beforeBalance.devOpsBalance) + println("communityPoolBalance", beforeBalance.communityPoolBalance, "->", communityPoolBalance, "|| diff", communityPoolBalance-beforeBalance.communityPoolBalance) + println("govStakerBalance", beforeBalance.govStakerBalance, "->", govStakerBalance, "|| diff", govStakerBalance-beforeBalance.govStakerBalance) + println("protocolFeeBalance", beforeBalance.protocolFeeBalance, "->", protocolFeeBalance, "|| diff", protocolFeeBalance-beforeBalance.protocolFeeBalance) + println("callerBalance", beforeBalance.callerBalance, "->", callerBalance, "|| diff", callerBalance-beforeBalance.callerBalance) + println("[END] gnsBalanceCheck") + println() + } + + return gnsBalanceTracker{ + height: height, + stakerBalance: stakerBalance, + devOpsBalance: devOpsBalance, + communityPoolBalance: communityPoolBalance, + govStakerBalance: govStakerBalance, + protocolFeeBalance: protocolFeeBalance, + callerBalance: callerBalance, + } +} diff --git a/staker/reward_calculation_pool_tier.gno b/staker/reward_calculation_pool_tier.gno index 313cabf3c..5622bc851 100644 --- a/staker/reward_calculation_pool_tier.gno +++ b/staker/reward_calculation_pool_tier.gno @@ -53,7 +53,7 @@ func TierRatioFromCounts(tier1Count, tier2Count, tier3Count uint64) *TierRatio { func (self *TierRatio) Get(tier uint64) uint64 { switch tier { case 1: - return self.Tier1 + return self.Tier1 case 2: return self.Tier2 case 3: @@ -92,7 +92,7 @@ func NewPoolTier(currentHeight uint64) *PoolTier { tierRatio: NewUintTree(), membership: avl.NewTree(), rewardCache: [4]*RewardCacheTree{ - nil, + nil, NewRewardCacheTree(), NewRewardCacheTree(), NewRewardCacheTree(), @@ -262,7 +262,6 @@ func (self *PoolTier) TierDenominatorUpdates(tier uint64, startHeight, endHeight 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 @@ -300,7 +299,7 @@ func (self *PoolTier) rewardCacheOf(tier uint64) *RewardCacheTree { } } -// emission update and tier membership update happens rarely. +// emission update and tier membership update happens rarely. // for each emission update(halving or ms per block change), // iterate over denominator updates and cache reward for each block func (self *PoolTier) cacheTierReward(tier uint64, startHeight, endHeight uint64, currentEmissionUpdateHeight uint64, currentEmissionUpdate uint64, emissionUpdateHeights []uint64, emissionUpdates []uint64) { @@ -332,7 +331,6 @@ func (self *PoolTier) cacheTierReward(tier uint64, startHeight, endHeight uint64 currentEmissionUpdateHeight = emissionUpdateHeights[i] currentEmissionUpdate = emissionUpdates[i] } - heights, updates = self.TierDenominatorUpdates(tier, currentEmissionUpdateHeight, endHeight) for j := 0; j < len(heights); j++ { @@ -346,11 +344,10 @@ func (self *PoolTier) cacheTierReward(tier uint64, startHeight, endHeight uint64 } } - func (self *PoolTier) cacheReward(endHeight uint64, emissionUpdateHeights []uint64, emissionUpdates []uint64) { startHeight := *self.lastRewardCacheHeight for tier := uint64(1); tier <= 3; tier++ { self.cacheTierReward(tier, startHeight, endHeight, emissionUpdateHeights[0], emissionUpdates[0], emissionUpdateHeights[1:], emissionUpdates[1:]) } -} \ No newline at end of file +} diff --git a/staker/reward_calculation_tick.gno b/staker/reward_calculation_tick.gno index 1efc4913b..34efea55e 100644 --- a/staker/reward_calculation_tick.gno +++ b/staker/reward_calculation_tick.gno @@ -11,8 +11,6 @@ import ( i256 "gno.land/p/gnoswap/int256" u256 "gno.land/p/gnoswap/uint256" - ufmt "gno.land/p/demo/ufmt" - pl "gno.land/r/gnoswap/v1/pool" ) @@ -173,12 +171,12 @@ func ForEachEligibleInterval(startHeight, endHeight int64, currentInRange bool, tickUpperCrossLen := len(tickUpperCross) tickLowerCrossLen := len(tickLowerCross) - println(ufmt.Sprintf("currentInRange: %v, tickUpperCrossLen: %d, tickLowerCrossLen: %d", currentInRange, tickUpperCrossLen, tickLowerCrossLen)) + // println(ufmt.Sprintf("currentInRange: %v, tickUpperCrossLen: %d, tickLowerCrossLen: %d", currentInRange, tickUpperCrossLen, tickLowerCrossLen)) for _, tickUpperCross := range tickUpperCross { - println(ufmt.Sprintf("tickUpperCross: %d", tickUpperCross)) + // println(ufmt.Sprintf("tickUpperCross: %d", tickUpperCross)) } for _, tickLowerCross := range tickLowerCross { - println(ufmt.Sprintf("tickLowerCross: %d", tickLowerCross)) + // println(ufmt.Sprintf("tickLowerCross: %d", tickLowerCross)) } for tickUpperCrossI < tickUpperCrossLen && tickLowerCrossI < tickLowerCrossLen { @@ -204,7 +202,7 @@ func ForEachEligibleInterval(startHeight, endHeight int64, currentInRange bool, if upperCross == lowerCross { if currentInRange { // exit range - println(ufmt.Sprintf("same zeroForOne exit range: startHeight: %d, endHeight: %d", startHeight, lowerHeight)) + // println(ufmt.Sprintf("same zeroForOne exit range: startHeight: %d, endHeight: %d", startHeight, lowerHeight)) f(uint64(startHeight), uint64(lowerHeight)) currentInRange = false } @@ -239,7 +237,7 @@ func ForEachEligibleInterval(startHeight, endHeight int64, currentInRange bool, } else { // exit range if currentInRange { - println(ufmt.Sprintf("upper tick passed exit range: startHeight: %d, endHeight: %d", startHeight, upperHeight)) + // println(ufmt.Sprintf("upper tick passed exit range: startHeight: %d, endHeight: %d", startHeight, upperHeight)) f(uint64(startHeight), uint64(upperHeight)) currentInRange = false } @@ -262,7 +260,7 @@ func ForEachEligibleInterval(startHeight, endHeight int64, currentInRange bool, } else { // exit range if currentInRange { - println(ufmt.Sprintf("lower tick passed exit range: startHeight: %d, endHeight: %d", startHeight, lowerHeight)) + // println(ufmt.Sprintf("lower tick passed exit range: startHeight: %d, endHeight: %d", startHeight, lowerHeight)) f(uint64(startHeight), uint64(lowerHeight)) currentInRange = false } @@ -289,7 +287,7 @@ func ForEachEligibleInterval(startHeight, endHeight int64, currentInRange bool, } else { // exit range if currentInRange { - println(ufmt.Sprintf("upper tick passed exit range: startHeight: %d, endHeight: %d", startHeight, cross)) + // println(ufmt.Sprintf("upper tick passed exit range: startHeight: %d, endHeight: %d", startHeight, cross)) f(uint64(startHeight), uint64(cross)) currentInRange = false } @@ -311,7 +309,7 @@ func ForEachEligibleInterval(startHeight, endHeight int64, currentInRange bool, } else { // exit range if currentInRange { - println(ufmt.Sprintf("lower tick passed exit range: startHeight: %d, endHeight: %d", startHeight, cross)) + // println(ufmt.Sprintf("lower tick passed exit range: startHeight: %d, endHeight: %d", startHeight, cross)) f(uint64(startHeight), uint64(-cross)) currentInRange = false } @@ -319,8 +317,8 @@ func ForEachEligibleInterval(startHeight, endHeight int64, currentInRange bool, } if currentInRange { - println("residual currentInRange") - println(ufmt.Sprintf("startHeight: %d, endHeight: %d", startHeight, endHeight)) + // println("residual currentInRange") + // println(ufmt.Sprintf("startHeight: %d, endHeight: %d", startHeight, endHeight)) f(uint64(startHeight), uint64(endHeight)) } diff --git a/staker/reward_calculation_warmup.gno b/staker/reward_calculation_warmup.gno index c5456de05..5d39f1f10 100644 --- a/staker/reward_calculation_warmup.gno +++ b/staker/reward_calculation_warmup.gno @@ -11,8 +11,8 @@ import ( ) type Warmup struct { - Index int - BlockDuration int64 + Index int + BlockDuration int64 NextWarmupHeight int64 // set to 0 for template WarmupRatio uint64 } @@ -28,28 +28,28 @@ func DefaultWarmupTemplate(currentHeight int64) []Warmup { return []Warmup{ { - Index: 0, + Index: 0, BlockDuration: blocksIn5Days, //NextWarmupHeight: currentHeight + blocksIn5Days, - WarmupRatio: 30, + WarmupRatio: 30, }, { - Index: 1, + Index: 1, BlockDuration: blocksIn10Days, //NextWarmupHeight: currentHeight + blocksIn10Days, - WarmupRatio: 50, + WarmupRatio: 50, }, { - Index: 2, + Index: 2, BlockDuration: blocksIn30Days, //NextWarmupHeight: currentHeight + blocksIn30Days, - WarmupRatio: 70, + WarmupRatio: 70, }, { - Index: 3, + Index: 3, BlockDuration: math.MaxInt64, //NextWarmupHeight: math.MaxInt64, - WarmupRatio: 100, + WarmupRatio: 100, }, } } @@ -66,11 +66,11 @@ func modifyWarmup(index int, blockDuration int64) { func InstantiateWarmup(currentHeight int64) []Warmup { warmups := make([]Warmup, 0) for _, warmup := range warmupTemplate { - warmups = append(warmups, Warmup { - Index: warmup.Index, - BlockDuration: warmup.BlockDuration, + warmups = append(warmups, Warmup{ + Index: warmup.Index, + BlockDuration: warmup.BlockDuration, NextWarmupHeight: currentHeight + warmup.BlockDuration, - WarmupRatio: warmup.WarmupRatio, + WarmupRatio: warmup.WarmupRatio, }) currentHeight += warmup.BlockDuration } @@ -110,7 +110,6 @@ func (warmup *Warmup) Apply(poolReward uint64, positionLiquidity, stakedLiquidit return totalReward.Uint64(), totalPenalty.Uint64() } - func (self *Deposit) FindWarmup(currentHeight int64) int { for i, warmup := range self.warmups { if currentHeight < warmup.NextWarmupHeight { diff --git a/staker/staker.gno b/staker/staker.gno index b705eee55..e29456ed9 100644 --- a/staker/staker.gno +++ b/staker/staker.gno @@ -13,12 +13,12 @@ import ( "gno.land/r/gnoswap/v1/gnft" "gno.land/r/gnoswap/v1/gns" - pl "gno.land/r/gnoswap/v1/pool" en "gno.land/r/gnoswap/v1/emission" + pl "gno.land/r/gnoswap/v1/pool" pn "gno.land/r/gnoswap/v1/position" - u256 "gno.land/p/gnoswap/uint256" i256 "gno.land/p/gnoswap/int256" + u256 "gno.land/p/gnoswap/uint256" ) type Deposits struct { @@ -34,7 +34,7 @@ func NewDeposits() *Deposits { func (self *Deposits) Get(tokenId uint64) *Deposit { depositI, ok := self.tree.Get(EncodeUint(tokenId)) if !ok { - panic(addDetailToError( + panic(addDetailToError( errDataNotFound, ufmt.Sprintf("staker.gno__Deposits.Get() || tokenId(%d) not found", tokenId), )) @@ -124,20 +124,19 @@ func StakeToken(tokenId uint64) (string, string, string) { if deposits.Has(tokenId) { panic(addDetailToError( errAlreadyStaked, - ufmt.Sprintf("staker.gno__StakeToken() || tokenId(%d) already staked", tokenId), + ufmt.Sprintf("tokenId(%d) already staked", tokenId), )) } - owner, err := gnft.OwnerOf(tid(tokenId)) if err != nil { - panic(err) + panic(err.Error()) } caller := std.PrevRealm().Addr() token0Amount, token1Amount, err := calculateStakeTokenAmount(tokenId, owner, caller) if err != nil { - panic(err) + panic(err.Error()) } // check pool path from tokenid @@ -149,14 +148,14 @@ func StakeToken(tokenId uint64) (string, string, string) { if !ok { panic(addDetailToError( errNonIncentivizedPool, - ufmt.Sprintf("staker.gno__StakeToken() || can not stake position to non incentivized pool(%s)", poolPath), + ufmt.Sprintf("can not stake position to non existing pool(%s)", poolPath), )) } hasExternal := pool.IsExternallyIncentivizedPool(uint64(std.GetHeight())) if hasInternal == false && hasExternal == false { panic(addDetailToError( errNonIncentivizedPool, - ufmt.Sprintf("staker.gno__StakeToken() || can not stake position to non incentivized pool(%s)", poolPath), + ufmt.Sprintf("can not stake position to non incentivized pool(%s)", poolPath), )) } @@ -166,7 +165,7 @@ func StakeToken(tokenId uint64) (string, string, string) { if liquidity.Lte(u256.Zero()) { panic(addDetailToError( errZeroLiquidity, - ufmt.Sprintf("staker.gno__StakeToken() || tokenId(%d) has no liquidity", tokenId), + ufmt.Sprintf("tokenId(%d) has no liquidity", tokenId), )) } @@ -189,7 +188,7 @@ func StakeToken(tokenId uint64) (string, string, string) { if caller == owner { // if caller is owner, transfer NFT ownership to staker contract if err := transferDeposit(tokenId, owner, caller, consts.STAKER_ADDR); err != nil { - panic(err) + panic(err.Error()) } } @@ -299,7 +298,7 @@ func transferDeposit(tokenId uint64, owner, caller, to std.Address) error { // ref: https://docs.gnoswap.io/contracts/staker/staker.gno#collectreward func CollectReward(tokenId uint64, unwrapResult bool) string { common.IsHalted() - + en.MintAndDistributeGns() deposit := deposits.Get(tokenId) @@ -316,7 +315,9 @@ func CollectReward(tokenId uint64, unwrapResult bool) string { gns.Transfer(a2u(consts.COMMUNITY_POOL_ADDR), reward.InternalPenalty) - panic("SEND EXTERNAL REWARD") + // panic("SEND EXTERNAL REWARD") + // totalExternalReward + // totalExternalPenalty prevAddr, prevRealm := getPrev() std.Emit( @@ -336,6 +337,7 @@ func CollectReward(tokenId uint64, unwrapResult bool) string { return "" } + /* func applyCollectReward( result *collectResult, @@ -463,7 +465,7 @@ func applyUnstake(tokenId uint64) { token0Amount, token1Amount, err := calculateStakeTokenAmount(tokenId, owner, caller) if err != nil { - panic(err) + panic(err.Error()) } prevAddr, prevRealm := getPrev() @@ -494,6 +496,10 @@ func requireTokenOwnership(owner, caller std.Address) error { // poolHasIncentives checks if the pool has any active incentives (internal or external). func poolHasIncentives(poolPath string) error { + pools.tree.Iterate("", "", func(poolPath string, value interface{}) bool { + return false + }) + // pool, ok := pools.Get(poolPath) if !ok { return ufmt.Errorf(