diff --git a/staker/filetests/z_average_block_time_change_from_gns_filetest.gnoA b/staker/filetests/z_average_block_time_change_from_gns_filetest.gnoA new file mode 100644 index 000000000..dcc70ab1a --- /dev/null +++ b/staker/filetests/z_average_block_time_change_from_gns_filetest.gnoA @@ -0,0 +1,189 @@ +// PKGPATH: gno.land/r/gnoswap/v1/staker_test + +// POOLs: +// 1. gnot:gns:3000 + +// POSITIONs: +// 1. in-range ( will be unstaked ) + +// REWARDs: +// - internal tier 1 ( gnot:gns:3000 ) +// - halving changes + +package staker_test + +import ( + "std" + "strconv" + + "gno.land/p/demo/grc/grc721" + "gno.land/p/demo/testutils" + + "gno.land/r/gnoswap/v1/common" + "gno.land/r/gnoswap/v1/consts" + + "gno.land/r/demo/wugnot" + "gno.land/r/gnoswap/v1/gns" + + "gno.land/r/gnoswap/v1/gnft" + + pl "gno.land/r/gnoswap/v1/pool" + pn "gno.land/r/gnoswap/v1/position" + sr "gno.land/r/gnoswap/v1/staker" +) + +var ( + adminAddr = consts.ADMIN + adminUser = common.AddrToUser(adminAddr) + adminRealm = std.NewUserRealm(adminAddr) + + // g1v4u8getjdeskcsmjv4shgmmjta047h6lua7mup + externalCreatorAddr = testutils.TestAddress("externalCreator") + externalCreatorUser = common.AddrToUser(externalCreatorAddr) + externalCreatorRealm = std.NewUserRealm(externalCreatorAddr) + + stakerAddr = consts.STAKER_ADDR + stakerUser = common.AddrToUser(stakerAddr) + stakerRealm = std.NewCodeRealm(consts.STAKER_PATH) + + wugnotAddr = consts.WUGNOT_ADDR + + fooPath = "gno.land/r/onbloc/foo" + barPath = "gno.land/r/onbloc/bar" + bazPath = "gno.land/r/onbloc/baz" + quxPath = "gno.land/r/onbloc/qux" + oblPath = "gno.land/r/onbloc/obl" + + gnsPath = "gno.land/r/gnoswap/v1/gns" + wugnotPath = "gno.land/r/demo/wugnot" + + fee100 uint32 = 100 + fee500 uint32 = 500 + fee3000 uint32 = 3000 + + max_timeout int64 = 9999999999 + + // external incentive deposit fee + depositGnsAmount uint64 = 1_000_000_000 // 1_000 GNS + + TIMESTAMP_90DAYS int64 = 90 * 24 * 60 * 60 + TIMESTAMP_180DAYS int64 = 180 * 24 * 60 * 60 + TIMESTAMP_365DAYS int64 = 365 * 24 * 60 * 60 + + poolPath = "gno.land/r/demo/wugnot:gno.land/r/gnoswap/v1/gns:3000" +) + +func main() { + testInit() + testCreatePool() + testMintWugnotGnsPos01() + testStakeTokenPos01() + testGnsAvgBlockTime() +} + +func testInit() { + std.TestSetRealm(adminRealm) + + // short warm-up period + sr.SetWarmUp(100, 901) + sr.SetWarmUp(70, 301) + sr.SetWarmUp(50, 151) + sr.SetWarmUp(30, 1) + + // prepare wugnot + std.TestIssueCoins(adminAddr, std.Coins{{"ugnot", 100_000_000_000_000}}) + banker := std.GetBanker(std.BankerTypeRealmSend) + banker.SendCoins(adminAddr, wugnotAddr, std.Coins{{"ugnot", 50_000_000_000_000}}) + std.TestSetOrigSend(std.Coins{{"ugnot", 50_000_000_000_000}}, nil) + wugnot.Deposit() + std.TestSetOrigSend(nil, nil) +} + +func testCreatePool() { + std.TestSetRealm(adminRealm) + + pl.SetPoolCreationFeeByAdmin(0) + + std.TestSkipHeights(1) + pl.CreatePool( + wugnotPath, + gnsPath, + fee3000, + common.TickMathGetSqrtRatioAtTick(0).ToString(), // 79228162514264337593543950337 + ) +} + +func testMintWugnotGnsPos01() { + std.TestSetRealm(adminRealm) + + wugnot.Approve(common.AddrToUser(consts.POOL_ADDR), consts.UINT64_MAX) + gns.Approve(common.AddrToUser(consts.POOL_ADDR), consts.UINT64_MAX) + + std.TestSkipHeights(1) + pn.Mint( + wugnotPath, + gnsPath, + fee3000, + int32(-60), + int32(60), + "50", + "50", + "1", + "1", + max_timeout, + adminAddr, + adminAddr, + ) +} + +func testStakeTokenPos01() { + std.TestSetRealm(adminRealm) + + // stake + gnft.Approve(consts.STAKER_ADDR, tokenIdFrom(1)) + sr.StakeToken(1) + + /* + std.TestSkipHeights(100) + + TODO: (after fixing unit test) check collected reward + - position-01 has 100 blocks of reward + */ +} + +func testGnsAvgBlockTime() { + std.TestSetRealm(adminRealm) + + std.TestSkipHeights(1) + gns.SetAvgBlockTimeInMsByAdmin(4000) // orig block time was 2000ms(2s), change it to 4s + + /* + std.TestSkipHeights(100) + + TODO: (after fixing unit test) check collected reward + - avg block time has changed + - meaning emission amount per block changes + - position-01 has.. + - 1. 100 blocks of reward when avg block time was 2000ms(2s) + - 2. 50 blocks of reward when avg block time was 4000ms(4s) + */ +} + +func tokenIdFrom(tokenId interface{}) grc721.TokenID { + if tokenId == nil { + panic("tokenId is nil") + } + + switch tokenId.(type) { + case string: + return grc721.TokenID(tokenId.(string)) + case int: + return grc721.TokenID(strconv.Itoa(tokenId.(int))) + case uint64: + return grc721.TokenID(strconv.Itoa(int(tokenId.(uint64)))) + case grc721.TokenID: + return tokenId.(grc721.TokenID) + default: + panic("unsupported tokenId type") + } +} diff --git a/staker/filetests/z_no_position_to_give_reward_filetest.gnoA b/staker/filetests/z_no_position_to_give_reward_filetest.gnoA new file mode 100644 index 000000000..262466e7b --- /dev/null +++ b/staker/filetests/z_no_position_to_give_reward_filetest.gnoA @@ -0,0 +1,180 @@ +// PKGPATH: gno.land/r/gnoswap/v1/staker_test + +// POOLs: +// 1. gnot:gns:3000 + +// POSITIONs: +// 1. in-range ( will be unstaked ) + +// REWARDs: +// - internal tier 1 ( gnot:gns:3000 ) +// - external bar ( gnot:gns:3000 ) + +package staker_test + +import ( + "std" + "strconv" + + "gno.land/p/demo/grc/grc721" + "gno.land/p/demo/testutils" + + "gno.land/r/gnoswap/v1/common" + "gno.land/r/gnoswap/v1/consts" + + "gno.land/r/demo/wugnot" + "gno.land/r/gnoswap/v1/gns" + + "gno.land/r/gnoswap/v1/gnft" + + pl "gno.land/r/gnoswap/v1/pool" + pn "gno.land/r/gnoswap/v1/position" + sr "gno.land/r/gnoswap/v1/staker" +) + +var ( + adminAddr = consts.ADMIN + adminUser = common.AddrToUser(adminAddr) + adminRealm = std.NewUserRealm(adminAddr) + + // g1v4u8getjdeskcsmjv4shgmmjta047h6lua7mup + externalCreatorAddr = testutils.TestAddress("externalCreator") + externalCreatorUser = common.AddrToUser(externalCreatorAddr) + externalCreatorRealm = std.NewUserRealm(externalCreatorAddr) + + stakerAddr = consts.STAKER_ADDR + stakerUser = common.AddrToUser(stakerAddr) + stakerRealm = std.NewCodeRealm(consts.STAKER_PATH) + + wugnotAddr = consts.WUGNOT_ADDR + + fooPath = "gno.land/r/onbloc/foo" + barPath = "gno.land/r/onbloc/bar" + bazPath = "gno.land/r/onbloc/baz" + quxPath = "gno.land/r/onbloc/qux" + oblPath = "gno.land/r/onbloc/obl" + + gnsPath = "gno.land/r/gnoswap/v1/gns" + wugnotPath = "gno.land/r/demo/wugnot" + + fee100 uint32 = 100 + fee500 uint32 = 500 + fee3000 uint32 = 3000 + + max_timeout int64 = 9999999999 + + // external incentive deposit fee + depositGnsAmount uint64 = 1_000_000_000 // 1_000 GNS + + TIMESTAMP_90DAYS int64 = 90 * 24 * 60 * 60 + TIMESTAMP_180DAYS int64 = 180 * 24 * 60 * 60 + TIMESTAMP_365DAYS int64 = 365 * 24 * 60 * 60 + + poolPath = "gno.land/r/demo/wugnot:gno.land/r/gnoswap/v1/gns:3000" +) + +func main() { + testInit() + testCreatePool() + testMintWugnotGnsPos01() + testStakeTokenPos01() + + testUnstakePos01() +} + +func testInit() { + std.TestSetRealm(adminRealm) + + // short warm-up period + sr.SetWarmUp(100, 901) + sr.SetWarmUp(70, 301) + sr.SetWarmUp(50, 151) + sr.SetWarmUp(30, 1) + + // prepare wugnot + std.TestIssueCoins(adminAddr, std.Coins{{"ugnot", 100_000_000_000_000}}) + banker := std.GetBanker(std.BankerTypeRealmSend) + banker.SendCoins(adminAddr, wugnotAddr, std.Coins{{"ugnot", 50_000_000_000_000}}) + std.TestSetOrigSend(std.Coins{{"ugnot", 50_000_000_000_000}}, nil) + wugnot.Deposit() + std.TestSetOrigSend(nil, nil) +} + +func testCreatePool() { + std.TestSetRealm(adminRealm) + + pl.SetPoolCreationFeeByAdmin(0) + + std.TestSkipHeights(1) + pl.CreatePool( + wugnotPath, + gnsPath, + fee3000, + common.TickMathGetSqrtRatioAtTick(0).ToString(), // 79228162514264337593543950337 + ) +} + +func testMintWugnotGnsPos01() { + std.TestSetRealm(adminRealm) + + wugnot.Approve(common.AddrToUser(consts.POOL_ADDR), consts.UINT64_MAX) + gns.Approve(common.AddrToUser(consts.POOL_ADDR), consts.UINT64_MAX) + + std.TestSkipHeights(1) + pn.Mint( + wugnotPath, + gnsPath, + fee3000, + int32(-60), + int32(60), + "50", + "50", + "1", + "1", + max_timeout, + adminAddr, + adminAddr, + ) +} + +func testStakeTokenPos01() { + std.TestSetRealm(adminRealm) + + // stake + gnft.Approve(consts.STAKER_ADDR, tokenIdFrom(1)) + sr.StakeToken(1) +} + +func testUnstakePos01() { + std.TestSetRealm(adminRealm) + + std.TestSkipHeights(1) + sr.UnstakeToken(1, false) + + /* + std.TestSkipHeights(1) + + TODO: (after fixing unit test) check collected reward + - no positions staked at gnot:gns:3000 + - community_pool will receive empty pool's reward + */ +} + +func tokenIdFrom(tokenId interface{}) grc721.TokenID { + if tokenId == nil { + panic("tokenId is nil") + } + + switch tokenId.(type) { + case string: + return grc721.TokenID(tokenId.(string)) + case int: + return grc721.TokenID(strconv.Itoa(tokenId.(int))) + case uint64: + return grc721.TokenID(strconv.Itoa(int(tokenId.(uint64)))) + case grc721.TokenID: + return tokenId.(grc721.TokenID) + default: + panic("unsupported tokenId type") + } +} diff --git a/staker/filetests/z_pool_add_to_tier2_and_change_to_tier3_internal_filetest.gnoA b/staker/filetests/z_pool_add_to_tier2_and_change_to_tier3_internal_filetest.gnoA new file mode 100644 index 000000000..c506af675 --- /dev/null +++ b/staker/filetests/z_pool_add_to_tier2_and_change_to_tier3_internal_filetest.gnoA @@ -0,0 +1,234 @@ +// PKGPATH: gno.land/r/gnoswap/v1/staker_test + +// POOLs: +// 1. gnot:gns:3000 +// 2. bar:baz:100 + +// POSITIONs: +// 1. in-range ( gnot:gns:3000 ) +// 2. in-range ( bar:baz:100 ) + +// REWARDs: +// - internal tier 1 ( gnot:gns:3000 ) +// - internal tier 2 ( bar:baz:100 ) -> will be changed to tier 3 + +package staker_test + +import ( + "std" + "strconv" + + "gno.land/p/demo/grc/grc721" + + "gno.land/r/gnoswap/v1/common" + "gno.land/r/gnoswap/v1/consts" + + "gno.land/r/demo/wugnot" + "gno.land/r/gnoswap/v1/gns" + "gno.land/r/onbloc/bar" + "gno.land/r/onbloc/baz" + + "gno.land/r/gnoswap/v1/gnft" + + pl "gno.land/r/gnoswap/v1/pool" + pn "gno.land/r/gnoswap/v1/position" + sr "gno.land/r/gnoswap/v1/staker" +) + +var ( + adminAddr = consts.ADMIN + adminUser = common.AddrToUser(adminAddr) + adminRealm = std.NewUserRealm(adminAddr) + + stakerAddr = consts.STAKER_ADDR + stakerUser = common.AddrToUser(stakerAddr) + stakerRealm = std.NewCodeRealm(consts.STAKER_PATH) + + wugnotAddr = consts.WUGNOT_ADDR + + fooPath = "gno.land/r/onbloc/foo" + barPath = "gno.land/r/onbloc/bar" + bazPath = "gno.land/r/onbloc/baz" + quxPath = "gno.land/r/onbloc/qux" + oblPath = "gno.land/r/onbloc/obl" + + gnsPath = "gno.land/r/gnoswap/v1/gns" + wugnotPath = "gno.land/r/demo/wugnot" + + fee100 uint32 = 100 + fee500 uint32 = 500 + fee3000 uint32 = 3000 + + max_timeout int64 = 9999999999 + + // external incentive deposit fee + depositGnsAmount uint64 = 1_000_000_000 // 1_000 GNS + + TIMESTAMP_90DAYS int64 = 90 * 24 * 60 * 60 + TIMESTAMP_180DAYS int64 = 180 * 24 * 60 * 60 + TIMESTAMP_365DAYS int64 = 365 * 24 * 60 * 60 +) + +func main() { + testInit() + testCreatePool() + + testMintAndStakeWugnotGnsPos01() + + testSetPoolTier2() // new pool is set to tier 2 + testMintAndStakeBarBazPos02() + + testChangePoolTier3() // pool is changed to tier 3 +} + +func testInit() { + std.TestSetRealm(adminRealm) + + // short warm-up period + sr.SetWarmUp(100, 901) + sr.SetWarmUp(70, 301) + sr.SetWarmUp(50, 151) + sr.SetWarmUp(30, 1) + + // prepare wugnot + std.TestIssueCoins(adminAddr, std.Coins{{"ugnot", 100_000_000_000_000}}) + banker := std.GetBanker(std.BankerTypeRealmSend) + banker.SendCoins(adminAddr, wugnotAddr, std.Coins{{"ugnot", 50_000_000_000_000}}) + std.TestSetOrigSend(std.Coins{{"ugnot", 50_000_000_000_000}}, nil) + wugnot.Deposit() + std.TestSetOrigSend(nil, nil) +} + +func testCreatePool() { + std.TestSetRealm(adminRealm) + + pl.SetPoolCreationFeeByAdmin(0) + + std.TestSkipHeights(1) + pl.CreatePool( + wugnotPath, + gnsPath, + fee3000, + common.TickMathGetSqrtRatioAtTick(0).ToString(), // 79228162514264337593543950337 + ) + pl.CreatePool( + barPath, + bazPath, + fee100, + common.TickMathGetSqrtRatioAtTick(0).ToString(), // 79228162514264337593543950337 + ) +} + +func testMintAndStakeWugnotGnsPos01() { + std.TestSetRealm(adminRealm) + + wugnot.Approve(common.AddrToUser(consts.POOL_ADDR), consts.UINT64_MAX) + gns.Approve(common.AddrToUser(consts.POOL_ADDR), consts.UINT64_MAX) + + std.TestSkipHeights(1) + pn.Mint( + wugnotPath, + gnsPath, + fee3000, + int32(-60), + int32(60), + "100", + "100", + "0", + "0", + max_timeout, + adminAddr, + adminAddr, + ) + + gnft.Approve(stakerAddr, tokenIdFrom(1)) + sr.StakeToken(1) + + /* + std.TestSkipHeights(1) + TODO: (after fixing unit test) check reward + */ +} + +func testSetPoolTier2() { + std.TestSetRealm(adminRealm) + + std.TestSkipHeights(1) + sr.SetPoolTierByAdmin("gno.land/r/onbloc/bar:gno.land/r/onbloc/baz:100", 2) +} + +func testMintAndStakeBarBazPos02() { + std.TestSetRealm(adminRealm) + + bar.Approve(common.AddrToUser(consts.POOL_ADDR), consts.UINT64_MAX) + baz.Approve(common.AddrToUser(consts.POOL_ADDR), consts.UINT64_MAX) + + std.TestSkipHeights(1) + pn.Mint( + barPath, + bazPath, + fee100, + int32(-50), + int32(50), + "100", + "100", + "0", + "0", + max_timeout, + adminAddr, + adminAddr, + ) + + gnft.Approve(stakerAddr, tokenIdFrom(2)) + sr.StakeToken(2) + + /* + std.TestSkipHeights(1) + + TODO: (after fixing unit test) check reward + - position-01 pool(gnot:gns:3000) is tier 1 + - position-02 pool(bar:baz:100) is tier 2 + - staker's emission reward should be distributed to each tier by ratio of 70% and 30% (70% for tier 1, 30% for tier 2) + */ +} + +func testChangePoolTier3() { + std.TestSetRealm(adminRealm) + + std.TestSkipHeights(1) + sr.ChangePoolTierByAdmin("gno.land/r/onbloc/bar:gno.land/r/onbloc/baz:100", 3) + + /* + std.TestSkipHeights(1) + + TODO: (after fixing unit test) check reward + - position-01 pool(gnot:gns:3000) is tier 1 + - position-02 pool(bar:baz:100) changed from tier 2 to tier 3 + - staker's emission reward should be distributed to each tier by ratio of 80% and 20% (80% for tier 1, 20% for tier 2) + + !! IMPORTANT: + user hasn' collected reward for position 2 yet + which means position 2 has reward both + - from pool when it was on tier2 and + - from pool when it is on tier3 + */ +} + +func tokenIdFrom(tokenId interface{}) grc721.TokenID { + if tokenId == nil { + panic("tokenId is nil") + } + + switch tokenId.(type) { + case string: + return grc721.TokenID(tokenId.(string)) + case int: + return grc721.TokenID(strconv.Itoa(tokenId.(int))) + case uint64: + return grc721.TokenID(strconv.Itoa(int(tokenId.(uint64)))) + case grc721.TokenID: + return tokenId.(grc721.TokenID) + default: + panic("unsupported tokenId type") + } +} diff --git a/staker/filetests/z_pool_add_to_tier2_and_removed_internal_filetest.gnoA b/staker/filetests/z_pool_add_to_tier2_and_removed_internal_filetest.gnoA new file mode 100644 index 000000000..79127bfbb --- /dev/null +++ b/staker/filetests/z_pool_add_to_tier2_and_removed_internal_filetest.gnoA @@ -0,0 +1,232 @@ +// PKGPATH: gno.land/r/gnoswap/v1/staker_test + +// POOLs: +// 1. gnot:gns:3000 +// 2. bar:baz:100 + +// POSITIONs: +// 1. in-range ( gnot:gns:3000 ) +// 2. in-range ( bar:baz:100 ) + +// REWARDs: +// - internal tier 1 ( gnot:gns:3000 ) +// - internal tier 2 ( bar:baz:100 ) -> will be removed from internal tiers + +package staker_test + +import ( + "std" + "strconv" + + "gno.land/p/demo/grc/grc721" + + "gno.land/r/gnoswap/v1/common" + "gno.land/r/gnoswap/v1/consts" + + "gno.land/r/demo/wugnot" + "gno.land/r/gnoswap/v1/gns" + "gno.land/r/onbloc/bar" + "gno.land/r/onbloc/baz" + + "gno.land/r/gnoswap/v1/gnft" + + pl "gno.land/r/gnoswap/v1/pool" + pn "gno.land/r/gnoswap/v1/position" + sr "gno.land/r/gnoswap/v1/staker" +) + +var ( + adminAddr = consts.ADMIN + adminUser = common.AddrToUser(adminAddr) + adminRealm = std.NewUserRealm(adminAddr) + + stakerAddr = consts.STAKER_ADDR + stakerUser = common.AddrToUser(stakerAddr) + stakerRealm = std.NewCodeRealm(consts.STAKER_PATH) + + wugnotAddr = consts.WUGNOT_ADDR + + fooPath = "gno.land/r/onbloc/foo" + barPath = "gno.land/r/onbloc/bar" + bazPath = "gno.land/r/onbloc/baz" + quxPath = "gno.land/r/onbloc/qux" + oblPath = "gno.land/r/onbloc/obl" + + gnsPath = "gno.land/r/gnoswap/v1/gns" + wugnotPath = "gno.land/r/demo/wugnot" + + fee100 uint32 = 100 + fee500 uint32 = 500 + fee3000 uint32 = 3000 + + max_timeout int64 = 9999999999 + + // external incentive deposit fee + depositGnsAmount uint64 = 1_000_000_000 // 1_000 GNS + + TIMESTAMP_90DAYS int64 = 90 * 24 * 60 * 60 + TIMESTAMP_180DAYS int64 = 180 * 24 * 60 * 60 + TIMESTAMP_365DAYS int64 = 365 * 24 * 60 * 60 +) + +func main() { + testInit() + testCreatePool() + + testMintAndStakeWugnotGnsPos01() + + testSetPoolTier2() // new pool is set to tier 2 + testMintAndStakeBarBazPos02() + + testRemovePoolTier() +} + +func testInit() { + std.TestSetRealm(adminRealm) + + // short warm-up period + sr.SetWarmUp(100, 901) + sr.SetWarmUp(70, 301) + sr.SetWarmUp(50, 151) + sr.SetWarmUp(30, 1) + + // prepare wugnot + std.TestIssueCoins(adminAddr, std.Coins{{"ugnot", 100_000_000_000_000}}) + banker := std.GetBanker(std.BankerTypeRealmSend) + banker.SendCoins(adminAddr, wugnotAddr, std.Coins{{"ugnot", 50_000_000_000_000}}) + std.TestSetOrigSend(std.Coins{{"ugnot", 50_000_000_000_000}}, nil) + wugnot.Deposit() + std.TestSetOrigSend(nil, nil) +} + +func testCreatePool() { + std.TestSetRealm(adminRealm) + + pl.SetPoolCreationFeeByAdmin(0) + + std.TestSkipHeights(1) + pl.CreatePool( + wugnotPath, + gnsPath, + fee3000, + common.TickMathGetSqrtRatioAtTick(0).ToString(), // 79228162514264337593543950337 + ) + pl.CreatePool( + barPath, + bazPath, + fee100, + common.TickMathGetSqrtRatioAtTick(0).ToString(), // 79228162514264337593543950337 + ) +} + +func testMintAndStakeWugnotGnsPos01() { + std.TestSetRealm(adminRealm) + + wugnot.Approve(common.AddrToUser(consts.POOL_ADDR), consts.UINT64_MAX) + gns.Approve(common.AddrToUser(consts.POOL_ADDR), consts.UINT64_MAX) + + std.TestSkipHeights(1) + pn.Mint( + wugnotPath, + gnsPath, + fee3000, + int32(-60), + int32(60), + "100", + "100", + "0", + "0", + max_timeout, + adminAddr, + adminAddr, + ) + + gnft.Approve(stakerAddr, tokenIdFrom(1)) + sr.StakeToken(1) + + /* + std.TestSkipHeights(1) + TODO: (after fixing unit test) check reward + */ +} + +func testSetPoolTier2() { + std.TestSetRealm(adminRealm) + + std.TestSkipHeights(1) + sr.SetPoolTierByAdmin("gno.land/r/onbloc/bar:gno.land/r/onbloc/baz:100", 2) +} + +func testMintAndStakeBarBazPos02() { + std.TestSetRealm(adminRealm) + + bar.Approve(common.AddrToUser(consts.POOL_ADDR), consts.UINT64_MAX) + baz.Approve(common.AddrToUser(consts.POOL_ADDR), consts.UINT64_MAX) + + std.TestSkipHeights(1) + pn.Mint( + barPath, + bazPath, + fee100, + int32(-50), + int32(50), + "100", + "100", + "0", + "0", + max_timeout, + adminAddr, + adminAddr, + ) + + gnft.Approve(stakerAddr, tokenIdFrom(2)) + sr.StakeToken(2) + + /* + std.TestSkipHeights(1) + + TODO: (after fixing unit test) check reward + - position-01 pool(gnot:gns:3000) is tier 1 + - position-02 pool(bar:baz:100) is tier 2 + - staker's emission reward should be distributed to each tier by ratio of 70% and 30% (70% for tier 1, 30% for tier 2) + */ +} + +func testRemovePoolTier() { + std.TestSetRealm(adminRealm) + + std.TestSkipHeights(1) + sr.RemovePoolTierByAdmin("gno.land/r/onbloc/bar:gno.land/r/onbloc/baz:100") + + /* + std.TestSkipHeights(1) + + TODO: (after fixing unit test) check reward + - position-01 pool(gnot:gns:3000) is tier 1 + - position-02 pool(bar:baz:100) is removed from internal tiers + + !! IMPORTANT: + user hasn' collected reward for position 2 yet + which means position 02's pool may not be in internal tiers + but user still can collect left reward for position 2 + */ +} + +func tokenIdFrom(tokenId interface{}) grc721.TokenID { + if tokenId == nil { + panic("tokenId is nil") + } + + switch tokenId.(type) { + case string: + return grc721.TokenID(tokenId.(string)) + case int: + return grc721.TokenID(strconv.Itoa(tokenId.(int))) + case uint64: + return grc721.TokenID(strconv.Itoa(int(tokenId.(uint64)))) + case grc721.TokenID: + return tokenId.(grc721.TokenID) + default: + panic("unsupported tokenId type") + } +} diff --git a/staker/filetests/z_position_inrange_change_by_swap_external_filetest.gnoA b/staker/filetests/z_position_inrange_change_by_swap_external_filetest.gnoA new file mode 100644 index 000000000..062c4e8fe --- /dev/null +++ b/staker/filetests/z_position_inrange_change_by_swap_external_filetest.gnoA @@ -0,0 +1,308 @@ +// PKGPATH: gno.land/r/gnoswap/v1/staker_test + +// POOLs: +// 1. bar:qux:100 + +// POSITIONs: +// 1. in-range -> out-range -> in-range +// 2. (always) in-range + +// REWARDs: +// - external bar ( bar:qux:100 ) + +package staker_test + +import ( + "std" + "strconv" + "time" + + "gno.land/p/demo/grc/grc721" + + "gno.land/r/gnoswap/v1/common" + "gno.land/r/gnoswap/v1/consts" + + _ "gno.land/r/demo/wugnot" + "gno.land/r/gnoswap/v1/gns" + "gno.land/r/onbloc/bar" + "gno.land/r/onbloc/qux" + + "gno.land/r/gnoswap/v1/gnft" + + en "gno.land/r/gnoswap/v1/emission" + pl "gno.land/r/gnoswap/v1/pool" + pn "gno.land/r/gnoswap/v1/position" + rr "gno.land/r/gnoswap/v1/router" + sr "gno.land/r/gnoswap/v1/staker" +) + +var ( + adminAddr = consts.ADMIN + adminUser = common.AddrToUser(adminAddr) + adminRealm = std.NewUserRealm(adminAddr) + + stakerAddr = consts.STAKER_ADDR + stakerUser = common.AddrToUser(stakerAddr) + stakerRealm = std.NewCodeRealm(consts.STAKER_PATH) + + fooPath = "gno.land/r/onbloc/foo" + barPath = "gno.land/r/onbloc/bar" + bazPath = "gno.land/r/onbloc/baz" + quxPath = "gno.land/r/onbloc/qux" + oblPath = "gno.land/r/onbloc/obl" + + gnsPath = "gno.land/r/gnoswap/v1/gns" + wugnotPath = "gno.land/r/demo/wugnot" + + fee100 uint32 = 100 + fee500 uint32 = 500 + fee3000 uint32 = 3000 + + max_timeout int64 = 9999999999 + + // external incentive deposit fee + depositGnsAmount uint64 = 1_000_000_000 // 1_000 GNS + + TIMESTAMP_90DAYS int64 = 90 * 24 * 60 * 60 + TIMESTAMP_180DAYS int64 = 180 * 24 * 60 * 60 + TIMESTAMP_365DAYS int64 = 365 * 24 * 60 * 60 + + poolPath = "gno.land/r/onbloc/bar:gno.land/r/onbloc/qux:100" +) + +func main() { + testInit() + testCreatePool() + testMintBarQuxPos01() + testMintBarQuxPos02() + testCreateExternalIncentive() + testStakeTokenPos01AndPos02() + testRewardCheckBeforeActive() + testMakeExternalBarStart() + + testCheckReward01() // both positions are in-range + + testMakePosition1OutRangeBySwap() + testCheckReward02() // position-01 is out-range + + testMakePosition1InRangeBySwap() // position-01 is in-range again + testCheckReward03() // both positions are in-range +} + +func testInit() { + std.TestSetRealm(adminRealm) + + // short warm-up period + sr.SetWarmUp(100, 901) + sr.SetWarmUp(70, 301) + sr.SetWarmUp(50, 151) + sr.SetWarmUp(30, 1) +} + +func testCreatePool() { + std.TestSetRealm(adminRealm) + + pl.SetPoolCreationFeeByAdmin(0) + + std.TestSkipHeights(1) + pl.CreatePool( + barPath, + quxPath, + fee100, + common.TickMathGetSqrtRatioAtTick(0).ToString(), // 79228162514264337593543950337 + ) +} + +func testMintBarQuxPos01() { + std.TestSetRealm(adminRealm) + + bar.Approve(common.AddrToUser(consts.POOL_ADDR), consts.UINT64_MAX) + qux.Approve(common.AddrToUser(consts.POOL_ADDR), consts.UINT64_MAX) + + std.TestSkipHeights(1) + pn.Mint( + barPath, + quxPath, + fee100, + int32(-50), + int32(50), + "50", + "50", + "1", + "1", + max_timeout, + adminAddr, + adminAddr, + ) +} + +func testMintBarQuxPos02() { + std.TestSetRealm(adminRealm) + + bar.Approve(common.AddrToUser(consts.POOL_ADDR), consts.UINT64_MAX) + qux.Approve(common.AddrToUser(consts.POOL_ADDR), consts.UINT64_MAX) + + std.TestSkipHeights(1) + pn.Mint( + barPath, + quxPath, + fee100, + int32(-1000), + int32(1000), + "500000", + "500000", + "1", + "1", + max_timeout, + adminAddr, + adminAddr, + ) +} + +func testCreateExternalIncentive() { + std.TestSetRealm(adminRealm) + + bar.Approve(common.AddrToUser(consts.STAKER_ADDR), consts.UINT64_MAX) + gns.Approve(common.AddrToUser(consts.STAKER_ADDR), depositGnsAmount) + + std.TestSkipHeights(1) + sr.CreateExternalIncentive( + "gno.land/r/onbloc/bar:gno.land/r/onbloc/qux:100", + barPath, + "9000000000", + 1234569600, + 1234569600+TIMESTAMP_90DAYS, + ) +} + +func testStakeTokenPos01AndPos02() { + std.TestSetRealm(adminRealm) + + gnft.Approve(stakerAddr, tokenIdFrom(1)) + gnft.Approve(stakerAddr, tokenIdFrom(2)) + + std.TestSkipHeights(1) + sr.StakeToken(1) + sr.StakeToken(2) +} + +func testRewardCheckBeforeActive() { + std.TestSkipHeights(1) + en.MintAndDistributeGns() + sr.CalcPoolPositionRefactor() + + /* + TODO: (after fixing unit test) check reward + - external bar is not active yet + - should be no reward for both positions + */ +} + +func testMakeExternalBarStart() { + externalStartTime := 1234569600 + nowTime := time.Now().Unix() + timeLeft := externalStartTime - nowTime + + blockAvgTime := consts.BLOCK_GENERATION_INTERVAL + blockLeft := timeLeft / blockAvgTime + + std.TestSkipHeights(int64(blockLeft)) // skip until external bar starts + std.TestSkipHeights(10) // skip bit more to see reward calculation + +} + +func testCheckReward01() { + std.TestSkipHeights(1) + /* + TODO: (after fixing unit test) check reward + - both positions are in-range + */ +} + +func testMakePosition1OutRangeBySwap() { + std.TestSetRealm(adminRealm) + + poolTick := pl.PoolGetSlot0Tick(poolPath) + + bar.Approve(common.AddrToUser(consts.POOL_ADDR), consts.UINT64_MAX) + qux.Approve(common.AddrToUser(consts.POOL_ADDR), consts.UINT64_MAX) + + bar.Approve(common.AddrToUser(consts.ROUTER_ADDR), consts.UINT64_MAX) + qux.Approve(common.AddrToUser(consts.ROUTER_ADDR), consts.UINT64_MAX) + + tokenIn, tokenOut := rr.ExactInSwapRoute( + barPath, // inputToken + quxPath, // outputToken + "100000", // finalAmountIn + poolPath, // RouteArr + "100", // quoteArr + "0", // amountOutMin + ) + + newPoolTick := pl.PoolGetSlot0Tick(poolPath) + println("oldPoolTick", poolTick) + println("newPoolTick", newPoolTick) + println() +} + +func testCheckReward02() { + std.TestSkipHeights(1) + /* + TODO: (after fixing unit test) check reward + - position-01 is out-range + - only position-02 reward should be increased + */ +} + +func testMakePosition1InRangeBySwap() { + std.TestSetRealm(adminRealm) + + poolTick := pl.PoolGetSlot0Tick(poolPath) + + bar.Approve(common.AddrToUser(consts.POOL_ADDR), consts.UINT64_MAX) + qux.Approve(common.AddrToUser(consts.POOL_ADDR), consts.UINT64_MAX) + + bar.Approve(common.AddrToUser(consts.ROUTER_ADDR), consts.UINT64_MAX) + qux.Approve(common.AddrToUser(consts.ROUTER_ADDR), consts.UINT64_MAX) + + tokenIn, tokenOut := rr.ExactInSwapRoute( + quxPath, + barPath, + "100000", + "gno.land/r/onbloc/qux:gno.land/r/onbloc/bar:100", + "100", + "0", + ) + + newPoolTick := pl.PoolGetSlot0Tick(poolPath) + println("oldPoolTick", poolTick) + println("newPoolTick", newPoolTick) + println() +} + +func testCheckReward03() { + std.TestSkipHeights(1) + /* + TODO: (after fixing unit test) check reward + - both positions are in-range + */ +} + +func tokenIdFrom(tokenId interface{}) grc721.TokenID { + if tokenId == nil { + panic("tokenId is nil") + } + + switch tokenId.(type) { + case string: + return grc721.TokenID(tokenId.(string)) + case int: + return grc721.TokenID(strconv.Itoa(tokenId.(int))) + case uint64: + return grc721.TokenID(strconv.Itoa(int(tokenId.(uint64)))) + case grc721.TokenID: + return tokenId.(grc721.TokenID) + default: + panic("unsupported tokenId type") + } +} diff --git a/staker/filetests/z_position_inrange_change_by_swap_internal_filetest.gnoA b/staker/filetests/z_position_inrange_change_by_swap_internal_filetest.gnoA new file mode 100644 index 000000000..3de07c8ac --- /dev/null +++ b/staker/filetests/z_position_inrange_change_by_swap_internal_filetest.gnoA @@ -0,0 +1,271 @@ +// PKGPATH: gno.land/r/gnoswap/v1/staker_test + +// POOLs: +// 1. gnot:gns:3000 + +// POSITIONs: +// 1. in-range -> out-range -> in-range +// 2. (always) in-range + +// REWARDs: +// - internal tier 1 ( gnot:gns:3000 ) + +package staker_test + +import ( + "std" + "strconv" + + "gno.land/p/demo/grc/grc721" + + "gno.land/r/gnoswap/v1/common" + "gno.land/r/gnoswap/v1/consts" + + "gno.land/r/demo/wugnot" + "gno.land/r/gnoswap/v1/gns" + + "gno.land/r/gnoswap/v1/gnft" + + pl "gno.land/r/gnoswap/v1/pool" + pn "gno.land/r/gnoswap/v1/position" + rr "gno.land/r/gnoswap/v1/router" + sr "gno.land/r/gnoswap/v1/staker" +) + +var ( + adminAddr = consts.ADMIN + adminUser = common.AddrToUser(adminAddr) + adminRealm = std.NewUserRealm(adminAddr) + + stakerAddr = consts.STAKER_ADDR + stakerUser = common.AddrToUser(stakerAddr) + stakerRealm = std.NewCodeRealm(consts.STAKER_PATH) + + wugnotAddr = consts.WUGNOT_ADDR + + fooPath = "gno.land/r/onbloc/foo" + barPath = "gno.land/r/onbloc/bar" + bazPath = "gno.land/r/onbloc/baz" + quxPath = "gno.land/r/onbloc/qux" + oblPath = "gno.land/r/onbloc/obl" + + gnsPath = "gno.land/r/gnoswap/v1/gns" + wugnotPath = "gno.land/r/demo/wugnot" + + fee100 uint32 = 100 + fee500 uint32 = 500 + fee3000 uint32 = 3000 + + max_timeout int64 = 9999999999 + + // external incentive deposit fee + depositGnsAmount uint64 = 1_000_000_000 // 1_000 GNS + + TIMESTAMP_90DAYS int64 = 90 * 24 * 60 * 60 + TIMESTAMP_180DAYS int64 = 180 * 24 * 60 * 60 + TIMESTAMP_365DAYS int64 = 365 * 24 * 60 * 60 + + poolPath = "gno.land/r/demo/wugnot:gno.land/r/gnoswap/v1/gns:3000" +) + +func main() { + testInit() + testCreatePool() + testMintWugnotGnsPos01() + testMintWugnotGnsPos02() + + testStakeTokenPos01AndPos02() + + testCheckReward01() // both positions are in-range + + testMakePosition1OutRangeBySwap() + testCheckReward02() // position-01 is out-range + + testMakePosition1InRangeBySwap() // position-01 is in-range again + testCheckReward03() // both positions are in-range +} + +func testInit() { + std.TestSetRealm(adminRealm) + + // short warm-up period + sr.SetWarmUp(100, 901) + sr.SetWarmUp(70, 301) + sr.SetWarmUp(50, 151) + sr.SetWarmUp(30, 1) + + // prepare wugnot + std.TestIssueCoins(adminAddr, std.Coins{{"ugnot", 100_000_000_000_000}}) + banker := std.GetBanker(std.BankerTypeRealmSend) + banker.SendCoins(adminAddr, wugnotAddr, std.Coins{{"ugnot", 50_000_000_000_000}}) + std.TestSetOrigSend(std.Coins{{"ugnot", 50_000_000_000_000}}, nil) + wugnot.Deposit() + std.TestSetOrigSend(nil, nil) +} + +func testCreatePool() { + std.TestSetRealm(adminRealm) + + pl.SetPoolCreationFeeByAdmin(0) + + std.TestSkipHeights(1) + pl.CreatePool( + wugnotPath, + gnsPath, + fee3000, + common.TickMathGetSqrtRatioAtTick(0).ToString(), // 79228162514264337593543950337 + ) +} + +func testMintWugnotGnsPos01() { + std.TestSetRealm(adminRealm) + + wugnot.Approve(common.AddrToUser(consts.POOL_ADDR), consts.UINT64_MAX) + gns.Approve(common.AddrToUser(consts.POOL_ADDR), consts.UINT64_MAX) + + std.TestSkipHeights(1) + pn.Mint( + wugnotPath, + gnsPath, + fee3000, + int32(-60), + int32(60), + "50", + "50", + "1", + "1", + max_timeout, + adminAddr, + adminAddr, + ) +} + +func testMintWugnotGnsPos02() { + std.TestSetRealm(adminRealm) + + wugnot.Approve(common.AddrToUser(consts.POOL_ADDR), consts.UINT64_MAX) + gns.Approve(common.AddrToUser(consts.POOL_ADDR), consts.UINT64_MAX) + + std.TestSkipHeights(1) + pn.Mint( + wugnotPath, + gnsPath, + fee3000, + int32(-1020), + int32(1020), + "500000", + "500000", + "1", + "1", + max_timeout, + adminAddr, + adminAddr, + ) +} + +func testStakeTokenPos01AndPos02() { + std.TestSetRealm(adminRealm) + + gnft.Approve(stakerAddr, tokenIdFrom(1)) + gnft.Approve(stakerAddr, tokenIdFrom(2)) + + std.TestSkipHeights(1) + sr.StakeToken(1) + sr.StakeToken(2) +} + +func testCheckReward01() { + std.TestSkipHeights(1) + /* + TODO: (after fixing unit test) check reward + - both positions are in-range + */ +} + +func testMakePosition1OutRangeBySwap() { + std.TestSetRealm(adminRealm) + + poolTick := pl.PoolGetSlot0Tick(poolPath) + + wugnot.Approve(common.AddrToUser(consts.POOL_ADDR), consts.UINT64_MAX) + gns.Approve(common.AddrToUser(consts.POOL_ADDR), consts.UINT64_MAX) + + wugnot.Approve(common.AddrToUser(consts.ROUTER_ADDR), consts.UINT64_MAX) + gns.Approve(common.AddrToUser(consts.ROUTER_ADDR), consts.UINT64_MAX) + + tokenIn, tokenOut := rr.ExactInSwapRoute( + wugnotPath, // inputToken + gnsPath, // outputToken + "100000", // finalAmountIn + poolPath, // RouteArr + "100", // quoteArr + "0", // amountOutMin + ) + + newPoolTick := pl.PoolGetSlot0Tick(poolPath) + println("oldPoolTick", poolTick) + println("newPoolTick", newPoolTick) + println() +} + +func testCheckReward02() { + std.TestSkipHeights(1) + /* + TODO: (after fixing unit test) check reward + - position-01 is out-range + - only position-02 reward should be increased + */ +} + +func testMakePosition1InRangeBySwap() { + std.TestSetRealm(adminRealm) + + poolTick := pl.PoolGetSlot0Tick(poolPath) + + wugnot.Approve(common.AddrToUser(consts.POOL_ADDR), consts.UINT64_MAX) + gns.Approve(common.AddrToUser(consts.POOL_ADDR), consts.UINT64_MAX) + + wugnot.Approve(common.AddrToUser(consts.ROUTER_ADDR), consts.UINT64_MAX) + gns.Approve(common.AddrToUser(consts.ROUTER_ADDR), consts.UINT64_MAX) + + tokenIn, tokenOut := rr.ExactInSwapRoute( + gnsPath, + wugnotPath, + "100000", + "gno.land/r/gnoswap/v1/gns:gno.land/r/demo/wugnot:3000", + "100", + "0", + ) + + newPoolTick := pl.PoolGetSlot0Tick(poolPath) + println("oldPoolTick", poolTick) + println("newPoolTick", newPoolTick) + println() +} + +func testCheckReward03() { + std.TestSkipHeights(1) + /* + TODO: (after fixing unit test) check reward + - both positions are in-range + */ +} + +func tokenIdFrom(tokenId interface{}) grc721.TokenID { + if tokenId == nil { + panic("tokenId is nil") + } + + switch tokenId.(type) { + case string: + return grc721.TokenID(tokenId.(string)) + case int: + return grc721.TokenID(strconv.Itoa(tokenId.(int))) + case uint64: + return grc721.TokenID(strconv.Itoa(int(tokenId.(uint64)))) + case grc721.TokenID: + return tokenId.(grc721.TokenID) + default: + panic("unsupported tokenId type") + } +} diff --git a/staker/filetests/z_reward_for_user_collect_change_by_collecting_reward_external_filetest.gnoA b/staker/filetests/z_reward_for_user_collect_change_by_collecting_reward_external_filetest.gnoA new file mode 100644 index 000000000..845444c87 --- /dev/null +++ b/staker/filetests/z_reward_for_user_collect_change_by_collecting_reward_external_filetest.gnoA @@ -0,0 +1,256 @@ +// PKGPATH: gno.land/r/gnoswap/v1/staker_test + +// POOLs: +// 1. gnot:gns:3000 + +// POSITIONs: +// 1. in-range +// 2. in-range + +// REWARDs: +// - external bar ( bar:qux:100 ) +// - external qux ( qux:gns:100 ) + +package staker_test + +import ( + "std" + "strconv" + "time" + + "gno.land/p/demo/grc/grc721" + + "gno.land/r/gnoswap/v1/common" + "gno.land/r/gnoswap/v1/consts" + + "gno.land/r/demo/wugnot" + "gno.land/r/gnoswap/v1/gns" + + "gno.land/r/onbloc/bar" + "gno.land/r/onbloc/qux" + + "gno.land/r/gnoswap/v1/gnft" + + pl "gno.land/r/gnoswap/v1/pool" + pn "gno.land/r/gnoswap/v1/position" + sr "gno.land/r/gnoswap/v1/staker" +) + +var ( + adminAddr = consts.ADMIN + adminUser = common.AddrToUser(adminAddr) + adminRealm = std.NewUserRealm(adminAddr) + + stakerAddr = consts.STAKER_ADDR + stakerUser = common.AddrToUser(stakerAddr) + stakerRealm = std.NewCodeRealm(consts.STAKER_PATH) + + wugnotAddr = consts.WUGNOT_ADDR + + fooPath = "gno.land/r/onbloc/foo" + barPath = "gno.land/r/onbloc/bar" + bazPath = "gno.land/r/onbloc/baz" + quxPath = "gno.land/r/onbloc/qux" + oblPath = "gno.land/r/onbloc/obl" + + gnsPath = "gno.land/r/gnoswap/v1/gns" + wugnotPath = "gno.land/r/demo/wugnot" + + fee100 uint32 = 100 + fee500 uint32 = 500 + fee3000 uint32 = 3000 + + max_timeout int64 = 9999999999 + + // external incentive deposit fee + depositGnsAmount uint64 = 1_000_000_000 // 1_000 GNS + + TIMESTAMP_90DAYS int64 = 90 * 24 * 60 * 60 + TIMESTAMP_180DAYS int64 = 180 * 24 * 60 * 60 + TIMESTAMP_365DAYS int64 = 365 * 24 * 60 * 60 +) + +func main() { + testInit() + testCreatePool() + + testCreateExternalIncentiveBar() + testCreateExternalIncentiveQux() + + testMintAndStakeBarQuxPos01() + testMintAndStakeBarQuxPos02() + + testMakeExternalBarAndQuxStart() + + testCollectRewardPos01() +} + +func testInit() { + std.TestSetRealm(adminRealm) + + // short warm-up period + sr.SetWarmUp(100, 901) + sr.SetWarmUp(70, 301) + sr.SetWarmUp(50, 151) + sr.SetWarmUp(30, 1) + + // prepare wugnot + std.TestIssueCoins(adminAddr, std.Coins{{"ugnot", 100_000_000_000_000}}) + banker := std.GetBanker(std.BankerTypeRealmSend) + banker.SendCoins(adminAddr, wugnotAddr, std.Coins{{"ugnot", 50_000_000_000_000}}) + std.TestSetOrigSend(std.Coins{{"ugnot", 50_000_000_000_000}}, nil) + wugnot.Deposit() + std.TestSetOrigSend(nil, nil) +} + +func testCreatePool() { + std.TestSetRealm(adminRealm) + + pl.SetPoolCreationFeeByAdmin(0) + + std.TestSkipHeights(1) + pl.CreatePool( + barPath, + quxPath, + fee100, + common.TickMathGetSqrtRatioAtTick(0).ToString(), // 79228162514264337593543950337 + ) +} + +func testCreateExternalIncentiveBar() { + std.TestSetRealm(adminRealm) + + bar.Approve(common.AddrToUser(consts.STAKER_ADDR), consts.UINT64_MAX) + gns.Approve(common.AddrToUser(consts.STAKER_ADDR), depositGnsAmount) + + std.TestSkipHeights(1) + sr.CreateExternalIncentive( + "gno.land/r/onbloc/bar:gno.land/r/onbloc/qux:100", + barPath, + "9000000000", + 1234569600, + 1234569600+TIMESTAMP_90DAYS, + ) +} + +func testCreateExternalIncentiveQux() { + std.TestSetRealm(adminRealm) + + qux.Approve(common.AddrToUser(consts.STAKER_ADDR), consts.UINT64_MAX) + gns.Approve(common.AddrToUser(consts.STAKER_ADDR), depositGnsAmount) + + std.TestSkipHeights(1) + sr.CreateExternalIncentive( + "gno.land/r/onbloc/bar:gno.land/r/onbloc/qux:100", + quxPath, + "18000000000", + 1234569600, + 1234569600+TIMESTAMP_90DAYS, + ) +} + +func testMintAndStakeBarQuxPos01() { + std.TestSetRealm(adminRealm) + + bar.Approve(common.AddrToUser(consts.POOL_ADDR), consts.UINT64_MAX) + qux.Approve(common.AddrToUser(consts.POOL_ADDR), consts.UINT64_MAX) + + std.TestSkipHeights(1) + pn.Mint( + barPath, + quxPath, + fee100, + int32(-50), + int32(50), + "100", + "100", + "0", + "0", + max_timeout, + adminAddr, + adminAddr, + ) + + gnft.Approve(stakerAddr, tokenIdFrom(1)) + sr.StakeToken(1) +} + +func testMintAndStakeBarQuxPos02() { + std.TestSetRealm(adminRealm) + + bar.Approve(common.AddrToUser(consts.POOL_ADDR), consts.UINT64_MAX) + qux.Approve(common.AddrToUser(consts.POOL_ADDR), consts.UINT64_MAX) + + std.TestSkipHeights(1) + pn.Mint( + barPath, + quxPath, + fee100, + int32(-50), + int32(50), + "100", + "100", + "0", + "0", + max_timeout, + adminAddr, + adminAddr, + ) + + gnft.Approve(stakerAddr, tokenIdFrom(2)) + sr.StakeToken(2) +} + +func testMakeExternalBarAndQuxStart() { + externalStartTime := 1234569600 + nowTime := time.Now().Unix() + timeLeft := externalStartTime - nowTime + + blockAvgTime := consts.BLOCK_GENERATION_INTERVAL + blockLeft := timeLeft / blockAvgTime + + std.TestSkipHeights(int64(blockLeft)) // skip until external bar starts + std.TestSkipHeights(10) // skip bit more to see reward calculation + + /* + std.TestSkipHeights(1) + + TODO: (after fixing unit test) check reward + - position-01 and position-02 should have the same reward for bar and qux + */ +} + +func testCollectRewardPos01() { + std.TestSetRealm(adminRealm) + + std.TestSkipHeights(1) + sr.CollectReward(1, false) + + /* + std.TestSkipHeights(1) + + TODO: (after fixing unit test) check reward + - only position-01 reward collected (bar and qux) + - reward for position-01 should be reset to 0 and increased + - reward for position-02 should be increased + */ +} + +func tokenIdFrom(tokenId interface{}) grc721.TokenID { + if tokenId == nil { + panic("tokenId is nil") + } + + switch tokenId.(type) { + case string: + return grc721.TokenID(tokenId.(string)) + case int: + return grc721.TokenID(strconv.Itoa(tokenId.(int))) + case uint64: + return grc721.TokenID(strconv.Itoa(int(tokenId.(uint64)))) + case grc721.TokenID: + return tokenId.(grc721.TokenID) + default: + panic("unsupported tokenId type") + } +} diff --git a/staker/filetests/z_reward_for_user_collect_change_by_collecting_reward_internal_filetest.gnoA b/staker/filetests/z_reward_for_user_collect_change_by_collecting_reward_internal_filetest.gnoA new file mode 100644 index 000000000..aa1b9ed27 --- /dev/null +++ b/staker/filetests/z_reward_for_user_collect_change_by_collecting_reward_internal_filetest.gnoA @@ -0,0 +1,207 @@ +// PKGPATH: gno.land/r/gnoswap/v1/staker_test + +// POOLs: +// 1. gnot:gns:3000 + +// POSITIONs: +// 1. in-range +// 2. in-range (will be unstaked) + +// REWARDs: +// - internal tier 1 ( gnot:gns:3000 ) + +package staker_test + +import ( + "std" + "strconv" + + "gno.land/p/demo/grc/grc721" + + "gno.land/r/gnoswap/v1/common" + "gno.land/r/gnoswap/v1/consts" + + "gno.land/r/demo/wugnot" + "gno.land/r/gnoswap/v1/gns" + + "gno.land/r/gnoswap/v1/gnft" + + pl "gno.land/r/gnoswap/v1/pool" + pn "gno.land/r/gnoswap/v1/position" + sr "gno.land/r/gnoswap/v1/staker" +) + +var ( + adminAddr = consts.ADMIN + adminUser = common.AddrToUser(adminAddr) + adminRealm = std.NewUserRealm(adminAddr) + + stakerAddr = consts.STAKER_ADDR + stakerUser = common.AddrToUser(stakerAddr) + stakerRealm = std.NewCodeRealm(consts.STAKER_PATH) + + wugnotAddr = consts.WUGNOT_ADDR + + fooPath = "gno.land/r/onbloc/foo" + barPath = "gno.land/r/onbloc/bar" + bazPath = "gno.land/r/onbloc/baz" + quxPath = "gno.land/r/onbloc/qux" + oblPath = "gno.land/r/onbloc/obl" + + gnsPath = "gno.land/r/gnoswap/v1/gns" + wugnotPath = "gno.land/r/demo/wugnot" + + fee100 uint32 = 100 + fee500 uint32 = 500 + fee3000 uint32 = 3000 + + max_timeout int64 = 9999999999 + + // external incentive deposit fee + depositGnsAmount uint64 = 1_000_000_000 // 1_000 GNS + + TIMESTAMP_90DAYS int64 = 90 * 24 * 60 * 60 + TIMESTAMP_180DAYS int64 = 180 * 24 * 60 * 60 + TIMESTAMP_365DAYS int64 = 365 * 24 * 60 * 60 +) + +func main() { + testInit() + testCreatePool() + + testMintAndStakeWugnotGnsPos01() // position-01 is in-range + testMintAndStakeWugnotGnsPos02() // position-02 is in-range + + testCollectRewardPos01() +} + +func testInit() { + std.TestSetRealm(adminRealm) + + // short warm-up period + sr.SetWarmUp(100, 901) + sr.SetWarmUp(70, 301) + sr.SetWarmUp(50, 151) + sr.SetWarmUp(30, 1) + + // prepare wugnot + std.TestIssueCoins(adminAddr, std.Coins{{"ugnot", 100_000_000_000_000}}) + banker := std.GetBanker(std.BankerTypeRealmSend) + banker.SendCoins(adminAddr, wugnotAddr, std.Coins{{"ugnot", 50_000_000_000_000}}) + std.TestSetOrigSend(std.Coins{{"ugnot", 50_000_000_000_000}}, nil) + wugnot.Deposit() + std.TestSetOrigSend(nil, nil) +} + +func testCreatePool() { + std.TestSetRealm(adminRealm) + + pl.SetPoolCreationFeeByAdmin(0) + + std.TestSkipHeights(1) + pl.CreatePool( + wugnotPath, + gnsPath, + fee3000, + common.TickMathGetSqrtRatioAtTick(0).ToString(), // 79228162514264337593543950337 + ) +} + +func testMintAndStakeWugnotGnsPos01() { + std.TestSetRealm(adminRealm) + + wugnot.Approve(common.AddrToUser(consts.POOL_ADDR), consts.UINT64_MAX) + gns.Approve(common.AddrToUser(consts.POOL_ADDR), consts.UINT64_MAX) + + std.TestSkipHeights(1) + pn.Mint( + wugnotPath, + gnsPath, + fee3000, + int32(-60), + int32(60), + "100", + "100", + "0", + "0", + max_timeout, + adminAddr, + adminAddr, + ) + + gnft.Approve(stakerAddr, tokenIdFrom(1)) + sr.StakeToken(1) + + /* + std.TestSkipHeights(1) + TODO: (after fixing unit test) check reward + */ +} + +func testMintAndStakeWugnotGnsPos02() { + std.TestSetRealm(adminRealm) + + wugnot.Approve(common.AddrToUser(consts.POOL_ADDR), consts.UINT64_MAX) + gns.Approve(common.AddrToUser(consts.POOL_ADDR), consts.UINT64_MAX) + + std.TestSkipHeights(1) + pn.Mint( + wugnotPath, + gnsPath, + fee3000, + int32(-60), + int32(60), + "100", + "100", + "0", + "0", + max_timeout, + adminAddr, + adminAddr, + ) + + gnft.Approve(stakerAddr, tokenIdFrom(2)) + sr.StakeToken(2) + + /* + std.TestSkipHeights(1) + + TODO: (after fixing unit test) check reward + - both positions are in-range + */ +} + +func testCollectRewardPos01() { + std.TestSetRealm(adminRealm) + + std.TestSkipHeights(1) + sr.CollectReward(1, false) + + /* + std.TestSkipHeights(1) + + TODO: (after fixing unit test) check reward + - only position-01 reward collected + - reward for position-01 should be reset to 0 and increased + - reward for position-02 should be increased + */ +} + +func tokenIdFrom(tokenId interface{}) grc721.TokenID { + if tokenId == nil { + panic("tokenId is nil") + } + + switch tokenId.(type) { + case string: + return grc721.TokenID(tokenId.(string)) + case int: + return grc721.TokenID(strconv.Itoa(tokenId.(int))) + case uint64: + return grc721.TokenID(strconv.Itoa(int(tokenId.(uint64)))) + case grc721.TokenID: + return tokenId.(grc721.TokenID) + default: + panic("unsupported tokenId type") + } +} diff --git a/staker/filetests/z_single_gns_external_ends_filetest.gnoA b/staker/filetests/z_single_gns_external_ends_filetest.gnoA new file mode 100644 index 000000000..289f3d6ff --- /dev/null +++ b/staker/filetests/z_single_gns_external_ends_filetest.gnoA @@ -0,0 +1,288 @@ +// PKGPATH: gno.land/r/gnoswap/v1/staker_test + +// POOLs: +// 1. bar:qux:100 + +// POSITIONs: +// 1. in-range + +// REWARDs: +// - external gns 90 days ( bar:qux:100 ) + +package staker_test + +import ( + "std" + "strconv" + "time" + + "gno.land/p/demo/grc/grc721" + "gno.land/p/demo/testutils" + + "gno.land/r/gnoswap/v1/common" + "gno.land/r/gnoswap/v1/consts" + + "gno.land/r/gnoswap/v1/gns" + "gno.land/r/onbloc/bar" + "gno.land/r/onbloc/qux" + + "gno.land/r/gnoswap/v1/gnft" + + pl "gno.land/r/gnoswap/v1/pool" + pn "gno.land/r/gnoswap/v1/position" + sr "gno.land/r/gnoswap/v1/staker" +) + +var ( + adminAddr = consts.ADMIN + adminUser = common.AddrToUser(adminAddr) + adminRealm = std.NewUserRealm(adminAddr) + + // g1v4u8getjdeskcsmjv4shgmmjta047h6lua7mup + externalCreatorAddr = testutils.TestAddress("externalCreator") + externalCreatorUser = common.AddrToUser(externalCreatorAddr) + externalCreatorRealm = std.NewUserRealm(externalCreatorAddr) + + stakerAddr = consts.STAKER_ADDR + stakerUser = common.AddrToUser(stakerAddr) + stakerRealm = std.NewCodeRealm(consts.STAKER_PATH) + + fooPath = "gno.land/r/onbloc/foo" + barPath = "gno.land/r/onbloc/bar" + bazPath = "gno.land/r/onbloc/baz" + quxPath = "gno.land/r/onbloc/qux" + oblPath = "gno.land/r/onbloc/obl" + + gnsPath = "gno.land/r/gnoswap/v1/gns" + wugnotPath = "gno.land/r/demo/wugnot" + + fee100 uint32 = 100 + fee500 uint32 = 500 + fee3000 uint32 = 3000 + + max_timeout int64 = 9999999999 + + // external incentive deposit fee + depositGnsAmount uint64 = 1_000_000_000 // 1_000 GNS + + TIMESTAMP_90DAYS int64 = 90 * 24 * 60 * 60 + TIMESTAMP_180DAYS int64 = 180 * 24 * 60 * 60 + TIMESTAMP_365DAYS int64 = 365 * 24 * 60 * 60 + + poolPath = "gno.land/r/onbloc/bar:gno.land/r/onbloc/qux:100" +) + +func main() { + testInit() + testCreatePool() + testMintBarQuxPos01() + testMintBarQuxPos02() + + testCreateExternalIncentiveGns() + testMakeExternalStart() + + testStakeTokenPos01() + testStakeTokenPos02() + + testCollectRewardPos01AndPos02() + + testEndExternalGns() + testCollectRewardPos01AndPos02AfterEnd() +} + +func testInit() { + std.TestSetRealm(adminRealm) + + // short warm-up period + sr.SetWarmUp(100, 901) + sr.SetWarmUp(70, 301) + sr.SetWarmUp(50, 151) + sr.SetWarmUp(30, 1) +} + +func testCreatePool() { + std.TestSetRealm(adminRealm) + + pl.SetPoolCreationFeeByAdmin(0) + + std.TestSkipHeights(1) + pl.CreatePool( + barPath, + quxPath, + fee100, + common.TickMathGetSqrtRatioAtTick(0).ToString(), // 79228162514264337593543950337 + ) +} + +func testMintBarQuxPos01() { + std.TestSetRealm(adminRealm) + + bar.Approve(common.AddrToUser(consts.POOL_ADDR), consts.UINT64_MAX) + qux.Approve(common.AddrToUser(consts.POOL_ADDR), consts.UINT64_MAX) + + std.TestSkipHeights(1) + pn.Mint( + barPath, + quxPath, + fee100, + int32(-50), + int32(50), + "50", + "50", + "1", + "1", + max_timeout, + adminAddr, + adminAddr, + ) +} + +func testMintBarQuxPos02() { + std.TestSetRealm(adminRealm) + + bar.Approve(common.AddrToUser(consts.POOL_ADDR), consts.UINT64_MAX) + qux.Approve(common.AddrToUser(consts.POOL_ADDR), consts.UINT64_MAX) + + std.TestSkipHeights(1) + pn.Mint( + barPath, + quxPath, + fee100, + int32(-50), + int32(50), + "50", + "50", + "1", + "1", + max_timeout, + adminAddr, + adminAddr, + ) +} + +func testCreateExternalIncentiveGns() { + std.TestSetRealm(adminRealm) + gns.Transfer(externalCreatorUser, 9000000000) + gns.Transfer(externalCreatorUser, depositGnsAmount) + + std.TestSetRealm(externalCreatorRealm) // creator + gns.Approve(common.AddrToUser(consts.STAKER_ADDR), consts.UINT64_MAX) + + std.TestSkipHeights(1) + sr.CreateExternalIncentive( + "gno.land/r/onbloc/bar:gno.land/r/onbloc/qux:100", + gnsPath, + "9000000000", + 1234569600, + 1234569600+TIMESTAMP_90DAYS, + ) +} + +func testMakeExternalStart() { + externalStartTime := int64(1234569600) + nowTime := time.Now().Unix() + timeLeft := externalStartTime - nowTime + + blockAvgTime := consts.BLOCK_GENERATION_INTERVAL + blockLeft := timeLeft / blockAvgTime + + std.TestSkipHeights(int64(blockLeft)) // skip until external bar starts + std.TestSkipHeights(10) // skip bit more to see reward calculation +} + +func testStakeTokenPos01() { + std.TestSetRealm(adminRealm) + + std.TestSkipHeights(1) + gnft.Approve(stakerAddr, tokenIdFrom(1)) + sr.StakeToken(1) +} + +func testStakeTokenPos02() { + std.TestSetRealm(adminRealm) + + std.TestSkipHeights(1) + gnft.Approve(stakerAddr, tokenIdFrom(2)) + sr.StakeToken(2) +} + +func testCollectRewardPos01AndPos02() { + std.TestSetRealm(adminRealm) + + std.TestSkipHeights(1) + sr.CollectReward(1) + sr.CollectReward(2) + + /* + std.TestSkipHeights(1) + + TODO: (after fixing unit test) check collected reward + */ +} + +func testEndExternalGns() { + externalEndTime := (1234569600 + TIMESTAMP_90DAYS) + nowTime := time.Now().Unix() + timeLeft := externalEndTime - nowTime + + blockAvgTime := consts.BLOCK_GENERATION_INTERVAL + blockLeft := timeLeft / blockAvgTime + + std.TestSkipHeights(int64(blockLeft)) // skip until external gns ends + std.TestSkipHeights(10) // skip bit more to see reward calculation + + std.TestSetRealm(externalCreatorRealm) + gnsBalanceBeforeEnds := gns.BalanceOf(externalCreatorUser) + sr.EndExternalIncentive( + externalCreatorAddr, + "gno.land/r/onbloc/bar:gno.land/r/onbloc/qux:100", + gnsPath, + 1234569600, + 1234569600+TIMESTAMP_90DAYS, + 126, + ) + gnsBalanceAfterEnds := gns.BalanceOf(externalCreatorUser) + + /* + std.TestSkipHeights(1) + + TODO: (after fixing unit test) check + - refunded reward amount for refunedee + - refunded gns amount for refundee (depositGnsAmount refund) + */ +} + +func testCollectRewardPos01AndPos02AfterEnd() { + std.TestSetRealm(adminRealm) + + std.TestSkipHeights(1) + sr.CollectReward(1) + sr.CollectReward(2) + + /* + std.TestSkipHeights(1) + + TODO: (after fixing unit test) check collected reward + - even external gns is ended, user hasn't collected reward yet + - there should be some reward collected + */ +} + +func tokenIdFrom(tokenId interface{}) grc721.TokenID { + if tokenId == nil { + panic("tokenId is nil") + } + + switch tokenId.(type) { + case string: + return grc721.TokenID(tokenId.(string)) + case int: + return grc721.TokenID(strconv.Itoa(tokenId.(int))) + case uint64: + return grc721.TokenID(strconv.Itoa(int(tokenId.(uint64)))) + case grc721.TokenID: + return tokenId.(grc721.TokenID) + default: + panic("unsupported tokenId type") + } +} diff --git a/staker/filetests/z_single_position_stake_unstake_restake_filetest.gnoA b/staker/filetests/z_single_position_stake_unstake_restake_filetest.gnoA new file mode 100644 index 000000000..c8288c953 --- /dev/null +++ b/staker/filetests/z_single_position_stake_unstake_restake_filetest.gnoA @@ -0,0 +1,225 @@ +// PKGPATH: gno.land/r/gnoswap/v1/staker_test + +// POOLs: +// 1. gnot:gns:3000 + +// POSITIONs: +// 1. in-range ( stake -> unstake -> restake) + +// REWARDs: +// - internal tier 1 ( gnot:gns:3000 ) + +package staker_test + +import ( + "std" + "strconv" + + "gno.land/p/demo/grc/grc721" + "gno.land/p/demo/testutils" + + "gno.land/r/gnoswap/v1/common" + "gno.land/r/gnoswap/v1/consts" + + "gno.land/r/demo/wugnot" + "gno.land/r/gnoswap/v1/gns" + + "gno.land/r/gnoswap/v1/gnft" + + pl "gno.land/r/gnoswap/v1/pool" + pn "gno.land/r/gnoswap/v1/position" + sr "gno.land/r/gnoswap/v1/staker" +) + +var ( + adminAddr = consts.ADMIN + adminUser = common.AddrToUser(adminAddr) + adminRealm = std.NewUserRealm(adminAddr) + + // g1v4u8getjdeskcsmjv4shgmmjta047h6lua7mup + externalCreatorAddr = testutils.TestAddress("externalCreator") + externalCreatorUser = common.AddrToUser(externalCreatorAddr) + externalCreatorRealm = std.NewUserRealm(externalCreatorAddr) + + stakerAddr = consts.STAKER_ADDR + stakerUser = common.AddrToUser(stakerAddr) + stakerRealm = std.NewCodeRealm(consts.STAKER_PATH) + + wugnotAddr = consts.WUGNOT_ADDR + + fooPath = "gno.land/r/onbloc/foo" + barPath = "gno.land/r/onbloc/bar" + bazPath = "gno.land/r/onbloc/baz" + quxPath = "gno.land/r/onbloc/qux" + oblPath = "gno.land/r/onbloc/obl" + + gnsPath = "gno.land/r/gnoswap/v1/gns" + wugnotPath = "gno.land/r/demo/wugnot" + + fee100 uint32 = 100 + fee500 uint32 = 500 + fee3000 uint32 = 3000 + + max_timeout int64 = 9999999999 + + // external incentive deposit fee + depositGnsAmount uint64 = 1_000_000_000 // 1_000 GNS + + TIMESTAMP_90DAYS int64 = 90 * 24 * 60 * 60 + TIMESTAMP_180DAYS int64 = 180 * 24 * 60 * 60 + TIMESTAMP_365DAYS int64 = 365 * 24 * 60 * 60 + + poolPath = "gno.land/r/demo/wugnot:gno.land/r/gnoswap/v1/gns:3000" +) + +func main() { + testInit() + testCreatePool() + testMintWugnotGnsPos01() + testMintWugnotGnsPos02() + + testStakeTokenPos01AndPos02() + + testUnstakeTokenPos01() + + // testStakeTokenPos01Again() +} + +func testInit() { + std.TestSetRealm(adminRealm) + + // short warm-up period + sr.SetWarmUp(100, 901) + sr.SetWarmUp(70, 301) + sr.SetWarmUp(50, 151) + sr.SetWarmUp(30, 1) + + // prepare wugnot + std.TestIssueCoins(adminAddr, std.Coins{{"ugnot", 100_000_000_000_000}}) + banker := std.GetBanker(std.BankerTypeRealmSend) + banker.SendCoins(adminAddr, wugnotAddr, std.Coins{{"ugnot", 50_000_000_000_000}}) + std.TestSetOrigSend(std.Coins{{"ugnot", 50_000_000_000_000}}, nil) + wugnot.Deposit() + std.TestSetOrigSend(nil, nil) +} + +func testCreatePool() { + std.TestSetRealm(adminRealm) + + pl.SetPoolCreationFeeByAdmin(0) + + std.TestSkipHeights(1) + pl.CreatePool( + wugnotPath, + gnsPath, + fee3000, + common.TickMathGetSqrtRatioAtTick(0).ToString(), // 79228162514264337593543950337 + ) +} + +func testMintWugnotGnsPos01() { + std.TestSetRealm(adminRealm) + + wugnot.Approve(common.AddrToUser(consts.POOL_ADDR), consts.UINT64_MAX) + gns.Approve(common.AddrToUser(consts.POOL_ADDR), consts.UINT64_MAX) + + std.TestSkipHeights(1) + pn.Mint( + wugnotPath, + gnsPath, + fee3000, + int32(-60), + int32(60), + "50", + "50", + "1", + "1", + max_timeout, + adminAddr, + adminAddr, + ) +} + +func testMintWugnotGnsPos02() { + std.TestSetRealm(adminRealm) + + wugnot.Approve(common.AddrToUser(consts.POOL_ADDR), consts.UINT64_MAX) + gns.Approve(common.AddrToUser(consts.POOL_ADDR), consts.UINT64_MAX) + + std.TestSkipHeights(1) + pn.Mint( + wugnotPath, + gnsPath, + fee3000, + int32(-1020), + int32(1020), + "500000", + "500000", + "1", + "1", + max_timeout, + adminAddr, + adminAddr, + ) +} + +func testStakeTokenPos01AndPos02() { + std.TestSetRealm(adminRealm) + + gnft.Approve(stakerAddr, tokenIdFrom(1)) + gnft.Approve(stakerAddr, tokenIdFrom(2)) + + std.TestSkipHeights(1) + sr.StakeToken(1) + sr.StakeToken(2) +} + +func testUnstakeTokenPos01() { + std.TestSetRealm(adminRealm) + + std.TestSkipHeights(1) + sr.UnstakeToken(1, false) + + /* + std.TestSkipHeights(1) + + TODO: (after fixing unit test) check collected reward + */ +} + +func testStakeTokenPos01Again() { + std.TestSetRealm(adminRealm) + + std.TestSkipHeights(1) + gnft.Approve(stakerAddr, tokenIdFrom(1)) + sr.StakeToken(1) + + /* + std.TestSkipHeights(1) + + TODO: (after fixing unit test) check collected reward + - token 01 has been re-staked (stake->unstake->stake) + + IMPORTANT: + reward amount should be calculate from new staked block height + */ +} + +func tokenIdFrom(tokenId interface{}) grc721.TokenID { + if tokenId == nil { + panic("tokenId is nil") + } + + switch tokenId.(type) { + case string: + return grc721.TokenID(tokenId.(string)) + case int: + return grc721.TokenID(strconv.Itoa(tokenId.(int))) + case uint64: + return grc721.TokenID(strconv.Itoa(int(tokenId.(uint64)))) + case grc721.TokenID: + return tokenId.(grc721.TokenID) + default: + panic("unsupported tokenId type") + } +} diff --git a/staker/filetests/z_single_position_stake_unstake_same_block_filetest.gnoA b/staker/filetests/z_single_position_stake_unstake_same_block_filetest.gnoA new file mode 100644 index 000000000..f5dda2c7a --- /dev/null +++ b/staker/filetests/z_single_position_stake_unstake_same_block_filetest.gnoA @@ -0,0 +1,172 @@ +// PKGPATH: gno.land/r/gnoswap/v1/staker_test + +// POOLs: +// 1. gnot:gns:3000 + +// POSITIONs: +// 1. in-range ( stake -> unstake in same block) + +// REWARDs: +// - internal tier 1 ( gnot:gns:3000 ) + +package staker_test + +import ( + "std" + "strconv" + + "gno.land/p/demo/grc/grc721" + "gno.land/p/demo/testutils" + + "gno.land/r/gnoswap/v1/common" + "gno.land/r/gnoswap/v1/consts" + + "gno.land/r/demo/wugnot" + "gno.land/r/gnoswap/v1/gns" + + "gno.land/r/gnoswap/v1/gnft" + + pl "gno.land/r/gnoswap/v1/pool" + pn "gno.land/r/gnoswap/v1/position" + sr "gno.land/r/gnoswap/v1/staker" +) + +var ( + adminAddr = consts.ADMIN + adminUser = common.AddrToUser(adminAddr) + adminRealm = std.NewUserRealm(adminAddr) + + // g1v4u8getjdeskcsmjv4shgmmjta047h6lua7mup + externalCreatorAddr = testutils.TestAddress("externalCreator") + externalCreatorUser = common.AddrToUser(externalCreatorAddr) + externalCreatorRealm = std.NewUserRealm(externalCreatorAddr) + + stakerAddr = consts.STAKER_ADDR + stakerUser = common.AddrToUser(stakerAddr) + stakerRealm = std.NewCodeRealm(consts.STAKER_PATH) + + wugnotAddr = consts.WUGNOT_ADDR + + fooPath = "gno.land/r/onbloc/foo" + barPath = "gno.land/r/onbloc/bar" + bazPath = "gno.land/r/onbloc/baz" + quxPath = "gno.land/r/onbloc/qux" + oblPath = "gno.land/r/onbloc/obl" + + gnsPath = "gno.land/r/gnoswap/v1/gns" + wugnotPath = "gno.land/r/demo/wugnot" + + fee100 uint32 = 100 + fee500 uint32 = 500 + fee3000 uint32 = 3000 + + max_timeout int64 = 9999999999 + + // external incentive deposit fee + depositGnsAmount uint64 = 1_000_000_000 // 1_000 GNS + + TIMESTAMP_90DAYS int64 = 90 * 24 * 60 * 60 + TIMESTAMP_180DAYS int64 = 180 * 24 * 60 * 60 + TIMESTAMP_365DAYS int64 = 365 * 24 * 60 * 60 + + poolPath = "gno.land/r/demo/wugnot:gno.land/r/gnoswap/v1/gns:3000" +) + +func main() { + testInit() + testCreatePool() + testMintWugnotGnsPos01() + testStakeAndUnstakeTokenPos01() +} + +func testInit() { + std.TestSetRealm(adminRealm) + + // short warm-up period + sr.SetWarmUp(100, 901) + sr.SetWarmUp(70, 301) + sr.SetWarmUp(50, 151) + sr.SetWarmUp(30, 1) + + // prepare wugnot + std.TestIssueCoins(adminAddr, std.Coins{{"ugnot", 100_000_000_000_000}}) + banker := std.GetBanker(std.BankerTypeRealmSend) + banker.SendCoins(adminAddr, wugnotAddr, std.Coins{{"ugnot", 50_000_000_000_000}}) + std.TestSetOrigSend(std.Coins{{"ugnot", 50_000_000_000_000}}, nil) + wugnot.Deposit() + std.TestSetOrigSend(nil, nil) +} + +func testCreatePool() { + std.TestSetRealm(adminRealm) + + pl.SetPoolCreationFeeByAdmin(0) + + std.TestSkipHeights(1) + pl.CreatePool( + wugnotPath, + gnsPath, + fee3000, + common.TickMathGetSqrtRatioAtTick(0).ToString(), // 79228162514264337593543950337 + ) +} + +func testMintWugnotGnsPos01() { + std.TestSetRealm(adminRealm) + + wugnot.Approve(common.AddrToUser(consts.POOL_ADDR), consts.UINT64_MAX) + gns.Approve(common.AddrToUser(consts.POOL_ADDR), consts.UINT64_MAX) + + std.TestSkipHeights(1) + pn.Mint( + wugnotPath, + gnsPath, + fee3000, + int32(-60), + int32(60), + "50", + "50", + "1", + "1", + max_timeout, + adminAddr, + adminAddr, + ) +} + +func testStakeAndUnstakeTokenPos01() { + std.TestSetRealm(adminRealm) + + gnft.Approve(stakerAddr, tokenIdFrom(1)) + + std.TestSkipHeights(1) + sr.StakeToken(1) + sr.UnstakeToken(1, false) + + /* + std.TestSkipHeights(1) + + TODO: (after fixing unit test) check collected reward + - position 01 has been unstaked in same block + - no reward should be collected + */ +} + +func tokenIdFrom(tokenId interface{}) grc721.TokenID { + if tokenId == nil { + panic("tokenId is nil") + } + + switch tokenId.(type) { + case string: + return grc721.TokenID(tokenId.(string)) + case int: + return grc721.TokenID(strconv.Itoa(tokenId.(int))) + case uint64: + return grc721.TokenID(strconv.Itoa(int(tokenId.(uint64)))) + case grc721.TokenID: + return tokenId.(grc721.TokenID) + default: + panic("unsupported tokenId type") + } +} diff --git a/staker/filetests/z_staked_liquidity_change_by_staking_external_filetest.gnoA b/staker/filetests/z_staked_liquidity_change_by_staking_external_filetest.gnoA new file mode 100644 index 000000000..c181c8d02 --- /dev/null +++ b/staker/filetests/z_staked_liquidity_change_by_staking_external_filetest.gnoA @@ -0,0 +1,261 @@ +// PKGPATH: gno.land/r/gnoswap/v1/staker_test + +// POOLs: +// 1. bar:qux:100 + +// POSITIONs: +// 1. in-range +// 2. (will be staked) out-range +// 3. (will be staked) in-range + +// REWARDs: +// - external bar ( bar:qux:100 ) + +package staker_test + +import ( + "std" + "strconv" + "time" + + "gno.land/p/demo/grc/grc721" + + "gno.land/r/gnoswap/v1/common" + "gno.land/r/gnoswap/v1/consts" + + _ "gno.land/r/demo/wugnot" + "gno.land/r/gnoswap/v1/gns" + "gno.land/r/onbloc/bar" + "gno.land/r/onbloc/qux" + + "gno.land/r/gnoswap/v1/gnft" + + pl "gno.land/r/gnoswap/v1/pool" + pn "gno.land/r/gnoswap/v1/position" + sr "gno.land/r/gnoswap/v1/staker" +) + +var ( + adminAddr = consts.ADMIN + adminUser = common.AddrToUser(adminAddr) + adminRealm = std.NewUserRealm(adminAddr) + + stakerAddr = consts.STAKER_ADDR + stakerUser = common.AddrToUser(stakerAddr) + stakerRealm = std.NewCodeRealm(consts.STAKER_PATH) + + fooPath = "gno.land/r/onbloc/foo" + barPath = "gno.land/r/onbloc/bar" + bazPath = "gno.land/r/onbloc/baz" + quxPath = "gno.land/r/onbloc/qux" + oblPath = "gno.land/r/onbloc/obl" + + gnsPath = "gno.land/r/gnoswap/v1/gns" + wugnotPath = "gno.land/r/demo/wugnot" + + fee100 uint32 = 100 + fee500 uint32 = 500 + fee3000 uint32 = 3000 + + max_timeout int64 = 9999999999 + + // external incentive deposit fee + depositGnsAmount uint64 = 1_000_000_000 // 1_000 GNS + + TIMESTAMP_90DAYS int64 = 90 * 24 * 60 * 60 + TIMESTAMP_180DAYS int64 = 180 * 24 * 60 * 60 + TIMESTAMP_365DAYS int64 = 365 * 24 * 60 * 60 + + poolPath = "gno.land/r/onbloc/bar:gno.land/r/onbloc/qux:100" +) + +func main() { + testInit() + testCreatePool() + testMintBarQuxPos01() + + testCreateExternalIncentive() + + testStakeTokenPos01() + testMakeExternalBarStart() // position-01 is in-range + + testMintAndStakeBarQuxPos02() // position-02 is out-range (no reward) + + testMintAndStakeBarQuxPos03() // position-03 is in-range + +} + +func testInit() { + std.TestSetRealm(adminRealm) + + // short warm-up period + sr.SetWarmUp(100, 901) + sr.SetWarmUp(70, 301) + sr.SetWarmUp(50, 151) + sr.SetWarmUp(30, 1) +} + +func testCreatePool() { + std.TestSetRealm(adminRealm) + + pl.SetPoolCreationFeeByAdmin(0) + + std.TestSkipHeights(1) + pl.CreatePool( + barPath, + quxPath, + fee100, + common.TickMathGetSqrtRatioAtTick(0).ToString(), // 79228162514264337593543950337 + ) +} + +func testMintBarQuxPos01() { + std.TestSetRealm(adminRealm) + + bar.Approve(common.AddrToUser(consts.POOL_ADDR), consts.UINT64_MAX) + qux.Approve(common.AddrToUser(consts.POOL_ADDR), consts.UINT64_MAX) + + std.TestSkipHeights(1) + pn.Mint( + barPath, + quxPath, + fee100, + int32(-50), + int32(50), + "50", + "50", + "1", + "1", + max_timeout, + adminAddr, + adminAddr, + ) +} + +func testCreateExternalIncentive() { + std.TestSetRealm(adminRealm) + + bar.Approve(common.AddrToUser(consts.STAKER_ADDR), consts.UINT64_MAX) + gns.Approve(common.AddrToUser(consts.STAKER_ADDR), depositGnsAmount) + + std.TestSkipHeights(1) + sr.CreateExternalIncentive( + "gno.land/r/onbloc/bar:gno.land/r/onbloc/qux:100", + barPath, + "9000000000", + 1234569600, + 1234569600+TIMESTAMP_90DAYS, + ) +} + +func testStakeTokenPos01() { + std.TestSetRealm(adminRealm) + + gnft.Approve(stakerAddr, tokenIdFrom(1)) + + std.TestSkipHeights(1) + sr.StakeToken(1) +} + +func testMakeExternalBarStart() { + externalStartTime := (1234569600 + TIMESTAMP_90DAYS) + nowTime := time.Now().Unix() + timeLeft := externalStartTime - nowTime + + blockAvgTime := consts.BLOCK_GENERATION_INTERVAL + blockLeft := timeLeft / blockAvgTime + + std.TestSkipHeights(int64(blockLeft)) // skip until external bar starts + std.TestSkipHeights(10) // skip bit more to see reward calculation + + /* + TODO: (after fixing unit test) check reward + - only position-01 reward should be increased + */ +} + +func testMintAndStakeBarQuxPos02() { + std.TestSetRealm(adminRealm) + + bar.Approve(common.AddrToUser(consts.POOL_ADDR), consts.UINT64_MAX) + qux.Approve(common.AddrToUser(consts.POOL_ADDR), consts.UINT64_MAX) + + std.TestSkipHeights(1) + pn.Mint( + barPath, + quxPath, + fee100, + int32(60), + int32(120), + "100", + "100", + "0", + "0", + max_timeout, + adminAddr, + adminAddr, + ) + + gnft.Approve(stakerAddr, tokenIdFrom(2)) + sr.StakeToken(2) + + /* + std.TestSkipHeights(1) + + TODO: (after fixing unit test) check reward + - position-02 is out-range + - only position-01 reward should be increased + */ +} + +func testMintAndStakeBarQuxPos03() { + std.TestSetRealm(adminRealm) + + bar.Approve(common.AddrToUser(consts.POOL_ADDR), consts.UINT64_MAX) + qux.Approve(common.AddrToUser(consts.POOL_ADDR), consts.UINT64_MAX) + + std.TestSkipHeights(1) + pn.Mint( + barPath, + quxPath, + fee100, + int32(-100), + int32(100), + "100", + "100", + "0", + "0", + max_timeout, + adminAddr, + adminAddr, + ) + + gnft.Approve(stakerAddr, tokenIdFrom(3)) + sr.StakeToken(3) + + /* + std.TestSkipHeights(1) + + TODO: (after fixing unit test) check reward + - position-01 and position-03 are in-range + */ +} + +func tokenIdFrom(tokenId interface{}) grc721.TokenID { + if tokenId == nil { + panic("tokenId is nil") + } + + switch tokenId.(type) { + case string: + return grc721.TokenID(tokenId.(string)) + case int: + return grc721.TokenID(strconv.Itoa(tokenId.(int))) + case uint64: + return grc721.TokenID(strconv.Itoa(int(tokenId.(uint64)))) + case grc721.TokenID: + return tokenId.(grc721.TokenID) + default: + panic("unsupported tokenId type") + } +} diff --git a/staker/filetests/z_staked_liquidity_change_by_staking_internal_filetest.gnoA b/staker/filetests/z_staked_liquidity_change_by_staking_internal_filetest.gnoA new file mode 100644 index 000000000..df3858f13 --- /dev/null +++ b/staker/filetests/z_staked_liquidity_change_by_staking_internal_filetest.gnoA @@ -0,0 +1,231 @@ +// PKGPATH: gno.land/r/gnoswap/v1/staker_test + +// POOLs: +// 1. gnot:gns:3000 + +// POSITIONs: +// 1. in-range +// 2. (will be staked) out-range +// 3. (will be staked) in-range + +// REWARDs: +// - internal tier 1 ( gnot:gns:3000 ) + +package staker_test + +import ( + "std" + "strconv" + + "gno.land/p/demo/grc/grc721" + + "gno.land/r/gnoswap/v1/common" + "gno.land/r/gnoswap/v1/consts" + + "gno.land/r/demo/wugnot" + "gno.land/r/gnoswap/v1/gns" + + "gno.land/r/gnoswap/v1/gnft" + + pl "gno.land/r/gnoswap/v1/pool" + pn "gno.land/r/gnoswap/v1/position" + sr "gno.land/r/gnoswap/v1/staker" +) + +var ( + adminAddr = consts.ADMIN + adminUser = common.AddrToUser(adminAddr) + adminRealm = std.NewUserRealm(adminAddr) + + stakerAddr = consts.STAKER_ADDR + stakerUser = common.AddrToUser(stakerAddr) + stakerRealm = std.NewCodeRealm(consts.STAKER_PATH) + + wugnotAddr = consts.WUGNOT_ADDR + + fooPath = "gno.land/r/onbloc/foo" + barPath = "gno.land/r/onbloc/bar" + bazPath = "gno.land/r/onbloc/baz" + quxPath = "gno.land/r/onbloc/qux" + oblPath = "gno.land/r/onbloc/obl" + + gnsPath = "gno.land/r/gnoswap/v1/gns" + wugnotPath = "gno.land/r/demo/wugnot" + + fee100 uint32 = 100 + fee500 uint32 = 500 + fee3000 uint32 = 3000 + + max_timeout int64 = 9999999999 + + // external incentive deposit fee + depositGnsAmount uint64 = 1_000_000_000 // 1_000 GNS + + TIMESTAMP_90DAYS int64 = 90 * 24 * 60 * 60 + TIMESTAMP_180DAYS int64 = 180 * 24 * 60 * 60 + TIMESTAMP_365DAYS int64 = 365 * 24 * 60 * 60 +) + +func main() { + testInit() + testCreatePool() + + testMintAndStakeWugnotGnsPos01() // position-01 is in-range + + testMintAndStakeWugnotGnsPos02() // position-02 is out-range (no reward) + + testMintAndStakeWugnotGnsPos03() // position-03 is in-range + +} + +func testInit() { + std.TestSetRealm(adminRealm) + + // short warm-up period + sr.SetWarmUp(100, 901) + sr.SetWarmUp(70, 301) + sr.SetWarmUp(50, 151) + sr.SetWarmUp(30, 1) + + // prepare wugnot + std.TestIssueCoins(adminAddr, std.Coins{{"ugnot", 100_000_000_000_000}}) + banker := std.GetBanker(std.BankerTypeRealmSend) + banker.SendCoins(adminAddr, wugnotAddr, std.Coins{{"ugnot", 50_000_000_000_000}}) + std.TestSetOrigSend(std.Coins{{"ugnot", 50_000_000_000_000}}, nil) + wugnot.Deposit() + std.TestSetOrigSend(nil, nil) +} + +func testCreatePool() { + std.TestSetRealm(adminRealm) + + pl.SetPoolCreationFeeByAdmin(0) + + std.TestSkipHeights(1) + pl.CreatePool( + wugnotPath, + gnsPath, + fee3000, + common.TickMathGetSqrtRatioAtTick(0).ToString(), // 79228162514264337593543950337 + ) +} + +func testMintAndStakeWugnotGnsPos01() { + std.TestSetRealm(adminRealm) + + wugnot.Approve(common.AddrToUser(consts.POOL_ADDR), consts.UINT64_MAX) + gns.Approve(common.AddrToUser(consts.POOL_ADDR), consts.UINT64_MAX) + + std.TestSkipHeights(1) + pn.Mint( + wugnotPath, + gnsPath, + fee3000, + int32(-60), + int32(60), + "100", + "100", + "0", + "0", + max_timeout, + adminAddr, + adminAddr, + ) + + gnft.Approve(stakerAddr, tokenIdFrom(1)) + sr.StakeToken(1) + + /* + std.TestSkipHeights(1) + + TODO: (after fixing unit test) check reward + - position-01 is in-range + */ +} + +func testMintAndStakeWugnotGnsPos02() { + std.TestSetRealm(adminRealm) + + wugnot.Approve(common.AddrToUser(consts.POOL_ADDR), consts.UINT64_MAX) + gns.Approve(common.AddrToUser(consts.POOL_ADDR), consts.UINT64_MAX) + + std.TestSkipHeights(1) + pn.Mint( + wugnotPath, + gnsPath, + fee3000, + int32(60), + int32(120), + "100", + "100", + "0", + "0", + max_timeout, + adminAddr, + adminAddr, + ) + + gnft.Approve(stakerAddr, tokenIdFrom(2)) + sr.StakeToken(2) + + /* + std.TestSkipHeights(1) + + TODO: (after fixing unit test) check reward + - position-02 is out-range + - only position-01 reward should be increased + */ +} + +func testMintAndStakeWugnotGnsPos03() { + std.TestSetRealm(adminRealm) + + wugnot.Approve(common.AddrToUser(consts.POOL_ADDR), consts.UINT64_MAX) + gns.Approve(common.AddrToUser(consts.POOL_ADDR), consts.UINT64_MAX) + + std.TestSkipHeights(1) + pn.Mint( + wugnotPath, + gnsPath, + fee3000, + int32(-60), + int32(60), + "100", + "100", + "0", + "0", + max_timeout, + adminAddr, + adminAddr, + ) + + gnft.Approve(stakerAddr, tokenIdFrom(3)) + sr.StakeToken(3) + + /* + std.TestSkipHeights(1) + + TODO: (after fixing unit test) check reward + - position-03 is in-range + - both position-01 and position-03 rewards should be increased + */ +} + +func tokenIdFrom(tokenId interface{}) grc721.TokenID { + if tokenId == nil { + panic("tokenId is nil") + } + + switch tokenId.(type) { + case string: + return grc721.TokenID(tokenId.(string)) + case int: + return grc721.TokenID(strconv.Itoa(tokenId.(int))) + case uint64: + return grc721.TokenID(strconv.Itoa(int(tokenId.(uint64)))) + case grc721.TokenID: + return tokenId.(grc721.TokenID) + default: + panic("unsupported tokenId type") + } +} diff --git a/staker/filetests/z_staked_liquidity_change_by_unstaking_external_filetest.gnoA b/staker/filetests/z_staked_liquidity_change_by_unstaking_external_filetest.gnoA new file mode 100644 index 000000000..a9d543aca --- /dev/null +++ b/staker/filetests/z_staked_liquidity_change_by_unstaking_external_filetest.gnoA @@ -0,0 +1,243 @@ +// PKGPATH: gno.land/r/gnoswap/v1/staker_test + +// POOLs: +// 1. bar:qux:100 + +// POSITIONs: +// 1. in-range +// 2. in-range (will be unstaked) + +// REWARDs: +// - external bar ( bar:qux:100 ) + +package staker_test + +import ( + "std" + "strconv" + "time" + + "gno.land/p/demo/grc/grc721" + + "gno.land/r/gnoswap/v1/common" + "gno.land/r/gnoswap/v1/consts" + + _ "gno.land/r/demo/wugnot" + "gno.land/r/gnoswap/v1/gns" + "gno.land/r/onbloc/bar" + "gno.land/r/onbloc/qux" + + "gno.land/r/gnoswap/v1/gnft" + + pl "gno.land/r/gnoswap/v1/pool" + pn "gno.land/r/gnoswap/v1/position" + sr "gno.land/r/gnoswap/v1/staker" +) + +var ( + adminAddr = consts.ADMIN + adminUser = common.AddrToUser(adminAddr) + adminRealm = std.NewUserRealm(adminAddr) + + stakerAddr = consts.STAKER_ADDR + stakerUser = common.AddrToUser(stakerAddr) + stakerRealm = std.NewCodeRealm(consts.STAKER_PATH) + + fooPath = "gno.land/r/onbloc/foo" + barPath = "gno.land/r/onbloc/bar" + bazPath = "gno.land/r/onbloc/baz" + quxPath = "gno.land/r/onbloc/qux" + oblPath = "gno.land/r/onbloc/obl" + + gnsPath = "gno.land/r/gnoswap/v1/gns" + wugnotPath = "gno.land/r/demo/wugnot" + + fee100 uint32 = 100 + fee500 uint32 = 500 + fee3000 uint32 = 3000 + + max_timeout int64 = 9999999999 + + // external incentive deposit fee + depositGnsAmount uint64 = 1_000_000_000 // 1_000 GNS + + TIMESTAMP_90DAYS int64 = 90 * 24 * 60 * 60 + TIMESTAMP_180DAYS int64 = 180 * 24 * 60 * 60 + TIMESTAMP_365DAYS int64 = 365 * 24 * 60 * 60 + + poolPath = "gno.land/r/onbloc/bar:gno.land/r/onbloc/qux:100" +) + +func main() { + testInit() + testCreatePool() + testMintBarQuxPos01() + + testCreateExternalIncentive() + + testStakeTokenPos01() + testMakeExternalBarStart() // position-01 is in-range + + testMintAndStakeBarQuxPos02() // position-02 is in-range + testUnstakeTokenPos02() // position-02 is unstaked + +} + +func testInit() { + std.TestSetRealm(adminRealm) + + // short warm-up period + sr.SetWarmUp(100, 901) + sr.SetWarmUp(70, 301) + sr.SetWarmUp(50, 151) + sr.SetWarmUp(30, 1) +} + +func testCreatePool() { + std.TestSetRealm(adminRealm) + + pl.SetPoolCreationFeeByAdmin(0) + + std.TestSkipHeights(1) + pl.CreatePool( + barPath, + quxPath, + fee100, + common.TickMathGetSqrtRatioAtTick(0).ToString(), // 79228162514264337593543950337 + ) +} + +func testMintBarQuxPos01() { + std.TestSetRealm(adminRealm) + + bar.Approve(common.AddrToUser(consts.POOL_ADDR), consts.UINT64_MAX) + qux.Approve(common.AddrToUser(consts.POOL_ADDR), consts.UINT64_MAX) + + std.TestSkipHeights(1) + pn.Mint( + barPath, + quxPath, + fee100, + int32(-50), + int32(50), + "50", + "50", + "1", + "1", + max_timeout, + adminAddr, + adminAddr, + ) +} + +func testCreateExternalIncentive() { + std.TestSetRealm(adminRealm) + + bar.Approve(common.AddrToUser(consts.STAKER_ADDR), consts.UINT64_MAX) + gns.Approve(common.AddrToUser(consts.STAKER_ADDR), depositGnsAmount) + + std.TestSkipHeights(1) + sr.CreateExternalIncentive( + "gno.land/r/onbloc/bar:gno.land/r/onbloc/qux:100", + barPath, + "9000000000", + 1234569600, + 1234569600+TIMESTAMP_90DAYS, + ) +} + +func testStakeTokenPos01() { + std.TestSetRealm(adminRealm) + + gnft.Approve(stakerAddr, tokenIdFrom(1)) + + std.TestSkipHeights(1) + sr.StakeToken(1) +} + +func testMakeExternalBarStart() { + externalStartTime := 1234569600 + nowTime := time.Now().Unix() + timeLeft := externalStartTime - nowTime + + blockAvgTime := consts.BLOCK_GENERATION_INTERVAL + blockLeft := timeLeft / blockAvgTime + + std.TestSkipHeights(int64(blockLeft)) // skip until external bar starts + std.TestSkipHeights(10) // skip bit more to see reward calculation + + /* + std.TestSkipHeights(1) + + TODO: (after fixing unit test) check reward + - only position-01 reward should be increased + */ +} + +func testMintAndStakeBarQuxPos02() { + std.TestSetRealm(adminRealm) + + bar.Approve(common.AddrToUser(consts.POOL_ADDR), consts.UINT64_MAX) + qux.Approve(common.AddrToUser(consts.POOL_ADDR), consts.UINT64_MAX) + + std.TestSkipHeights(1) + pn.Mint( + barPath, + quxPath, + fee100, + int32(-50), + int32(50), + "100", + "100", + "0", + "0", + max_timeout, + adminAddr, + adminAddr, + ) + + gnft.Approve(stakerAddr, tokenIdFrom(2)) + sr.StakeToken(2) + + /* + std.TestSkipHeights(1) + + TODO: (after fixing unit test) check reward + - position-02 is in-range + - both position-01 and position-02 rewards should be increased + */ +} + +func testUnstakeTokenPos02() { + std.TestSetRealm(adminRealm) + + std.TestSkipHeights(1) + sr.UnstakeToken(2) + + /* + std.TestSkipHeights(1) + + TODO: (after fixing unit test) check reward + - position-02 is unstaked + - only position-01 reward should be increased + */ +} + +func tokenIdFrom(tokenId interface{}) grc721.TokenID { + if tokenId == nil { + panic("tokenId is nil") + } + + switch tokenId.(type) { + case string: + return grc721.TokenID(tokenId.(string)) + case int: + return grc721.TokenID(strconv.Itoa(tokenId.(int))) + case uint64: + return grc721.TokenID(strconv.Itoa(int(tokenId.(uint64)))) + case grc721.TokenID: + return tokenId.(grc721.TokenID) + default: + panic("unsupported tokenId type") + } +} diff --git a/staker/filetests/z_staked_liquidity_change_by_unstaking_internal_filetest.gnoA b/staker/filetests/z_staked_liquidity_change_by_unstaking_internal_filetest.gnoA new file mode 100644 index 000000000..2d7d38e56 --- /dev/null +++ b/staker/filetests/z_staked_liquidity_change_by_unstaking_internal_filetest.gnoA @@ -0,0 +1,206 @@ +// PKGPATH: gno.land/r/gnoswap/v1/staker_test + +// POOLs: +// 1. gnot:gns:3000 + +// POSITIONs: +// 1. in-range +// 2. in-range (will be unstaked) + +// REWARDs: +// - internal tier 1 ( gnot:gns:3000 ) + +package staker_test + +import ( + "std" + "strconv" + + "gno.land/p/demo/grc/grc721" + + "gno.land/r/gnoswap/v1/common" + "gno.land/r/gnoswap/v1/consts" + + "gno.land/r/demo/wugnot" + "gno.land/r/gnoswap/v1/gns" + + "gno.land/r/gnoswap/v1/gnft" + + pl "gno.land/r/gnoswap/v1/pool" + pn "gno.land/r/gnoswap/v1/position" + sr "gno.land/r/gnoswap/v1/staker" +) + +var ( + adminAddr = consts.ADMIN + adminUser = common.AddrToUser(adminAddr) + adminRealm = std.NewUserRealm(adminAddr) + + stakerAddr = consts.STAKER_ADDR + stakerUser = common.AddrToUser(stakerAddr) + stakerRealm = std.NewCodeRealm(consts.STAKER_PATH) + + wugnotAddr = consts.WUGNOT_ADDR + + fooPath = "gno.land/r/onbloc/foo" + barPath = "gno.land/r/onbloc/bar" + bazPath = "gno.land/r/onbloc/baz" + quxPath = "gno.land/r/onbloc/qux" + oblPath = "gno.land/r/onbloc/obl" + + gnsPath = "gno.land/r/gnoswap/v1/gns" + wugnotPath = "gno.land/r/demo/wugnot" + + fee100 uint32 = 100 + fee500 uint32 = 500 + fee3000 uint32 = 3000 + + max_timeout int64 = 9999999999 + + // external incentive deposit fee + depositGnsAmount uint64 = 1_000_000_000 // 1_000 GNS + + TIMESTAMP_90DAYS int64 = 90 * 24 * 60 * 60 + TIMESTAMP_180DAYS int64 = 180 * 24 * 60 * 60 + TIMESTAMP_365DAYS int64 = 365 * 24 * 60 * 60 +) + +func main() { + testInit() + testCreatePool() + + testMintAndStakeWugnotGnsPos01() // position-01 is in-range + + testMintAndStakeWugnotGnsPos02() // position-02 is in-range + testUnstakeTokenPos02() // position-02 is unstaked +} + +func testInit() { + std.TestSetRealm(adminRealm) + + // short warm-up period + sr.SetWarmUp(100, 901) + sr.SetWarmUp(70, 301) + sr.SetWarmUp(50, 151) + sr.SetWarmUp(30, 1) + + // prepare wugnot + std.TestIssueCoins(adminAddr, std.Coins{{"ugnot", 100_000_000_000_000}}) + banker := std.GetBanker(std.BankerTypeRealmSend) + banker.SendCoins(adminAddr, wugnotAddr, std.Coins{{"ugnot", 50_000_000_000_000}}) + std.TestSetOrigSend(std.Coins{{"ugnot", 50_000_000_000_000}}, nil) + wugnot.Deposit() + std.TestSetOrigSend(nil, nil) +} + +func testCreatePool() { + std.TestSetRealm(adminRealm) + + pl.SetPoolCreationFeeByAdmin(0) + + std.TestSkipHeights(1) + pl.CreatePool( + wugnotPath, + gnsPath, + fee3000, + common.TickMathGetSqrtRatioAtTick(0).ToString(), // 79228162514264337593543950337 + ) +} + +func testMintAndStakeWugnotGnsPos01() { + std.TestSetRealm(adminRealm) + + wugnot.Approve(common.AddrToUser(consts.POOL_ADDR), consts.UINT64_MAX) + gns.Approve(common.AddrToUser(consts.POOL_ADDR), consts.UINT64_MAX) + + std.TestSkipHeights(1) + pn.Mint( + wugnotPath, + gnsPath, + fee3000, + int32(-60), + int32(60), + "100", + "100", + "0", + "0", + max_timeout, + adminAddr, + adminAddr, + ) + + gnft.Approve(stakerAddr, tokenIdFrom(1)) + sr.StakeToken(1) + + /* + std.TestSkipHeights(1) + TODO: (after fixing unit test) check reward + */ +} + +func testMintAndStakeWugnotGnsPos02() { + std.TestSetRealm(adminRealm) + + wugnot.Approve(common.AddrToUser(consts.POOL_ADDR), consts.UINT64_MAX) + gns.Approve(common.AddrToUser(consts.POOL_ADDR), consts.UINT64_MAX) + + std.TestSkipHeights(1) + pn.Mint( + wugnotPath, + gnsPath, + fee3000, + int32(-60), + int32(60), + "100", + "100", + "0", + "0", + max_timeout, + adminAddr, + adminAddr, + ) + + gnft.Approve(stakerAddr, tokenIdFrom(2)) + sr.StakeToken(2) + + /* + std.TestSkipHeights(1) + + TODO: (after fixing unit test) check reward + - both positions are in-range + */ +} + +func testUnstakeTokenPos02() { + std.TestSetRealm(adminRealm) + + std.TestSkipHeights(1) + sr.UnstakeToken(2) + + /* + std.TestSkipHeights(1) + + TODO: (after fixing unit test) check reward + - position-02 is unstaked + - only position-01 reward should be increased + */ +} + +func tokenIdFrom(tokenId interface{}) grc721.TokenID { + if tokenId == nil { + panic("tokenId is nil") + } + + switch tokenId.(type) { + case string: + return grc721.TokenID(tokenId.(string)) + case int: + return grc721.TokenID(strconv.Itoa(tokenId.(int))) + case uint64: + return grc721.TokenID(strconv.Itoa(int(tokenId.(uint64)))) + case grc721.TokenID: + return tokenId.(grc721.TokenID) + default: + panic("unsupported tokenId type") + } +} diff --git a/staker/filetests/z_staker_internal_external_01_both_has_gns_filetest.gnoA b/staker/filetests/z_staker_internal_external_01_both_has_gns_filetest.gnoA new file mode 100644 index 000000000..e42db9b18 --- /dev/null +++ b/staker/filetests/z_staker_internal_external_01_both_has_gns_filetest.gnoA @@ -0,0 +1,306 @@ +// PKGPATH: gno.land/r/gnoswap/v1/staker_test +// 1 pool for tier 1 (gnot:gns:0.3%) +// - same pool has 2 external incentives +// 1. bar +// 2. gns +// > internal gns and external gns must not affect each other + +package staker_test + +import ( + "std" + "strconv" + + "gno.land/p/demo/grc/grc721" + + "gno.land/r/gnoswap/v1/common" + "gno.land/r/gnoswap/v1/consts" + + "gno.land/r/demo/wugnot" + "gno.land/r/gnoswap/v1/gns" + "gno.land/r/onbloc/bar" + _ "gno.land/r/onbloc/baz" + + "gno.land/r/gnoswap/v1/gnft" + + en "gno.land/r/gnoswap/v1/emission" + pl "gno.land/r/gnoswap/v1/pool" + pn "gno.land/r/gnoswap/v1/position" + sr "gno.land/r/gnoswap/v1/staker" +) + +var ( + adminAddr = consts.ADMIN + adminUser = common.AddrToUser(adminAddr) + adminRealm = std.NewUserRealm(adminAddr) + + stakerAddr = consts.STAKER_ADDR + stakerUser = common.AddrToUser(stakerAddr) + stakerRealm = std.NewCodeRealm(consts.STAKER_PATH) + + wugnotAddr = consts.WUGNOT_ADDR + + fooPath = "gno.land/r/onbloc/foo" + barPath = "gno.land/r/onbloc/bar" + bazPath = "gno.land/r/onbloc/baz" + quxPath = "gno.land/r/onbloc/qux" + oblPath = "gno.land/r/onbloc/obl" + + gnsPath = "gno.land/r/gnoswap/v1/gns" + wugnotPath = "gno.land/r/demo/wugnot" + + fee100 uint32 = 100 + fee500 uint32 = 500 + fee3000 uint32 = 3000 + + max_timeout int64 = 9999999999 + + // external incentive deposit fee + depositGnsAmount uint64 = 1_000_000_000 // 1_000 GNS + + TIMESTAMP_90DAYS int64 = 90 * 24 * 60 * 60 + TIMESTAMP_180DAYS int64 = 180 * 24 * 60 * 60 + TIMESTAMP_365DAYS int64 = 365 * 24 * 60 * 60 +) + +func main() { + testInit() + testCreatePool() + testMintWugnotGns01() + testCreateExternalIncentiveBar() + testCreateExternalIncentiveGns() + testStakeToken01() + testRewardCheckBeforeActive() + testRewardCheckAfterActive() + testDuration200() + testCollectReward() + testCollectRewardSameBlockNoReward() + testCollectRewardSingleBlock() +} + +func testInit() { + std.TestSetRealm(adminRealm) + + // short warm-up period + sr.SetWarmUp(100, 901) + sr.SetWarmUp(70, 301) + sr.SetWarmUp(50, 151) + sr.SetWarmUp(30, 1) + + // issue ugnot coins + std.TestIssueCoins(adminAddr, std.Coins{{"ugnot", 100_000_000_000_000}}) + + // prepare wugnot + banker := std.GetBanker(std.BankerTypeRealmSend) + banker.SendCoins(adminAddr, wugnotAddr, std.Coins{{"ugnot", 50_000_000_000_000}}) + std.TestSetOrigSend(std.Coins{{"ugnot", 50_000_000_000_000}}, nil) + wugnot.Deposit() + std.TestSetOrigSend(nil, nil) +} + +func testCreatePool() { + std.TestSetRealm(adminRealm) + + pl.SetPoolCreationFeeByAdmin(0) + + std.TestSkipHeights(1) + // tier 1 by default + pl.CreatePool( + wugnotPath, + gnsPath, + fee3000, + common.TickMathGetSqrtRatioAtTick(0).ToString(), // 79228162514264337593543950337 + ) + + // wil be set to tier 2, and change to tier 1 + pl.CreatePool( + barPath, + bazPath, + fee100, + common.TickMathGetSqrtRatioAtTick(0).ToString(), // 79228162514264337593543950337 + ) +} + +func testMintWugnotGns01() { + std.TestSetRealm(adminRealm) + + wugnot.Approve(common.AddrToUser(consts.POOL_ADDR), consts.UINT64_MAX) + gns.Approve(common.AddrToUser(consts.POOL_ADDR), consts.UINT64_MAX) + + std.TestSkipHeights(1) + pn.Mint( + wugnotPath, + gnsPath, + fee3000, + int32(-1020), + int32(1020), + "500000000", + "500000000", + "1", + "1", + max_timeout, + adminAddr, + adminAddr, + ) +} + +func testCreateExternalIncentiveBar() { + std.TestSetRealm(adminRealm) + + bar.Approve(common.AddrToUser(consts.STAKER_ADDR), consts.UINT64_MAX) + gns.Approve(common.AddrToUser(consts.STAKER_ADDR), depositGnsAmount) + + std.TestSkipHeights(1) + sr.AddToken(barPath) + sr.CreateExternalIncentive( + "gno.land/r/demo/wugnot:gno.land/r/gnoswap/v1/gns:3000", + barPath, + "20000000", + 1234569600, + 1234569600+TIMESTAMP_90DAYS, + ) +} + +func testCreateExternalIncentiveGns() { + std.TestSetRealm(adminRealm) + + gns.Approve(common.AddrToUser(consts.STAKER_ADDR), consts.UINT64_MAX) + + std.TestSkipHeights(1) + sr.CreateExternalIncentive( + "gno.land/r/demo/wugnot:gno.land/r/gnoswap/v1/gns:3000", + gnsPath, + "20000000", + 1234569600, + 1234569600+TIMESTAMP_90DAYS, + ) +} + +func testStakeToken01() { + std.TestSetRealm(adminRealm) + + gnft.Approve(stakerAddr, tokenIdFrom(1)) + + std.TestSkipHeights(1) + sr.StakeToken(1) + // TODO: (after fixing unit test) check reward by querying with api funcs +} + +func testRewardCheckBeforeActive() { + std.TestSkipHeights(1) + en.MintAndDistributeGns() + sr.CalcPoolPositionRefactor() + + // TODO: (after fixing unit test) check reward by querying with api funcs +} + +func testRewardCheckAfterActive() { + std.TestSkipHeights(849) // in active + std.TestSkipHeights(1) // active // but no block passed since active + std.TestSkipHeights(50) // skip 50 more block + + // TODO: (after fixing unit test) check reward by querying with api funcs +} + +func testDuration200() { + std.TestSkipHeights(200) + + // TODO: (after fixing unit test) check reward by querying with api funcs +} + +func testCollectReward() { + std.TestSetRealm(adminRealm) + + oldBar := bar.BalanceOf(adminUser) + oldGns := gns.BalanceOf(adminUser) + + std.TestSkipHeights(1) + sr.CollectReward(1, false) + + newBar := bar.BalanceOf(adminUser) + newGns := gns.BalanceOf(adminUser) + + println("oldBar", oldBar) + println("newBar", newBar) + + // TODO: this gns balance will have both internal and external gns + // need to check how much gns is from internal and how much is from external + println("oldGns", oldGns) + println("newGns", newGns) +} + +func testCollectRewardSameBlockNoReward() { + std.TestSetRealm(adminRealm) + + oldBar := bar.BalanceOf(adminUser) + oldGns := gns.BalanceOf(adminUser) + + // std.TestSkipHeights(1) // DO NOT SKIP HEIGHT + // current logic is to test collect reward in the same block + sr.CollectReward(1, false) + + newBar := bar.BalanceOf(adminUser) + newGns := gns.BalanceOf(adminUser) + + println("oldBar", oldBar) + println("newBar", newBar) + + // TODO: this gns balance will have both internal and external gns + // need to check how much gns is from internal and how much is from external + println("oldGns", oldGns) + println("newGns", newGns) +} + +func testCollectRewardSingleBlock() { + std.TestSetRealm(adminRealm) + + oldBar := bar.BalanceOf(adminUser) + oldGns := gns.BalanceOf(adminUser) + + std.TestSkipHeights(1) // ONLY 1 BLOCK PASSED + sr.CollectReward(1, false) + + newBar := bar.BalanceOf(adminUser) + newGns := gns.BalanceOf(adminUser) + + println("oldBar", oldBar) + println("newBar", newBar) + + // TODO: this gns balance will have both internal and external gns + // need to check how much gns is from internal and how much is from external + println("oldGns", oldGns) + println("newGns", newGns) +} + +// NOTE: filetest can not access helper functions in origin package +// so we need to implement the helper function here + +func tokenIdFrom(tokenId interface{}) grc721.TokenID { + if tokenId == nil { + panic("tokenId is nil") + } + + switch tokenId.(type) { + case string: + return grc721.TokenID(tokenId.(string)) + case int: + return grc721.TokenID(strconv.Itoa(tokenId.(int))) + case uint64: + return grc721.TokenID(strconv.Itoa(int(tokenId.(uint64)))) + case grc721.TokenID: + return tokenId.(grc721.TokenID) + default: + panic("unsupported tokenId type") + } +} + +func ugnotBalanceOf(addr std.Address) uint64 { + testBanker := std.GetBanker(std.BankerTypeReadonly) + + coins := testBanker.GetCoins(addr) + if len(coins) == 0 { + return 0 + } + + return uint64(coins.AmountOf("ugnot")) +} diff --git a/staker/filetests/z_staker_internal_external_02_position_range_change.gnoA b/staker/filetests/z_staker_internal_external_02_position_range_change.gnoA new file mode 100644 index 000000000..f26c5ca8c --- /dev/null +++ b/staker/filetests/z_staker_internal_external_02_position_range_change.gnoA @@ -0,0 +1,318 @@ +// PKGPATH: gno.land/r/gnoswap/v1/staker_test +// 1 pool for tier 1 (gnot:gns:0.3%) +// - same pool has 1 external incentive (bar) +// 1 position (in-range changes) +// - in-range +// - out-range +// - in-range + +package staker_test + +import ( + "std" + "strconv" + + "gno.land/p/demo/grc/grc721" + + "gno.land/r/gnoswap/v1/common" + "gno.land/r/gnoswap/v1/consts" + + "gno.land/r/demo/wugnot" + "gno.land/r/gnoswap/v1/gns" + "gno.land/r/onbloc/bar" + _ "gno.land/r/onbloc/baz" + + "gno.land/r/gnoswap/v1/gnft" + + en "gno.land/r/gnoswap/v1/emission" + pl "gno.land/r/gnoswap/v1/pool" + pn "gno.land/r/gnoswap/v1/position" + rr "gno.land/r/gnoswap/v1/router" + sr "gno.land/r/gnoswap/v1/staker" +) + +var ( + adminAddr = consts.ADMIN + adminUser = common.AddrToUser(adminAddr) + adminRealm = std.NewUserRealm(adminAddr) + + stakerAddr = consts.STAKER_ADDR + stakerUser = common.AddrToUser(stakerAddr) + stakerRealm = std.NewCodeRealm(consts.STAKER_PATH) + + wugnotAddr = consts.WUGNOT_ADDR + + fooPath = "gno.land/r/onbloc/foo" + barPath = "gno.land/r/onbloc/bar" + bazPath = "gno.land/r/onbloc/baz" + quxPath = "gno.land/r/onbloc/qux" + oblPath = "gno.land/r/onbloc/obl" + + gnsPath = "gno.land/r/gnoswap/v1/gns" + wugnotPath = "gno.land/r/demo/wugnot" + + fee100 uint32 = 100 + fee500 uint32 = 500 + fee3000 uint32 = 3000 + + max_timeout int64 = 9999999999 + + // external incentive deposit fee + depositGnsAmount uint64 = 1_000_000_000 // 1_000 GNS + + TIMESTAMP_90DAYS int64 = 90 * 24 * 60 * 60 + TIMESTAMP_180DAYS int64 = 180 * 24 * 60 * 60 + TIMESTAMP_365DAYS int64 = 365 * 24 * 60 * 60 + + poolPath = "gno.land/r/demo/wugnot:gno.land/r/gnoswap/v1/gns:3000" +) + +func main() { + testInit() + testCreatePool() + testMintWugnotGns01() + testMintWugnotGns02() + testCreateExternalIncentiveBar() + testStakeToken01And02() + testRewardCheckBeforeActive() + testRewardCheckAfterActive() + testMakePosition01OutRangeBySwap() + testRewardCheckAfterOut() + testMakePosition01InRangeBySwap() + testRewardCheckAfterIn() +} + +func testInit() { + std.TestSetRealm(adminRealm) + + // short warm-up period + sr.SetWarmUp(100, 901) + sr.SetWarmUp(70, 301) + sr.SetWarmUp(50, 151) + sr.SetWarmUp(30, 1) + + // issue ugnot coins + std.TestIssueCoins(adminAddr, std.Coins{{"ugnot", 100_000_000_000_000}}) + + // prepare wugnot + banker := std.GetBanker(std.BankerTypeRealmSend) + banker.SendCoins(adminAddr, wugnotAddr, std.Coins{{"ugnot", 50_000_000_000_000}}) + std.TestSetOrigSend(std.Coins{{"ugnot", 50_000_000_000_000}}, nil) + wugnot.Deposit() + std.TestSetOrigSend(nil, nil) +} + +func testCreatePool() { + std.TestSetRealm(adminRealm) + + pl.SetPoolCreationFeeByAdmin(0) + + std.TestSkipHeights(1) + // tier 1 by default + pl.CreatePool( + wugnotPath, + gnsPath, + fee3000, + common.TickMathGetSqrtRatioAtTick(0).ToString(), // 79228162514264337593543950337 + ) + + // wil be set to tier 2, and change to tier 1 + pl.CreatePool( + barPath, + bazPath, + fee100, + common.TickMathGetSqrtRatioAtTick(0).ToString(), // 79228162514264337593543950337 + ) +} + +func testMintWugnotGns01() { + std.TestSetRealm(adminRealm) + + wugnot.Approve(common.AddrToUser(consts.POOL_ADDR), consts.UINT64_MAX) + gns.Approve(common.AddrToUser(consts.POOL_ADDR), consts.UINT64_MAX) + + std.TestSkipHeights(1) + pn.Mint( + wugnotPath, + gnsPath, + fee3000, + int32(-60), + int32(60), + "5000", + "5000", + "1", + "1", + max_timeout, + adminAddr, + adminAddr, + ) +} + +func testMintWugnotGns02() { + std.TestSetRealm(adminRealm) + + wugnot.Approve(common.AddrToUser(consts.POOL_ADDR), consts.UINT64_MAX) + gns.Approve(common.AddrToUser(consts.POOL_ADDR), consts.UINT64_MAX) + + std.TestSkipHeights(1) + pn.Mint( + wugnotPath, + gnsPath, + fee3000, + int32(-6000), + int32(6000), + "500000000", + "500000000", + "1", + "1", + max_timeout, + adminAddr, + adminAddr, + ) +} + +func testCreateExternalIncentiveBar() { + std.TestSetRealm(adminRealm) + + bar.Approve(common.AddrToUser(consts.STAKER_ADDR), consts.UINT64_MAX) + gns.Approve(common.AddrToUser(consts.STAKER_ADDR), depositGnsAmount) + + std.TestSkipHeights(1) + sr.AddToken(barPath) + sr.CreateExternalIncentive( + "gno.land/r/demo/wugnot:gno.land/r/gnoswap/v1/gns:3000", + barPath, + "20000000", + 1234569600, + 1234569600+TIMESTAMP_90DAYS, + ) +} + +func testStakeToken01And02() { + std.TestSetRealm(adminRealm) + + gnft.Approve(stakerAddr, tokenIdFrom(1)) + gnft.Approve(stakerAddr, tokenIdFrom(2)) + + std.TestSkipHeights(1) + sr.StakeToken(1) + sr.StakeToken(2) + // TODO: (after fixing unit test) check reward by querying with api funcs +} + +func testRewardCheckBeforeActive() { + std.TestSkipHeights(1) + en.MintAndDistributeGns() + sr.CalcPoolPositionRefactor() + + // TODO: (after fixing unit test) check reward by querying with api funcs +} + +func testRewardCheckAfterActive() { + std.TestSkipHeights(849) // in active + std.TestSkipHeights(1) // active // but no block passed since active + std.TestSkipHeights(50) // skip 50 more block + + // TODO: (after fixing unit test) check reward by querying with api funcs +} + +func testMakePosition01OutRangeBySwap() { + std.TestSetRealm(adminRealm) + + poolTick := pl.PoolGetSlot0Tick(poolPath) + + wugnot.Approve(common.AddrToUser(consts.POOL_ADDR), consts.UINT64_MAX) + gns.Approve(common.AddrToUser(consts.POOL_ADDR), consts.UINT64_MAX) + + wugnot.Approve(common.AddrToUser(consts.ROUTER_ADDR), consts.UINT64_MAX) + gns.Approve(common.AddrToUser(consts.ROUTER_ADDR), consts.UINT64_MAX) + + tokenIn, tokenOut := rr.ExactInSwapRoute( + wugnotPath, // inputToken + gnsPath, // outputToken + "10000000", // finalAmountIn + poolPath, // RouteArr + "100", // quoteArr + "0", // amountOutMin + ) + println("tokenIn", tokenIn) + println("tokenOut", tokenOut) + println() + + newPoolTick := pl.PoolGetSlot0Tick(poolPath) + println("oldPoolTick", poolTick) + println("newPoolTick", newPoolTick) + println() +} + +func testRewardCheckAfterOut() { + std.TestSkipHeights(1) + // TODO: (after fixing unit test) check reward by querying with api funcs +} + +func testMakePosition01InRangeBySwap() { + std.TestSetRealm(adminRealm) + + poolTick := pl.PoolGetSlot0Tick(poolPath) + + wugnot.Approve(common.AddrToUser(consts.POOL_ADDR), consts.UINT64_MAX) + gns.Approve(common.AddrToUser(consts.POOL_ADDR), consts.UINT64_MAX) + + wugnot.Approve(common.AddrToUser(consts.ROUTER_ADDR), consts.UINT64_MAX) + gns.Approve(common.AddrToUser(consts.ROUTER_ADDR), consts.UINT64_MAX) + + tokenIn, tokenOut := rr.ExactInSwapRoute( + wugnotPath, // inputToken + gnsPath, // outputToken + "10000000", // finalAmountIn + "gno.land/r/gnoswap/v1/gns:gno.land/r/demo/wugnot:3000", // routeArr + "100", // quoteArr + "0", // amountOutMin + ) + println("tokenIn", tokenIn) + println("tokenOut", tokenOut) + println() + + newPoolTick := pl.PoolGetSlot0Tick(poolPath) + println("oldPoolTick", poolTick) + println("newPoolTick", newPoolTick) + println() +} + +func testRewardCheckAfterIn() { + std.TestSkipHeights(1) + // TODO: (after fixing unit test) check reward by querying with api funcs +} + +// NOTE: filetest can not access helper functions in origin package +// so we need to implement the helper function here + +func tokenIdFrom(tokenId interface{}) grc721.TokenID { + if tokenId == nil { + panic("tokenId is nil") + } + + switch tokenId.(type) { + case string: + return grc721.TokenID(tokenId.(string)) + case int: + return grc721.TokenID(strconv.Itoa(tokenId.(int))) + case uint64: + return grc721.TokenID(strconv.Itoa(int(tokenId.(uint64)))) + case grc721.TokenID: + return tokenId.(grc721.TokenID) + default: + panic("unsupported tokenId type") + } +} + +func ugnotBalanceOf(addr std.Address) uint64 { + testBanker := std.GetBanker(std.BankerTypeReadonly) + + coins := testBanker.GetCoins(addr) + if len(coins) == 0 { + return 0 + } + + return uint64(coins.AmountOf("ugnot")) +} diff --git a/staker/filetests/z_two_external_incentive_one_ends_external_filetest.gnoA b/staker/filetests/z_two_external_incentive_one_ends_external_filetest.gnoA new file mode 100644 index 000000000..1433c86e5 --- /dev/null +++ b/staker/filetests/z_two_external_incentive_one_ends_external_filetest.gnoA @@ -0,0 +1,251 @@ +// PKGPATH: gno.land/r/gnoswap/v1/staker_test + +// POOLs: +// 1. bar:qux:100 + +// POSITIONs: +// 1. in-range + +// REWARDs: +// - external bar 90 days ( bar:qux:100 ) +// - external qux 180 days ( bar:qux:100 ) + +package staker_test + +import ( + "std" + "strconv" + "time" + + "gno.land/p/demo/grc/grc721" + "gno.land/p/demo/testutils" + + "gno.land/r/gnoswap/v1/common" + "gno.land/r/gnoswap/v1/consts" + + "gno.land/r/gnoswap/v1/gns" + "gno.land/r/onbloc/bar" + "gno.land/r/onbloc/qux" + + "gno.land/r/gnoswap/v1/gnft" + + pl "gno.land/r/gnoswap/v1/pool" + pn "gno.land/r/gnoswap/v1/position" + sr "gno.land/r/gnoswap/v1/staker" +) + +var ( + adminAddr = consts.ADMIN + adminUser = common.AddrToUser(adminAddr) + adminRealm = std.NewUserRealm(adminAddr) + + // g1v4u8getjdeskcsmjv4shgmmjta047h6lua7mup + externalCreatorAddr = testutils.TestAddress("externalCreator") + externalCreatorUser = common.AddrToUser(externalCreatorAddr) + externalCreatorRealm = std.NewUserRealm(externalCreatorAddr) + + stakerAddr = consts.STAKER_ADDR + stakerUser = common.AddrToUser(stakerAddr) + stakerRealm = std.NewCodeRealm(consts.STAKER_PATH) + + fooPath = "gno.land/r/onbloc/foo" + barPath = "gno.land/r/onbloc/bar" + bazPath = "gno.land/r/onbloc/baz" + quxPath = "gno.land/r/onbloc/qux" + oblPath = "gno.land/r/onbloc/obl" + + gnsPath = "gno.land/r/gnoswap/v1/gns" + wugnotPath = "gno.land/r/demo/wugnot" + + fee100 uint32 = 100 + fee500 uint32 = 500 + fee3000 uint32 = 3000 + + max_timeout int64 = 9999999999 + + // external incentive deposit fee + depositGnsAmount uint64 = 1_000_000_000 // 1_000 GNS + + TIMESTAMP_90DAYS int64 = 90 * 24 * 60 * 60 + TIMESTAMP_180DAYS int64 = 180 * 24 * 60 * 60 + TIMESTAMP_365DAYS int64 = 365 * 24 * 60 * 60 + + poolPath = "gno.land/r/onbloc/bar:gno.land/r/onbloc/qux:100" +) + +func main() { + testInit() + testCreatePool() + testMintBarQuxPos01() + + testCreateExternalIncentiveBar() // 90 days + testCreateExternalIncentiveQux() // 180 days + + testStakeTokenPos01() + testMakeExternalStart() // position-01 is in-range + + testEndExternalBar() // 90days external(bar) ends, 180days external(qux) still active +} + +func testInit() { + std.TestSetRealm(adminRealm) + + // short warm-up period + sr.SetWarmUp(100, 901) + sr.SetWarmUp(70, 301) + sr.SetWarmUp(50, 151) + sr.SetWarmUp(30, 1) +} + +func testCreatePool() { + std.TestSetRealm(adminRealm) + + pl.SetPoolCreationFeeByAdmin(0) + + std.TestSkipHeights(1) + pl.CreatePool( + barPath, + quxPath, + fee100, + common.TickMathGetSqrtRatioAtTick(0).ToString(), // 79228162514264337593543950337 + ) +} + +func testMintBarQuxPos01() { + std.TestSetRealm(adminRealm) + + bar.Approve(common.AddrToUser(consts.POOL_ADDR), consts.UINT64_MAX) + qux.Approve(common.AddrToUser(consts.POOL_ADDR), consts.UINT64_MAX) + + std.TestSkipHeights(1) + pn.Mint( + barPath, + quxPath, + fee100, + int32(-50), + int32(50), + "50", + "50", + "1", + "1", + max_timeout, + adminAddr, + adminAddr, + ) +} + +func testCreateExternalIncentiveBar() { + std.TestSetRealm(adminRealm) + bar.Transfer(externalCreatorUser, 9000000000) + gns.Transfer(externalCreatorUser, depositGnsAmount) + + std.TestSetRealm(externalCreatorRealm) // creator + bar.Approve(common.AddrToUser(consts.STAKER_ADDR), consts.UINT64_MAX) + gns.Approve(common.AddrToUser(consts.STAKER_ADDR), depositGnsAmount) + + std.TestSkipHeights(1) + sr.CreateExternalIncentive( + "gno.land/r/onbloc/bar:gno.land/r/onbloc/qux:100", + barPath, + "9000000000", + 1234569600, + 1234569600+TIMESTAMP_90DAYS, + ) +} + +func testCreateExternalIncentiveQux() { + std.TestSetRealm(adminRealm) + qux.Transfer(externalCreatorUser, 36500000000) + gns.Transfer(externalCreatorUser, depositGnsAmount) + + std.TestSetRealm(externalCreatorRealm) // creator + qux.Approve(common.AddrToUser(consts.STAKER_ADDR), consts.UINT64_MAX) + gns.Approve(common.AddrToUser(consts.STAKER_ADDR), depositGnsAmount) + + std.TestSkipHeights(1) + sr.CreateExternalIncentive( + "gno.land/r/onbloc/bar:gno.land/r/onbloc/qux:100", + quxPath, + "36500000000", + 1234569600, + 1234569600+TIMESTAMP_180DAYS, + ) +} + +func testStakeTokenPos01() { + std.TestSetRealm(adminRealm) + + gnft.Approve(stakerAddr, tokenIdFrom(1)) + + std.TestSkipHeights(1) + sr.StakeToken(1) +} + +func testMakeExternalStart() { + externalStartTime := int64(1234569600) + nowTime := time.Now().Unix() + timeLeft := externalStartTime - nowTime + + blockAvgTime := consts.BLOCK_GENERATION_INTERVAL + blockLeft := timeLeft / blockAvgTime + + std.TestSkipHeights(int64(blockLeft)) // skip until external bar starts + std.TestSkipHeights(10) // skip bit more to see reward calculation + + /* + TODO: (after fixing unit test) check reward + */ +} + +func testEndExternalBar() { + externalEndTime := (1234569600 + TIMESTAMP_90DAYS) + nowTime := time.Now().Unix() + timeLeft := externalEndTime - nowTime + + blockAvgTime := consts.BLOCK_GENERATION_INTERVAL + blockLeft := timeLeft / blockAvgTime + + std.TestSkipHeights(int64(blockLeft)) // skip until external bar ends + std.TestSkipHeights(10) // skip bit more to see reward calculation + + std.TestSetRealm(externalCreatorRealm) + barBalanceBeforeEnds := bar.BalanceOf(externalCreatorUser) + sr.EndExternalIncentive( + externalCreatorAddr, + "gno.land/r/onbloc/bar:gno.land/r/onbloc/qux:100", + barPath, + 1234569600, + 1234569600+TIMESTAMP_90DAYS, + 126, + ) + barBalanceAfterEnds := bar.BalanceOf(externalCreatorUser) + + /* + std.TestSkipHeights(1) + + TODO: (after fixing unit test) check + - refunded reward amount for refunedee + - refunded gns amount for refundee (depositGnsAmount refund) + + - only qux reward should be increased for position-01 + */ +} + +func tokenIdFrom(tokenId interface{}) grc721.TokenID { + if tokenId == nil { + panic("tokenId is nil") + } + + switch tokenId.(type) { + case string: + return grc721.TokenID(tokenId.(string)) + case int: + return grc721.TokenID(strconv.Itoa(tokenId.(int))) + case uint64: + return grc721.TokenID(strconv.Itoa(int(tokenId.(uint64)))) + case grc721.TokenID: + return tokenId.(grc721.TokenID) + default: + panic("unsupported tokenId type") + } +}