From 7b82a43a72fdd79213650b52280d58274bde3c50 Mon Sep 17 00:00:00 2001 From: n3wbie Date: Wed, 15 May 2024 22:40:25 +0900 Subject: [PATCH] GSW-1069 fix: one click staking for native ugnot coin (wrapping issue) --- .../_TEST_0_INIT_VARIABLE_AND_HELPER_test.gno | 11 ++ .../_TEST_staker_mint_and_stake_test.gn | 92 ----------- .../_TEST_staker_mint_and_stake_test.gno | 156 ++++++++++++++++++ staker/mint_stake.gno | 21 +++ staker/utils.gno | 10 ++ 5 files changed, 198 insertions(+), 92 deletions(-) delete mode 100644 staker/_TEST_/_TEST_staker_mint_and_stake_test.gn create mode 100644 staker/_TEST_/_TEST_staker_mint_and_stake_test.gno diff --git a/staker/_TEST_/_TEST_0_INIT_VARIABLE_AND_HELPER_test.gno b/staker/_TEST_/_TEST_0_INIT_VARIABLE_AND_HELPER_test.gno index 9a28b234..f0b3ee74 100644 --- a/staker/_TEST_/_TEST_0_INIT_VARIABLE_AND_HELPER_test.gno +++ b/staker/_TEST_/_TEST_0_INIT_VARIABLE_AND_HELPER_test.gno @@ -27,6 +27,17 @@ var ( ) /* HELPER */ +func ugnotBalanceOf(addr std.Address) uint64 { + testBanker := std.GetBanker(std.BankerTypeRealmIssue) + + coins := testBanker.GetCoins(addr) + if len(coins) == 0 { + return 0 + } + + return uint64(testBanker.GetCoins(addr)[0].Amount) +} + func shouldEQ(t *testing.T, got, expected interface{}) { if got != expected { t.Errorf("got %v, expected %v", got, expected) diff --git a/staker/_TEST_/_TEST_staker_mint_and_stake_test.gn b/staker/_TEST_/_TEST_staker_mint_and_stake_test.gn deleted file mode 100644 index ed19a6e0..00000000 --- a/staker/_TEST_/_TEST_staker_mint_and_stake_test.gn +++ /dev/null @@ -1,92 +0,0 @@ -package staker - -import ( - "std" - "testing" - - pl "gno.land/r/demo/pool" - pn "gno.land/r/demo/position" - - "gno.land/r/demo/bar" - "gno.land/r/demo/gnft" - "gno.land/r/demo/gns" - "gno.land/r/demo/qux" - - "gno.land/r/demo/gnoswap/consts" -) - -func init() { - // init pool tiers - // tier 1 - poolTiers["gno.land/r/demo/bar:gno.land/r/demo/qux:500"] = 1 // DEV - - // tier 2 - poolTiers["GNS/USDT_500"] = 2 - poolTiers["ATOM/GNS_500"] = 2 - - // tier 3 - poolTiers["ATOM/GNOT_500"] = 3 - poolTiers["ATOM/USDT_500"] = 3 - poolTiers["ATOM/WETH_500"] = 3 -} - -func TestPoolInitCreatePool(t *testing.T) { - std.TestSetPrevAddr(test1) - for i := 0; i < (5 * 1); i++ { - gns.Faucet() - } - gns.Approve(a2u(consts.POOL_ADDR), consts.POOL_CREATION_FEE) - std.TestSkipHeights(1) - - pl.CreatePool(barPath, quxPath, 500, "130621891405341611593710811006") // tick 10_000 ≈ x2.7 - std.TestSkipHeights(1) -} - -func TestMintAndStake(t *testing.T) { - std.TestSetPrevAddr(test1) - bar.Approve(a2u(consts.POOL_ADDR), consts.UINT64_MAX) - qux.Approve(a2u(consts.POOL_ADDR), consts.UINT64_MAX) - std.TestSkipHeights(2) - - lpTokenId, liquidity, amount0, amount1, poolPath := MintAndStake( - barPath, // token0 - quxPath, // token1 - fee500, // fee - int32(9000), // tickLower - int32(11000), // tickUpper - "1000", // amount0Desired - "1000", // amount1Desired - "1", // amount0Min - "1", // amount1Min - max_timeout, - ) - std.TestSkipHeights(1) -} - -func TestPositionCollectFee(t *testing.T) { - std.TestSetPrevAddr(test1) - pn.CollectFee(1) // lpTokenId - std.TestSkipHeights(1) -} - -func TestCollectReward(t *testing.T) { - // internal reward distribution - std.TestSetPrevAddr(consts.INTERNAL_REWARD_ACCOUNT) - gns.Approve(a2u(consts.STAKER_ADDR), consts.UINT64_MAX) - - std.TestSetPrevAddr(test1) - CollectReward(1) // lpTokenId - std.TestSkipHeights(1) -} - -func TestUnstakeToken(t *testing.T) { - ownerOfLp1 := gnft.OwnerOf(tid(1)) - shouldEQ(t, ownerOfLp1, a2u(consts.STAKER_ADDR)) - - std.TestSetPrevAddr(test1) - UnstakeToken(1) // lpTokenId - std.TestSkipHeights(1) - - ownerOfLp1 = gnft.OwnerOf(tid(1)) - shouldEQ(t, ownerOfLp1, a2u(test1)) -} diff --git a/staker/_TEST_/_TEST_staker_mint_and_stake_test.gno b/staker/_TEST_/_TEST_staker_mint_and_stake_test.gno new file mode 100644 index 00000000..fc384250 --- /dev/null +++ b/staker/_TEST_/_TEST_staker_mint_and_stake_test.gno @@ -0,0 +1,156 @@ +package staker + +import ( + "std" + "testing" + + pl "gno.land/r/demo/pool" + + "gno.land/r/demo/bar" + "gno.land/r/demo/gns" + "gno.land/r/demo/qux" + "gno.land/r/demo/wugnot" + + "gno.land/r/demo/gnoswap/consts" +) + +func init() { + // init pool tiers + // tier 1 + poolTiers["gno.land/r/demo/bar:gno.land/r/demo/qux:500"] = 1 // DEV + + // tier 2 + poolTiers["GNS/USDT_500"] = 2 + poolTiers["ATOM/GNS_500"] = 2 + + // tier 3 + poolTiers["ATOM/GNOT_500"] = 3 + poolTiers["ATOM/USDT_500"] = 3 + poolTiers["ATOM/WETH_500"] = 3 +} + +func TestPoolInitCreatePool(t *testing.T) { + std.TestSetPrevAddr(gsa) + + gns.Approve(a2u(consts.POOL_ADDR), consts.POOL_CREATION_FEE) + std.TestSkipHeights(1) + + pl.CreatePool(barPath, quxPath, 500, "130621891405341611593710811006") // tick 10_000 ≈ x2.7 + // --- event: {GNOSWAP gno.land/r/demo/pool CreatePool [{m_origCaller g13f63ua8uhmuf9mgc0x8zfz04yrsaqh7j78vcgq} {m_prevRealm } {p_poolPath gno.land/r/demo/bar:gno.land/r/demo/qux:500}]} + + pl.CreatePool(consts.GNOT, consts.GNS_PATH, 500, "130621891405341611593710811006") // tick 10_000 ≈ x2.7 + // --- event: {GNOSWAP gno.land/r/demo/pool CreatePool [{m_origCaller g13f63ua8uhmuf9mgc0x8zfz04yrsaqh7j78vcgq} {m_prevRealm } {p_poolPath gno.land/r/demo/gns:gno.land/r/demo/wugnot:500}]} + + std.TestSkipHeights(1) +} + +func TestMintAndStakeGRC20Pair(t *testing.T) { + std.TestSetPrevAddr(gsa) + bar.Approve(a2u(consts.POOL_ADDR), consts.UINT64_MAX) + qux.Approve(a2u(consts.POOL_ADDR), consts.UINT64_MAX) + std.TestSkipHeights(2) + + lpTokenId, liquidity, amount0, amount1, poolPath := MintAndStake( + barPath, // token0 + quxPath, // token1 + fee500, // fee + int32(9000), // tickLower + int32(11000), // tickUpper + "1000", // amount0Desired + "1000", // amount1Desired + "1", // amount0Min + "1", // amount1Min + max_timeout, + ) + // event: {GNOSWAP gno.land/r/demo/position Mint [{m_origCaller g13f63ua8uhmuf9mgc0x8zfz04yrsaqh7j78vcgq} {m_prevRealm gno.land/r/demo/staker} {p_poolPath gno.land/r/demo/bar:gno.land/r/demo/qux:500} {p_tickLower 9000} {p_tickUpper 11000} {tokenId 1} {liquidity 12437} {amount0 368} {amount1 1000}]} + // event: {GNOSWAP gno.land/r/demo/staker StakeToken [{p_tokenId 1} {poolPath gno.land/r/demo/bar:gno.land/r/demo/qux:500} {amount0 368} {amount1 1000}]} + shouldEQ(t, lpTokenId, uint64(1)) + + std.TestSkipHeights(1) +} + +func TestMintAndStakeNative(t *testing.T) { + std.TestSetPrevAddr(gsa) + gns.Approve(a2u(consts.POOL_ADDR), consts.UINT64_MAX) + wugnot.Approve(a2u(consts.POOL_ADDR), consts.UINT64_MAX) + wugnot.Approve(a2u(consts.POSITION_ADDR), consts.UINT64_MAX) + std.TestSkipHeights(2) + + // prepare 100005ugnot (5 for refund test) + testBanker := std.GetBanker(std.BankerTypeRealmIssue) + testBanker.IssueCoin(gsa, "ugnot", 100005) + + // simulate transfer & decrase + gsaNativeBalance := ugnotBalanceOf(gsa) + shouldEQ(t, gsaNativeBalance, 100005) + + std.TestSetOrigSend(std.Coins{{"ugnot", 100005}}, nil) + testBanker.RemoveCoin(std.GetOrigCaller(), "ugnot", -100005) + + gsaNativeBalance = ugnotBalanceOf(gsa) + shouldEQ(t, gsaNativeBalance, 0) + + gsaOldWugnotBalance := wugnot.BalanceOf(a2u(gsa)) + shouldEQ(t, gsaOldWugnotBalance, 0) + + lpTokenId, liquidity, amount0, amount1, poolPath := MintAndStake( + consts.GNOT, // token0 + consts.GNS_PATH, // token1 + fee500, // fee + int32(9000), // tickLower + int32(11000), // tickUpper + "100000", // amount0Desired + "100000", // amount1Desired + "1", // amount0Min + "1", // amount1Min + max_timeout, + ) + // event: {GNOSWAP gno.land/r/demo/position Mint [{m_origCaller g13f63ua8uhmuf9mgc0x8zfz04yrsaqh7j78vcgq} {m_prevRealm gno.land/r/demo/staker} {p_poolPath gno.land/r/demo/gns:gno.land/r/demo/wugnot:500} {p_tickLower -11000} {p_tickUpper -9000} {tokenId 2} {liquidity 1243732} {amount0 100000} {amount1 36790}]} + // event: {GNOSWAP gno.land/r/demo/staker StakeToken [{p_tokenId 2} {poolPath gno.land/r/demo/gns:gno.land/r/demo/wugnot:500} {amount0 100000} {amount1 36790}]} + + shouldEQ(t, lpTokenId, uint64(2)) + std.TestSkipHeights(1) + + // SPEND ALL WUGNOT + newOldWugnotBalance := wugnot.BalanceOf(a2u(gsa)) + shouldEQ(t, gsaOldWugnotBalance, 0) + + gsaNativeBalance = ugnotBalanceOf(gsa) + shouldEQ(t, gsaNativeBalance, 63215) + // 1. 100005 ugnot sent + // 2. 100005 ugnot wrapped to wugnot + // 3. 36790 wugnot spent to mint (amount1) + // 4. refund 100005 - 36790 = 63215 + +} + +/* +func TestPositionCollectFee(t *testing.T) { + std.TestSetPrevAddr(gsa) + pn.CollectFee(1) // lpTokenId + std.TestSkipHeights(1) +} + +func TestCollectReward(t *testing.T) { + // internal reward distribution + std.TestSetPrevAddr(consts.INTERNAL_REWARD_ACCOUNT) + gns.Approve(a2u(consts.STAKER_ADDR), consts.UINT64_MAX) + + std.TestSetPrevAddr(gsa) + CollectReward(1) // lpTokenId + std.TestSkipHeights(1) +} + +func TestUnstakeToken(t *testing.T) { + ownerOfLp1 := gnft.OwnerOf(tid(1)) + shouldEQ(t, ownerOfLp1, a2u(consts.STAKER_ADDR)) + + std.TestSetPrevAddr(gsa) + UnstakeToken(1) // lpTokenId + std.TestSkipHeights(1) + + ownerOfLp1 = gnft.OwnerOf(tid(1)) + shouldEQ(t, ownerOfLp1, a2u(gsa)) +} + +*/ diff --git a/staker/mint_stake.gno b/staker/mint_stake.gno index 7645b4c1..bbbd5169 100644 --- a/staker/mint_stake.gno +++ b/staker/mint_stake.gno @@ -1,8 +1,12 @@ package staker import ( + "std" + pn "gno.land/r/demo/position" + "gno.land/p/demo/ufmt" + "gno.land/r/demo/gnoswap/consts" ) @@ -20,6 +24,23 @@ func MintAndStake( amount1Min string, // *u256.Uint deadline int64, ) (uint64, string, string, string, string) { // tokenId, liquidity, amount0, amount1, poolPath ( *u256.Uint x3) + + // if one click native + if token0 == consts.GNOT || token1 == consts.GNOT { + // check sent ugnot + sent := std.GetOrigSend() + ugnotSent := uint64(sent.AmountOf("ugnot")) + + // not enough ugnot sent + if ugnotSent < consts.UGNOT_MINT_DEPOSIT_TO_WRAP { + panic(ufmt.Sprintf("[STAKER] mint_stake.gno__MintAndStake() || too less(%d) ugnot sent (minimum:%d)", ugnotSent, consts.UGNOT_MINT_DEPOSIT_TO_WRAP)) + } + + // send it over to position to wrap + banker := std.GetBanker(std.BankerTypeRealmSend) + banker.SendCoins(consts.STAKER_ADDR, consts.POSITION_ADDR, std.Coins{{"ugnot", int64(ugnotSent)}}) + } + tokenId, liquidity, amount0, amount1 := pn.Mint( token0, token1, diff --git a/staker/utils.gno b/staker/utils.gno index ce01a082..2a305fb5 100644 --- a/staker/utils.gno +++ b/staker/utils.gno @@ -81,6 +81,16 @@ func uint64ToStr(i uint64) string { return strconv.FormatInt(int64(i), 10) } +func strToUint64(s string) uint64 { + i, err := strconv.Atoi(s) + + if err != nil { + panic(ufmt.Sprintf("[STAKER] utils.gno__strToUint64() || failed to convert string(%s) to uint64", s)) + } + + return uint64(i) +} + func boolToStr(b bool) string { if b { return "true"