diff --git a/_setup/gns/gns.gno b/_setup/gns/gns.gno index ccdb48330..cf88f49d3 100644 --- a/_setup/gns/gns.gno +++ b/_setup/gns/gns.gno @@ -24,6 +24,8 @@ func init() { lp01 = std.Address("g1d3crqv2lta047h6lta047h6lta047h6lnjw069") // Liquidity Provider 01 + tr01 = std.Address("g1w3erqv2lta047h6lta047h6lta047h6lfmmdyv") // Trader 01 + stakerAddr = std.Address("g13h5s9utqcwg3a655njen0p89txusjpfrs3vxp8") ira = std.Address("g1d9exzh6lta047h6lta047h6lta047h6l8ylkpa") // INTERNAL REWARD ACCOUNT ) @@ -39,6 +41,10 @@ func init() { gns.Mint(lp01, 50000000000) gns.Approve(lp01, poolAddr, 50000000000) + // Swap + gns.Mint(tr01, 50000000000) + gns.Approve(tr01, poolAddr, 50000000000) + // default minter gns.Mint(std.Address("g1jg8mtutu9khhfwc4nxmuhcpftf0pajdhfvsqf5"), 1000000*10000) diff --git a/_test/_TEST_router_native_middle_test.gnoa b/_test/_TEST_router_native_middle_test.gnoa new file mode 100644 index 000000000..82efb84fc --- /dev/null +++ b/_test/_TEST_router_native_middle_test.gnoa @@ -0,0 +1,147 @@ +package tc + +import ( + "encoding/gjson" + "std" + "testing" + + "gno.land/p/demo/testutils" + + _ "gno.land/r/grc20_wrapper" + pl "gno.land/r/pool" + pos "gno.land/r/position" + rou "gno.land/r/router" +) + +var ( + pc01 = testutils.TestAddress("pc01") // Pool Creator 01 + lp01 = testutils.TestAddress("lp01") // Liquidity Provider 01 + tr01 = testutils.TestAddress("tr01") // Trader 01 + + poolAddr = std.DerivePkgAddr("gno.land/r/pool") + posAddr = std.DerivePkgAddr("gno.land/r/position") + routerAddr = std.DerivePkgAddr("gno.land/r/router") +) + +var ( + // Common + barPath = "gno.land/r/bar" + gnsPath = "gno.land/r/gns" + gnotPath = "gnot" + + MAX_TIMEOUT bigint = 9999999999 +) + +// debug addr +func init() { + println(pc01, "// pc01") + println(lp01, "// lp01") + println(tr01, "// tr01") + println(poolAddr, "// pool") + println(posAddr, "// pos") + println(routerAddr, "// router") +} + +func TestInitManual(t *testing.T) { + std.TestSetOrigCaller(pc01) + pl.InitManual() + std.TestSkipHeights(1) +} + +func TestCreatePool(t *testing.T) { + std.TestSetOrigCaller(pc01) + + // gns > bar + // gns:gnot + // gnot:bar + + pl.CreatePool(gnsPath, gnotPath, uint16(100), 130621891405341611593710811006) // tick = 10_000, ratio = 2.71814592682522526700950038502924144268035888671875 + pl.CreatePool(gnotPath, barPath, uint16(100), 130621891405341611593710811006) // tick = 10_000, ratio = 2.71814592682522526700950038502924144268035888671875 + // 1 gns ≈ 7.29 bar + + jsonOutput := pl.ApiGetPools() + jsonStr := gjson.Parse(jsonOutput) + shouldEQ(t, len(jsonStr.Get("response.data").Array()), 2) +} + +func TestPositionMintGnsGnot(t *testing.T) { + std.TestSetOrigCaller(lp01) + + // prepare ugnot + testBanker := std.GetBanker(std.BankerTypeRealmIssue) + testBanker.IssueCoin(std.GetOrigCaller(), "ugnot", 99999) + + // send + std.TestSetOrigSend(std.Coins{{"ugnot", 99999}}, nil) + testBanker.RemoveCoin(std.GetOrigCaller(), "ugnot", 99999) + + tokenId, liquidity, amount0, amount1 := pos.Mint(gnsPath, gnotPath, uint16(100), int32(9000), int32(11000), bigint(100_000), bigint(100_000), 0, 0, MAX_TIMEOUT) + shouldEQ(t, tokenId, uint64(1)) + shouldEQ(t, amount0 > 0, true) // 36789 + shouldEQ(t, amount1 > 0, true) // 99999 +} + +func TestPositionMintGnotBar(t *testing.T) { + std.TestSetOrigCaller(lp01) + + // prepare ugnot + testBanker := std.GetBanker(std.BankerTypeRealmIssue) + testBanker.IssueCoin(std.GetOrigCaller(), "ugnot", 99999) + + // send + std.TestSetOrigSend(std.Coins{{"ugnot", 99999}}, nil) + testBanker.RemoveCoin(std.GetOrigCaller(), "ugnot", 99999) + + tokenId, liquidity, amount0, amount1 := pos.Mint(gnotPath, barPath, uint16(100), int32(9000), int32(11000), bigint(100_000), bigint(100_000), 0, 0, MAX_TIMEOUT) + shouldEQ(t, tokenId, uint64(2)) + shouldEQ(t, amount0 > 0, true) // 99999 + shouldEQ(t, amount1 > 0, true) // 36865 +} + +func TestDrySwapRouteGnsBarExactIn(t *testing.T) { + std.TestSetOrigCaller(lp01) + + dryResult := rou.DrySwapRoute( + gnsPath, // inputToken + barPath, // outputToken + bigint(1000), // amountSpecified + "EXACT_IN", // swapType + "gno.land/r/gns:gnot:100*POOL*gnot:gno.land/r/bar:100", // strRouteArr + "100", // quoteArr + ) + shouldEQ(t, dryResult, bigint(7339)) +} + +func TestSwapRouteGnsBarExactIn(t *testing.T) { + std.TestSetOrigCaller(lp01) + + std.TestSetOrigSend(std.Coins{{"", 0}}, nil) + swapResult := rou.SwapRoute( + gnsPath, // inputToken + barPath, // outputToken + bigint(1000), // amountSpecified + "EXACT_IN", // swapType + "gno.land/r/gns:gnot:100*POOL*gnot:gno.land/r/bar:100", // strRouteArr + "100", // quoteArr + "0", // tokenAmountLimit + ) + shouldEQ(t, swapResult, bigint(7339)) +} + +/* HELPER */ +func shouldEQ(t *testing.T, got, expected interface{}) { + if got != expected { + t.Errorf("got %v, expected %v", got, expected) + } +} + +func ugnotBalance(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) +} diff --git a/_test/_TEST_router_native_wrap_unwrap_test.gnoa b/_test/_TEST_router_native_wrap_unwrap_test.gnoa new file mode 100644 index 000000000..09791266d --- /dev/null +++ b/_test/_TEST_router_native_wrap_unwrap_test.gnoa @@ -0,0 +1,198 @@ +package tc + +import ( + "encoding/gjson" + "std" + "testing" + + "gno.land/p/demo/testutils" + + _ "gno.land/r/grc20_wrapper" + pl "gno.land/r/pool" + pos "gno.land/r/position" + rou "gno.land/r/router" +) + +var ( + pc01 = testutils.TestAddress("pc01") // Pool Creator 01 + lp01 = testutils.TestAddress("lp01") // Liquidity Provider 01 + tr01 = testutils.TestAddress("tr01") // Trader 01 + + poolAddr = std.DerivePkgAddr("gno.land/r/pool") + posAddr = std.DerivePkgAddr("gno.land/r/position") + routerAddr = std.DerivePkgAddr("gno.land/r/router") +) + +var ( + // Common + fooPath = "gno.land/r/foo" + barPath = "gno.land/r/bar" + gnsPath = "gno.land/r/gns" + quxPath = "gno.land/r/qux" + gnotPath = "gnot" + + MAX_TIMEOUT bigint = 9999999999 +) + +// // debug addr +// func init() { +// println(pc01, "// pc01") +// println(lp01, "// lp01") +// println(tr01, "// tr01") +// println(poolAddr, "// pool") +// println(posAddr, "// pos") +// println(routerAddr, "// router") +// } + +func TestInitManual(t *testing.T) { + std.TestSetOrigCaller(pc01) + pl.InitManual() + std.TestSkipHeights(1) +} + +func TestCreatePool(t *testing.T) { + std.TestSetOrigCaller(pc01) + + // WRAP + // gns > bar + // gns:gnot + // gnot:bar + pl.CreatePool(gnsPath, gnotPath, uint16(100), 130621891405341611593710811006) // tick = 10_000, ratio = 2.71814592682522526700950038502924144268035888671875 + pl.CreatePool(gnotPath, barPath, uint16(100), 130621891405341611593710811006) // tick = 10_000, ratio = 2.71814592682522526700950038502924144268035888671875 + + // UNWRAP + // foo > qux + // qux > ugnot + pl.CreatePool(fooPath, quxPath, uint16(100), 130621891405341611593710811006) // tick = 10_000, ratio = 2.71814592682522526700950038502924144268035888671875 + pl.CreatePool(quxPath, gnotPath, uint16(100), 130621891405341611593710811006) // tick = 10_000, ratio = 2.71814592682522526700950038502924144268035888671875 + // 1 foo ≈ 7.29 gnot + + jsonOutput := pl.ApiGetPools() + jsonStr := gjson.Parse(jsonOutput) + shouldEQ(t, len(jsonStr.Get("response.data").Array()), 4) +} + +func TestPositionMintGnsGnot(t *testing.T) { + std.TestSetOrigCaller(lp01) + + // prepare ugnot + testBanker := std.GetBanker(std.BankerTypeRealmIssue) + testBanker.IssueCoin(std.GetOrigCaller(), "ugnot", 99999) + + // send + std.TestSetOrigSend(std.Coins{{"ugnot", 99999}}, nil) + testBanker.RemoveCoin(std.GetOrigCaller(), "ugnot", 99999) + + tokenId, liquidity, amount0, amount1 := pos.Mint(gnsPath, gnotPath, uint16(100), int32(9000), int32(11000), bigint(100_000), bigint(100_000), 0, 0, MAX_TIMEOUT) + shouldEQ(t, tokenId, uint64(1)) + shouldEQ(t, amount0 > 0, true) // 36789 + shouldEQ(t, amount1 > 0, true) // 99999 +} + +func TestPositionMintGnotBar(t *testing.T) { + std.TestSetOrigCaller(lp01) + + // prepare ugnot + testBanker := std.GetBanker(std.BankerTypeRealmIssue) + testBanker.IssueCoin(std.GetOrigCaller(), "ugnot", 99999) + + // send + std.TestSetOrigSend(std.Coins{{"ugnot", 99999}}, nil) + testBanker.RemoveCoin(std.GetOrigCaller(), "ugnot", 99999) + + tokenId, liquidity, amount0, amount1 := pos.Mint(gnotPath, barPath, uint16(100), int32(9000), int32(11000), bigint(100_000), bigint(100_000), 0, 0, MAX_TIMEOUT) + shouldEQ(t, tokenId, uint64(2)) + shouldEQ(t, amount0 > 0, true) // 99999 + shouldEQ(t, amount1 > 0, true) // 36865 +} + +func TestPositionMintFooQux(t *testing.T) { + std.TestSetOrigCaller(lp01) + + tokenId, liquidity, amount0, amount1 := pos.Mint(fooPath, quxPath, uint16(100), int32(9000), int32(11000), bigint(100_000), bigint(100_000), 0, 0, MAX_TIMEOUT) + shouldEQ(t, tokenId, uint64(3)) + shouldEQ(t, amount0 > 0, true) // 99999 + shouldEQ(t, amount1 > 0, true) // 36865 +} + +func TestPositionMintQuxGnot(t *testing.T) { + std.TestSetOrigCaller(lp01) + + // prepare ugnot + testBanker := std.GetBanker(std.BankerTypeRealmIssue) + testBanker.IssueCoin(std.GetOrigCaller(), "ugnot", 99999) + + // send + std.TestSetOrigSend(std.Coins{{"ugnot", 99999}}, nil) + testBanker.RemoveCoin(std.GetOrigCaller(), "ugnot", 99999) + + tokenId, liquidity, amount0, amount1 := pos.Mint(quxPath, gnotPath, uint16(100), int32(9000), int32(11000), bigint(100_000), bigint(100_000), 0, 0, MAX_TIMEOUT) + shouldEQ(t, tokenId, uint64(4)) + shouldEQ(t, amount0 > 0, true) // 99999 + shouldEQ(t, amount1 > 0, true) // 36865 +} + +// Wrap TEST +func TestSwapRouteGnsBarExactIn(t *testing.T) { + std.TestSetOrigCaller(tr01) + + trOldUgnot := ugnotBalance(tr01) + shouldEQ(t, trOldUgnot, uint64(0)) + + std.TestSetOrigSend(std.Coins{{"", 0}}, nil) + swapResult := rou.SwapRoute( + gnsPath, // inputToken + barPath, // outputToken + bigint(1000), // amountSpecified + "EXACT_IN", // swapType + "gno.land/r/gns:gnot:100*POOL*gnot:gno.land/r/bar:100", // strRouteArr + "100", // quoteArr + "0", // tokenAmountLimit + ) + + trNewUgnot := ugnotBalance(tr01) + shouldEQ(t, trNewUgnot, uint64(0)) + + shouldEQ(t, swapResult, bigint(7339)) +} + +// UnWrap TEST +func TestSwapRouteFooGnotExactIn(t *testing.T) { + std.TestSetOrigCaller(tr01) + + trOldUgnot := ugnotBalance(tr01) + shouldEQ(t, trOldUgnot, uint64(0)) + + std.TestSetOrigSend(std.Coins{{"", 0}}, nil) + swapResult := rou.SwapRoute( + fooPath, // inputToken + gnotPath, // outputToken + bigint(1000), // amountSpecified + "EXACT_IN", // swapType + "gno.land/r/foo:gno.land/r/qux:100*POOL*gno.land/r/qux:gnot:100", // strRouteArr + "100", // quoteArr + "0", // tokenAmountLimit + ) + shouldEQ(t, swapResult, bigint(7339)) + + trNewUgnot := ugnotBalance(tr01) + shouldEQ(t, trNewUgnot, uint64(7339)) +} + +/* HELPER */ +func shouldEQ(t *testing.T, got, expected interface{}) { + if got != expected { + t.Errorf("got %v, expected %v", got, expected) + } +} + +func ugnotBalance(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) +} diff --git a/pool/pool.gno b/pool/pool.gno index 30f687aa4..40a085d07 100644 --- a/pool/pool.gno +++ b/pool/pool.gno @@ -152,17 +152,17 @@ func Collect( requireUnsigned(amount1, ufmt.Sprintf("[POOL] pool.gno__Collect() || amount1(%d) >= 0", amount1)) require(pool.balances.token0 >= amount0, ufmt.Sprintf("[POOL] pool.gno__Collect() || pool.balances.token0(%d) >= amount0(%d)", pool.balances.token0, amount0)) - if pool.token0Path == "gno.land/r/wugnot" { + if pool.token0Path == GNOT { // UNWRAP if native(ugnot) is being collect - unWrap(pool.token0Path, uint64(amount0)) + unWrap(pool.token0Path, recipient, uint64(amount0)) } else { transferByRegisterCall(pool.token0Path, recipient, uint64(amount0)) } require(pool.balances.token1 >= amount1, ufmt.Sprintf("[POOL] pool.gno__Collect() || pool.balances.token1(%d) >= amount1(%d)", pool.balances.token1, amount1)) - if pool.token1Path == "gno.land/r/wugnot" { + if pool.token1Path == GNOT { // UNWRAP if native(ugnot) is being collect - unWrap(pool.token1Path, uint64(amount1)) + unWrap(pool.token1Path, recipient, uint64(amount1)) } else { transferByRegisterCall(pool.token1Path, recipient, uint64(amount1)) } @@ -415,7 +415,8 @@ func Swap( panic("[POOL] pool.gno__Swap() || transferByRegisterCall(pool.token1Path, recipient, uint64(-amount1)) failed") } pool.balances.token1 += amount1 - unWrap(pool.token1Path, uint64(-amount1)) + + unWrap(pool.token1Path, recipient, uint64(-amount1)) } require(pool.balances.token1 >= 0, ufmt.Sprintf("[POOL] pool.gno__Swap() || pool.balances.token1(%d) >= 0__#1", pool.balances.token1)) @@ -445,7 +446,7 @@ func Swap( if !ok { panic("[POOL] pool.gno__Swap() || transferByRegisterCall(pool.token0Path, recipient, uint64(-amount0)) failed") } - unWrap(pool.token0Path, uint64(-amount0)) + unWrap(pool.token0Path, recipient, uint64(-amount0)) pool.balances.token0 += amount0 } require(pool.balances.token0 >= 0, ufmt.Sprintf("[POOL] pool.gno__Swap() || pool.balances.token0(%d) >= 0__#2", pool.balances.token0)) diff --git a/pool/wrap_unwrap.gno b/pool/wrap_unwrap.gno index 29a9980ba..1bf0b3d30 100644 --- a/pool/wrap_unwrap.gno +++ b/pool/wrap_unwrap.gno @@ -12,7 +12,12 @@ func wrap(tokenPath string) { return } - caller := std.GetOrigCaller() // GOTTA BE USER for POOL.MINT + prev := std.PrevRealm().PkgPath() + if tokenPath == GNOT && prev == "gno.land/r/router" { + return + } + + caller := std.GetOrigCaller() sentCoins := std.GetOrigSend() if len(sentCoins) != 1 { @@ -28,12 +33,17 @@ func wrap(tokenPath string) { wugnot.Wrap(a2u(caller), uint64(sentCoin.Amount)) } -func unWrap(tokenPath string, amount uint64) { +func unWrap(tokenPath string, recipient std.Address, amount uint64) { if tokenPath != GNOT { return } - caller := std.GetOrigCaller() // GOTTA BE USER for POOL.MINT + if tokenPath == GNOT && recipient == ADDR_ROUTER { + return + } + + // Collect + caller := std.GetOrigCaller() refund := std.Coins{ std.Coin{ diff --git a/router/_TEST_router_api_getter_test.gno b/router/_TEST_router_api_getter_test.gnoa similarity index 100% rename from router/_TEST_router_api_getter_test.gno rename to router/_TEST_router_api_getter_test.gnoa diff --git a/router/_TEST_router_swap_route_1route_3hop_native_test.gnoa b/router/_TEST_router_swap_route_1route_2hop_native_in_out_test.gnoa similarity index 100% rename from router/_TEST_router_swap_route_1route_3hop_native_test.gnoa rename to router/_TEST_router_swap_route_1route_2hop_native_in_out_test.gnoa diff --git a/router/_TEST_router_swap_route_1route_3hop_native_middle_test.gno b/router/_TEST_router_swap_route_1route_3hop_native_middle_test.gno new file mode 100644 index 000000000..a1547ed7b --- /dev/null +++ b/router/_TEST_router_swap_route_1route_3hop_native_middle_test.gno @@ -0,0 +1,136 @@ +package router + +import ( + "encoding/gjson" + "std" + "testing" + + "gno.land/p/demo/testutils" + + _ "gno.land/r/grc20_wrapper" + pl "gno.land/r/pool" + pos "gno.land/r/position" +) + +var ( + pc01 = testutils.TestAddress("pc01") // Pool Creator 01 + lp01 = testutils.TestAddress("lp01") // Liquidity Provider 01 + tr01 = testutils.TestAddress("tr01") // Trader 01 + + poolAddr = std.DerivePkgAddr("gno.land/r/pool") + posAddr = std.DerivePkgAddr("gno.land/r/position") + routerAddr = std.DerivePkgAddr("gno.land/r/router") +) + +var ( + // Common + barPath = "gno.land/r/bar" + gnsPath = "gno.land/r/gns" + gnotPath = "gnot" + + MAX_TIMEOUT bigint = 9999999999 +) + +// debug addr +func init() { + println(pc01, "// pc01") + println(lp01, "// lp01") + println(tr01, "// tr01") + println(poolAddr, "// pool") + println(posAddr, "// pos") + println(routerAddr, "// router") +} + +func TestInitManual(t *testing.T) { + std.TestSetOrigCaller(pc01) + pl.InitManual() + std.TestSkipHeights(1) +} + +func TestCreatePool(t *testing.T) { + std.TestSetOrigCaller(pc01) + + // gns > bar + + // gns:gnot + // gnot:bar + + pl.CreatePool(gnsPath, gnotPath, uint16(100), 130621891405341611593710811006) // tick = 10_000, ratio = 2.71814592682522526700950038502924144268035888671875 + pl.CreatePool(gnotPath, barPath, uint16(100), 130621891405341611593710811006) // tick = 10_000, ratio = 2.71814592682522526700950038502924144268035888671875 + // 1 gns ≈ 7.29 bar + + jsonOutput := pl.ApiGetPools() + jsonStr := gjson.Parse(jsonOutput) + shouldEQ(t, len(jsonStr.Get("response.data").Array()), 2) +} + +func TestPositionMintGnsGnot(t *testing.T) { + std.TestSetOrigCaller(lp01) + + // prepare ugnot + testBanker := std.GetBanker(std.BankerTypeRealmIssue) + testBanker.IssueCoin(std.GetOrigCaller(), "ugnot", 99999) + + // send + std.TestSetOrigSend(std.Coins{{"ugnot", 99999}}, nil) + testBanker.RemoveCoin(std.GetOrigCaller(), "ugnot", 99999) + + tokenId, liquidity, amount0, amount1 := pos.Mint(gnsPath, gnotPath, uint16(100), int32(9000), int32(11000), bigint(100_000), bigint(100_000), 0, 0, MAX_TIMEOUT) + shouldEQ(t, tokenId, uint64(1)) + shouldEQ(t, amount0 > 0, true) // 36789 + shouldEQ(t, amount1 > 0, true) // 99999 +} + +func TestPositionMintGnotBar(t *testing.T) { + std.TestSetOrigCaller(lp01) + + // prepare ugnot + testBanker := std.GetBanker(std.BankerTypeRealmIssue) + testBanker.IssueCoin(std.GetOrigCaller(), "ugnot", 99999) + + // send + std.TestSetOrigSend(std.Coins{{"ugnot", 99999}}, nil) + testBanker.RemoveCoin(std.GetOrigCaller(), "ugnot", 99999) + + tokenId, liquidity, amount0, amount1 := pos.Mint(gnotPath, barPath, uint16(100), int32(9000), int32(11000), bigint(100_000), bigint(100_000), 0, 0, MAX_TIMEOUT) + shouldEQ(t, tokenId, uint64(2)) + shouldEQ(t, amount0 > 0, true) // 99999 + shouldEQ(t, amount1 > 0, true) // 36865 +} + +func TestDrySwapRouteBarGnotExactIn(t *testing.T) { + std.TestSetOrigCaller(lp01) + + dryResult := DrySwapRoute( + gnsPath, // inputToken + barPath, // outputToken + bigint(1000), // amountSpecified + "EXACT_IN", // swapType + "gno.land/r/gns:gnot:100*POOL*gnot:gno.land/r/bar:100", // strRouteArr + "100", // quoteArr + ) + shouldEQ(t, dryResult, bigint(7339)) +} + +func TestSwapRouteBarGnotExactIn(t *testing.T) { + std.TestSetOrigCaller(lp01) + + std.TestSetOrigSend(std.Coins{{"", 0}}, nil) + swapResult := SwapRoute( + gnsPath, // inputToken + barPath, // outputToken + bigint(1000), // amountSpecified + "EXACT_IN", // swapType + "gno.land/r/gns:gnot:100*POOL*gnot:gno.land/r/bar:100", // strRouteArr + "100", // quoteArr + "0", // tokenAmountLimit + ) + shouldEQ(t, swapResult, bigint(7339)) +} + +/* HELPER */ +func shouldEQ(t *testing.T, got, expected interface{}) { + if got != expected { + t.Errorf("got %v, expected %v", got, expected) + } +}