diff --git a/_setup/bar/bar.gno b/_setup/bar/bar.gno index 42bad433..c3d263d5 100644 --- a/_setup/bar/bar.gno +++ b/_setup/bar/bar.gno @@ -31,22 +31,17 @@ func init() { posAddr = std.DerivePkgAddr("gno.land/r/position") ) - bar.Mint(lp01, 50000000) - bar.Mint(lp02, 50000000) - bar.Mint(lp03, 50000000) - bar.Mint(tr01, 50000000) - - bar.Approve(lp01, poolAddr, 50000000) - // bar.Approve(lp02, poolAddr, 50000000) - // bar.Approve(lp03, poolAddr, 50000000) - bar.Approve(tr01, poolAddr, 50000000) - - // bar.Approve(lp01, lp01, 50000000) - // bar.Approve(lp02, lp02, 50000000) - // bar.Approve(lp03, lp03, 50000000) - // bar.Approve(tr01, tr01, 50000000) - - // bar.Approve(poolAddr, registryAddr, 50000000) + bar.Mint(lp01, 50000000000) + bar.Mint(lp02, 50000000000) + bar.Mint(lp03, 50000000000) + bar.Mint(tr01, 50000000000) + + bar.Approve(lp01, poolAddr, 50000000000) + bar.Approve(lp02, poolAddr, 50000000000) + bar.Approve(lp03, poolAddr, 50000000000) + bar.Approve(tr01, poolAddr, 50000000000) + + // bar.Approve(poolAddr, registryAddr, 50000000000) } // method proxies as public functions. @@ -117,7 +112,7 @@ func FaucetL() { // FIXME: add limits? // FIXME: add payment in gnot? caller := std.PrevRealm().Addr() - bar.Mint(caller, 50000000000) // 50_000_000_000 + bar.Mint(caller, 50000000000000) // 50_000_000_000 } // administration. diff --git a/_setup/baz/baz.gno b/_setup/baz/baz.gno index 46b56794..f2491329 100644 --- a/_setup/baz/baz.gno +++ b/_setup/baz/baz.gno @@ -31,22 +31,17 @@ func init() { posAddr = std.DerivePkgAddr("gno.land/r/position") ) - baz.Mint(lp01, 50000000) - baz.Mint(lp02, 50000000) - baz.Mint(lp03, 50000000) - baz.Mint(tr01, 50000000) - - baz.Approve(lp01, poolAddr, 50000000) - // baz.Approve(lp02, poolAddr, 50000000) - // baz.Approve(lp03, poolAddr, 50000000) - baz.Approve(tr01, poolAddr, 50000000) - - // baz.Approve(lp01, lp01, 50000000) - // baz.Approve(lp02, lp02, 50000000) - // baz.Approve(lp03, lp03, 50000000) - // baz.Approve(tr01, tr01, 50000000) - - // baz.Approve(posAddr, poolAddr, 50000000) + baz.Mint(lp01, 50000000000) + baz.Mint(lp02, 50000000000) + baz.Mint(lp03, 50000000000) + baz.Mint(tr01, 50000000000) + + baz.Approve(lp01, poolAddr, 50000000000) + baz.Approve(lp02, poolAddr, 50000000000) + baz.Approve(lp03, poolAddr, 50000000000) + baz.Approve(tr01, poolAddr, 50000000000) + + baz.Approve(posAddr, poolAddr, 50000000000) } // method proxies as public functions. @@ -121,7 +116,7 @@ func FaucetL() { // FIXME: add payment in gnot? // caller := std.GetOrigCaller() caller := std.PrevRealm().Addr() - baz.Mint(caller, 50000000000) // 50_000_000_000 + baz.Mint(caller, 50000000000000) // 50_000_000_000 } // administration. diff --git a/_setup/foo/foo.gno b/_setup/foo/foo.gno index 4a9aa981..9f3dca40 100644 --- a/_setup/foo/foo.gno +++ b/_setup/foo/foo.gno @@ -32,22 +32,17 @@ func init() { posAddr = std.DerivePkgAddr("gno.land/r/position") ) - foo.Mint(lp01, 50000000) - foo.Mint(lp02, 50000000) - foo.Mint(lp03, 50000000) - foo.Mint(tr01, 50000000) - - foo.Approve(lp01, poolAddr, 50000000) - // foo.Approve(lp02, poolAddr, 50000000) - // foo.Approve(lp03, poolAddr, 50000000) - foo.Approve(tr01, poolAddr, 50000000) - - // foo.Approve(lp01, lp01, 50000000) - // foo.Approve(lp02, lp02, 50000000) - // foo.Approve(lp03, lp03, 50000000) - // foo.Approve(tr01, tr01, 50000000) - - // foo.Approve(posAddr, poolAddr, 50000000) + foo.Mint(lp01, 50000000000) + foo.Mint(lp02, 50000000000) + foo.Mint(lp03, 50000000000) + foo.Mint(tr01, 50000000000) + + foo.Approve(lp01, poolAddr, 50000000000) + foo.Approve(lp02, poolAddr, 50000000000) + foo.Approve(lp03, poolAddr, 50000000000) + foo.Approve(tr01, poolAddr, 50000000000) + + // foo.Approve(posAddr, poolAddr, 50000000000) } // method proxies as public functions. diff --git a/_setup/gnos/gno.mod b/_setup/gnos/gno.mod deleted file mode 100644 index 3cae0054..00000000 --- a/_setup/gnos/gno.mod +++ /dev/null @@ -1 +0,0 @@ -module gno.land/r/gnos \ No newline at end of file diff --git a/_setup/gns/gno.mod b/_setup/gns/gno.mod new file mode 100644 index 00000000..0ad40983 --- /dev/null +++ b/_setup/gns/gno.mod @@ -0,0 +1 @@ +module gno.land/r/gns \ No newline at end of file diff --git a/_setup/gnos/gnos.gno b/_setup/gns/gns.gno similarity index 78% rename from _setup/gnos/gnos.gno rename to _setup/gns/gns.gno index 636c7b1a..4ca4fb8a 100644 --- a/_setup/gnos/gnos.gno +++ b/_setup/gns/gns.gno @@ -1,4 +1,4 @@ -package gnos +package gns import ( "std" @@ -10,12 +10,12 @@ import ( ) var ( - gnos *grc20.AdminToken + gns *grc20.AdminToken admin []string ) func init() { - gnos = grc20.NewAdminToken("Gnoswap Shares", "GNOS", 4) + gns = grc20.NewAdminToken("Gnoswap Shares", "GNOS", 4) // for swap > staker var ( @@ -23,8 +23,8 @@ func init() { poolAddr = std.Address("g1ee305k8yk0pjz443xpwtqdyep522f9g5r7d63w") ira = std.Address("g1d9exzh6lta047h6lta047h6lta047h6l8ylkpa") // INTERNAL REWARD ACCOUNT ) - gnos.Mint(ira, 50000000000) // 50_000_000_000 - gnos.Approve(ira, stakerAddr, 50000000000) // owner, spender, amount + gns.Mint(ira, 50000000000) // 50_000_000_000 + gns.Approve(ira, stakerAddr, 50000000000) // owner, spender, amount admin = append(admin, string(stakerAddr)) } @@ -34,15 +34,15 @@ func init() { // getters. func GetGRC20() *grc20.AdminToken { - return gnos + return gns } func TotalSupply() uint64 { - return gnos.TotalSupply() + return gns.TotalSupply() } func BalanceOf(owner users.AddressOrName) uint64 { - balance, err := gnos.BalanceOf(owner.Resolve()) + balance, err := gns.BalanceOf(owner.Resolve()) if err != nil { panic(err) } @@ -50,7 +50,7 @@ func BalanceOf(owner users.AddressOrName) uint64 { } func Allowance(owner, spender users.AddressOrName) uint64 { - allowance, err := gnos.Allowance(owner.Resolve(), spender.Resolve()) + allowance, err := gns.Allowance(owner.Resolve(), spender.Resolve()) if err != nil { panic(err) } @@ -62,7 +62,7 @@ func Allowance(owner, spender users.AddressOrName) uint64 { func Transfer(to users.AddressOrName, amount uint64) { // caller := std.GetOrigCaller() caller := std.PrevRealm().Addr() - err := gnos.Transfer(caller, to.Resolve(), amount) + err := gns.Transfer(caller, to.Resolve(), amount) if err != nil { panic(err.Error()) } @@ -71,7 +71,7 @@ func Transfer(to users.AddressOrName, amount uint64) { func Approve(spender users.AddressOrName, amount uint64) { // caller := std.GetOrigCaller() caller := std.PrevRealm().Addr() - err := gnos.Approve(caller, spender.Resolve(), amount) + err := gns.Approve(caller, spender.Resolve(), amount) if err != nil { panic(err.Error()) } @@ -80,7 +80,7 @@ func Approve(spender users.AddressOrName, amount uint64) { func TransferFrom(from, to users.AddressOrName, amount uint64) { // caller := std.GetOrigCaller() caller := std.PrevRealm().Addr() - err := gnos.TransferFrom(caller, from.Resolve(), to.Resolve(), amount) + err := gns.TransferFrom(caller, from.Resolve(), to.Resolve(), amount) if err != nil { panic(err.Error()) } @@ -93,7 +93,7 @@ func Faucet() { // FIXME: add payment in gnot? // caller := std.GetOrigCaller() caller := std.PrevRealm().Addr() - gnos.Mint(caller, 1000*10000) // 1k + gns.Mint(caller, 1000*10000) // 1k } func FaucetL() { @@ -101,7 +101,7 @@ func FaucetL() { // FIXME: add payment in gnot? // caller := std.GetOrigCaller() caller := std.PrevRealm().Addr() - gnos.Mint(caller, 50000000000) // 50_000_000_000 + gns.Mint(caller, 50000000000) // 50_000_000_000 } // administration. @@ -110,14 +110,14 @@ func Mint(address users.AddressOrName, amount uint64) { // caller := std.GetOrigCaller() caller := std.PrevRealm().Addr() assertIsAdmin(caller) - gnos.Mint(address.Resolve(), amount) + gns.Mint(address.Resolve(), amount) } func Burn(address users.AddressOrName, amount uint64) { // caller := std.GetOrigCaller() caller := std.PrevRealm().Addr() assertIsAdmin(caller) - gnos.Burn(address.Resolve(), amount) + gns.Burn(address.Resolve(), amount) } func AppendAdmin(address users.AddressOrName) { @@ -148,10 +148,10 @@ func Render(path string) string { switch { case path == "": - return gnos.RenderHome() + return gns.RenderHome() case c == 2 && parts[0] == "balance": owner := users.AddressOrName(parts[1]) - balance, _ := gnos.BalanceOf(owner.Resolve()) + balance, _ := gns.BalanceOf(owner.Resolve()) return ufmt.Sprintf("%d\n", balance) default: return "404\n" diff --git a/_setup/gnos/gnos_test.gno b/_setup/gns/gns_test.gno similarity index 99% rename from _setup/gnos/gnos_test.gno rename to _setup/gns/gns_test.gno index e509ed67..3dd6d3c0 100644 --- a/_setup/gnos/gnos_test.gno +++ b/_setup/gns/gns_test.gno @@ -1,4 +1,4 @@ -package gnos +package gns import ( "std" diff --git a/_setup/qux/gno.mod b/_setup/qux/gno.mod new file mode 100644 index 00000000..7af24dac --- /dev/null +++ b/_setup/qux/gno.mod @@ -0,0 +1 @@ +module gno.land/r/qux \ No newline at end of file diff --git a/_setup/qux/qux.gno b/_setup/qux/qux.gno new file mode 100644 index 00000000..0fd41cc0 --- /dev/null +++ b/_setup/qux/qux.gno @@ -0,0 +1,154 @@ +package qux + +import ( + "std" + "strings" + + "gno.land/p/demo/grc/grc20" + "gno.land/p/demo/ufmt" + "gno.land/r/demo/users" + + // for swap + "gno.land/p/demo/testutils" +) + +var ( + qux *grc20.AdminToken + admin std.Address = "g1jg8mtutu9khhfwc4nxmuhcpftf0pajdhfvsqf5" // TODO: helper to change admin +) + +func init() { + qux = grc20.NewAdminToken("Qux", "QUX", 4) + qux.Mint(admin, 1000000*10000) // @administrator (1M) + + // for swap > pool + var ( + lp01 = testutils.TestAddress("lp01") // Liquidity Provider 01 + lp02 = testutils.TestAddress("lp02") // Liquidity Provider 02 + lp03 = testutils.TestAddress("lp03") // Liquidity Provider 03 + tr01 = testutils.TestAddress("tr01") // Trader 01 + poolAddr = std.DerivePkgAddr("gno.land/r/pool") + posAddr = std.DerivePkgAddr("gno.land/r/position") + ) + + qux.Mint(lp01, 50000000000) + qux.Mint(lp02, 50000000000) + qux.Mint(lp03, 50000000000) + qux.Mint(tr01, 50000000000) + + qux.Approve(lp01, poolAddr, 50000000000) + qux.Approve(lp02, poolAddr, 50000000000) + qux.Approve(lp03, poolAddr, 50000000000) + qux.Approve(tr01, poolAddr, 50000000000) + + // qux.Approve(posAddr, poolAddr, 50000000000) +} + +// method proxies as public functions. +// + +// getters. +func GetGRC20() *grc20.AdminToken { + return qux +} + +func TotalSupply() uint64 { + return qux.TotalSupply() +} + +func BalanceOf(owner users.AddressOrName) uint64 { + balance, err := qux.BalanceOf(owner.Resolve()) + if err != nil { + panic(err) + } + return balance +} + +func Allowance(owner, spender users.AddressOrName) uint64 { + allowance, err := qux.Allowance(owner.Resolve(), spender.Resolve()) + if err != nil { + panic(err) + } + return allowance +} + +// setters. + +func Transfer(to users.AddressOrName, amount uint64) { + caller := std.PrevRealm().Addr() + err := qux.Transfer(caller, to.Resolve(), amount) + if err != nil { + panic(err.Error()) + } +} + +func Approve(spender users.AddressOrName, amount uint64) { + caller := std.PrevRealm().Addr() + err := qux.Approve(caller, spender.Resolve(), amount) + if err != nil { + panic(err.Error()) + } +} + +func TransferFrom(from, to users.AddressOrName, amount uint64) { + caller := std.PrevRealm().Addr() + err := qux.TransferFrom(caller, from.Resolve(), to.Resolve(), amount) + if err != nil { + panic(err.Error()) + } +} + +// faucet. + +func Faucet() { + // FIXME: add limits? + // FIXME: add payment in gnot? + caller := std.PrevRealm().Addr() + qux.Mint(caller, 1000*10000) // 1k +} + +func FaucetL() { + // FIXME: add limits? + // FIXME: add payment in gnot? + caller := std.PrevRealm().Addr() + qux.Mint(caller, 50000000000000) // 50_000_000_000 +} + +// administration. + +func Mint(address users.AddressOrName, amount uint64) { + caller := std.PrevRealm().Addr() + assertIsAdmin(caller) + qux.Mint(address.Resolve(), amount) +} + +func Burn(address users.AddressOrName, amount uint64) { + caller := std.PrevRealm().Addr() + assertIsAdmin(caller) + qux.Burn(address.Resolve(), amount) +} + +// render. +// + +func Render(path string) string { + parts := strings.Split(path, "/") + c := len(parts) + + switch { + case path == "": + return qux.RenderHome() + case c == 2 && parts[0] == "balance": + owner := users.AddressOrName(parts[1]) + balance, _ := qux.BalanceOf(owner.Resolve()) + return ufmt.Sprintf("%d\n", balance) + default: + return "404\n" + } +} + +func assertIsAdmin(address std.Address) { + if address != admin { + panic("restricted access") + } +} diff --git a/_setup/qux/qux_test.gno b/_setup/qux/qux_test.gno new file mode 100644 index 00000000..e1ab5dbe --- /dev/null +++ b/_setup/qux/qux_test.gno @@ -0,0 +1,73 @@ +package qux + +import ( + "std" + "testing" + + "gno.land/p/demo/testutils" + + "gno.land/r/demo/users" +) + +var ( + a1 = testutils.TestAddress("a1") + a2 = testutils.TestAddress("a2") + a3 = testutils.TestAddress("a3") + a4 = testutils.TestAddress("a4") +) + +func init() { + std.TestSetOrigCaller(a1) + Faucet() +} + +func TestTransfer(t *testing.T) { + std.TestSetOrigCaller(a1) + Transfer(a2u(a2), 100) + + std.TestSetOrigCaller(a2) + Transfer(a2u(a3), 95) + + shouldPanicWithMsg(t, func() { Transfer(a2u(a3), 10) }, "insufficient balance") +} + +func TestApprove(t *testing.T) { + std.TestSetOrigCaller(std.Address("")) + shouldPanicWithMsg(t, func() { Approve(a2u(a2), 1000) }, "invalid address") + + std.TestSetOrigCaller(a1) + shouldPanicWithMsg(t, func() { Approve(a2u(std.Address("")), 1000) }, "invalid address") +} + +func TestTransferFrom(t *testing.T) { + std.TestSetOrigCaller(a1) + Approve(a2u(a2), 1000) + + std.TestSetOrigCaller(a2) + TransferFrom(a2u(a1), a2u(a3), 100) + + // not enough allowance + shouldPanicWithMsg(t, func() { TransferFrom(a2u(a1), a2u(a3), 901) }, "insufficient allowance") + + // didn't approve + std.TestSetOrigCaller(a3) + shouldPanicWithMsg(t, func() { TransferFrom(a2u(a1), a2u(a4), 100) }, "insufficient allowance") + +} + +func a2u(addr std.Address) users.AddressOrName { + return users.AddressOrName(addr) +} + +func shouldPanicWithMsg(t *testing.T, f func(), msg string) { + defer func() { + if r := recover(); r == nil { + t.Errorf("The code did not panic") + } else { + if r != msg { + t.Errorf("excepted panic(%v), got(%v)", msg, r) + } + } + }() + f() +} diff --git a/pool/_TEST_math_logic_test.gnoa b/pool/_TEST_math_logic_test.gnoa index 7ea61415..4b1230b3 100644 --- a/pool/_TEST_math_logic_test.gnoa +++ b/pool/_TEST_math_logic_test.gnoa @@ -6,28 +6,34 @@ import ( "gno.land/p/demo/testutils" - pos "gno.land/r/position" + _ "gno.land/r/grc20_wrapper" ) var ( gsa = testutils.TestAddress("gsa") lp01 = testutils.TestAddress("lp01") - pToken0 = "foo" - pToken1 = "bar" - pFee uint16 = 500 + poolAddr = std.DerivePkgAddr("gno.land/r/pool") + posAddr = std.DerivePkgAddr("gno.land/r/position") +) + +var ( + fooPath = "gno.land/r/foo" // token1 + barPath = "gno.land/r/bar" // token2 + pFee = uint16(500) sqrtPrice bigint = 130621891405341611593710811006 - tickLower int32 = 9000 - tickUpper int32 = 11000 - liquidityAmount bigint = 123456789 - currentTick int32 = 10000 + tickLower = int32(9000) + tickUpper = int32(11000) + liquidityExpect = bigint(100_000_000) + + currentTick = int32(10000) ) func init() { std.TestSetOrigCaller(gsa) InitManual() - CreatePool(pToken0, pToken1, pFee, sqrtPrice) + CreatePool(fooPath, barPath, pFee, sqrtPrice) } func TestGetSqrtRatioFromTick(t *testing.T) { @@ -41,22 +47,23 @@ func TestGetTickFromSqrtRatio(t *testing.T) { } func TestDrySwap_ZeroForOneTrue_AmountSpecified_Positive_16000(t *testing.T) { + std.TestSetPrevRealm("gno.land/r/position") std.TestSetOrigCaller(lp01) // no mint == no liquidity => swap will fail - shouldPanic(t, func() { DrySwap(pToken0, pToken1, pFee, "_", true, 16000, MIN_PRICE) }) + shouldPanic(t, func() { DrySwap(fooPath, barPath, pFee, "_", true, 16000, MIN_PRICE) }) // not enough mint == swap will fail - pos.Mint(pToken0, pToken1, pFee, tickLower, tickUpper, 1000, 1000, 0, 0, 9999999999) - shouldPanic(t, func() { DrySwap(pToken0, pToken1, pFee, "_", true, 16000, MIN_PRICE) }) + Mint(fooPath, barPath, pFee, posAddr, tickLower, tickUpper, 1000) + shouldPanic(t, func() { DrySwap(fooPath, barPath, pFee, "_", true, 16000, MIN_PRICE) }) - pos.Mint(pToken0, pToken1, pFee, tickLower, tickUpper, 100000, 100000, 0, 0, 9999999999) + Mint(fooPath, barPath, pFee, posAddr, tickLower, tickUpper, liquidityExpect) // zeroForOne true // amountSpecified 16000 input, output := DrySwap( - pToken0, // pToken0 - pToken1, // pToken1 + fooPath, // fooPath + barPath, // barPath pFee, // pFee "_", // recipient true, // zeroForOne @@ -64,7 +71,7 @@ func TestDrySwap_ZeroForOneTrue_AmountSpecified_Positive_16000(t *testing.T) { MIN_PRICE, // sqrtPriceLimitX96 ) shouldEQ(t, input, bigint(16000)) - shouldEQ(t, output, bigint(-42574)) + shouldEQ(t, output, bigint(-43457)) } func TestDrySwap_ZeroForOneTrue_AmountSpecified_Negative_16000(t *testing.T) { @@ -72,8 +79,8 @@ func TestDrySwap_ZeroForOneTrue_AmountSpecified_Negative_16000(t *testing.T) { // amountSpecified -16000 input, output := DrySwap( - pToken0, // pToken0 - pToken1, // pToken1 + fooPath, // fooPath + barPath, // barPath pFee, // pFee "_", // recipient true, // zeroForOne @@ -81,7 +88,7 @@ func TestDrySwap_ZeroForOneTrue_AmountSpecified_Negative_16000(t *testing.T) { MIN_SQRT_RATIO+1, // sqrtPriceLimitX96 ) - shouldEQ(t, input, bigint(5934)) + shouldEQ(t, input, bigint(5888)) shouldEQ(t, output, bigint(-15999)) } @@ -90,15 +97,15 @@ func TestDrySwap_ZeroForOneFalse_AmountSpecified_Positive_16000(t *testing.T) { // amountSpecified 16000 input, output := DrySwap( - pToken0, // pToken0 - pToken1, // pToken1 + fooPath, // fooPath + barPath, // barPath pFee, // pFee "_", // recipient false, // zeroForOne 16000, // amountSpecified MAX_SQRT_RATIO-1, // sqrtPriceLimitX96 ) - shouldEQ(t, input, bigint(-42574)) + shouldEQ(t, input, bigint(-43457)) shouldEQ(t, output, bigint(16000)) } @@ -107,8 +114,8 @@ func TestDrySwap_ZeroForOneFalse_AmountSpecified_Negative_16000(t *testing.T) { // amountSpecified -16000 input, output := DrySwap( - pToken0, // pToken0 - pToken1, // pToken1 + fooPath, // fooPath + barPath, // barPath pFee, // pFee "_", // recipient false, // zeroForOne @@ -116,7 +123,7 @@ func TestDrySwap_ZeroForOneFalse_AmountSpecified_Negative_16000(t *testing.T) { MAX_SQRT_RATIO-1, // sqrtPriceLimitX96 ) shouldEQ(t, input, bigint(-15999)) - shouldEQ(t, output, bigint(5934)) + shouldEQ(t, output, bigint(5888)) } /* HELPER */ diff --git a/pool/_TEST_pool_multi_lp_fee_api_test.gnoa b/pool/_TEST_pool_multi_lp_fee_api_test.gnoa index aa3df056..4a060b2e 100644 --- a/pool/_TEST_pool_multi_lp_fee_api_test.gnoa +++ b/pool/_TEST_pool_multi_lp_fee_api_test.gnoa @@ -6,10 +6,8 @@ import ( "testing" "gno.land/p/demo/testutils" - "gno.land/r/demo/users" - bar "gno.land/r/bar" - foo "gno.land/r/foo" + _ "gno.land/r/grc20_wrapper" g "gno.land/r/gov" ) @@ -19,13 +17,14 @@ var ( lp01 = testutils.TestAddress("lp01") // Liquidity Provider 01 tr01 = testutils.TestAddress("tr01") // Trader 01 - posAddr = std.DerivePkgAddr("gno.land/r/position") + poolAddr = std.DerivePkgAddr("gno.land/r/pool") + posAddr = std.DerivePkgAddr("gno.land/r/position") ) var ( // Common - pToken0 = "foo" - pToken1 = "bar" + fooPath = "gno.land/r/foo" // token1 + barPath = "gno.land/r/bar" // token2 pFee = uint16(500) test_tickLower = int32(9000) @@ -42,7 +41,7 @@ func TestCreatePool(t *testing.T) { jsonStr := gjson.Parse(ApiGetPools()) shouldEQ(t, len(jsonStr.Get("response.data").Array()), 0) - CreatePool("foo", "bar", pFee, 130622891405341611593710811006) + CreatePool(fooPath, barPath, pFee, 130622891405341611593710811006) std.TestSkipHeights(1) // 130621891405341611593710811006 // 9999 // 130622891405341611593710811006 // 10000 @@ -52,48 +51,33 @@ func TestCreatePool(t *testing.T) { // 10000 = 1% // USv3 default jsonStr = gjson.Parse(ApiGetPools()) shouldEQ(t, len(jsonStr.Get("response.data").Array()), 1) - shouldEQ(t, jsonStr.Get("response.data").Array()[0].String(), "bar_foo_500") + shouldEQ(t, jsonStr.Get("response.data").Array()[0].String(), "gno.land/r/bar:gno.land/r/foo:500") - jsonStr = gjson.Parse(ApiGetPool("bar_foo_500")) + jsonStr = gjson.Parse(ApiGetPool("gno.land/r/bar:gno.land/r/foo:500")) shouldEQ(t, jsonStr.Get("response.data.liquidity").Int(), 0) shouldEQ(t, len(jsonStr.Get("response.data.positions").Array()), 0) // sqrtPrice // 130621891405341611593710811006 // tick = 10000 - shouldPanic(t, func() { CreatePool("foo", "bar", 500, 130621891405341611593710811006) }) + shouldPanic(t, func() { CreatePool(fooPath, barPath, 500, 130621891405341611593710811006) }) std.TestSkipHeights(1) - - // Approve - std.TestSetOrigCaller(lp01) - foo.Approve(users.AddressOrName(GetOrigPkgAddr()), 50000000) - bar.Approve(users.AddressOrName(GetOrigPkgAddr()), 50000000) - foo.Approve(users.AddressOrName(lp01), 50000000) - bar.Approve(users.AddressOrName(lp01), 50000000) - std.TestSkipHeights(4) - - std.TestSetOrigCaller(tr01) - foo.Approve(users.AddressOrName(GetOrigPkgAddr()), 50000000) - bar.Approve(users.AddressOrName(GetOrigPkgAddr()), 50000000) - foo.Approve(users.AddressOrName(tr01), 50000000) - bar.Approve(users.AddressOrName(tr01), 50000000) - std.TestSkipHeights(4) } // Swap by tr01, Mint by lp01 ~ 02 func TestSwap(t *testing.T) { - pool := GetPool(pToken0, pToken1, pFee) + pool := GetPool(fooPath, barPath, pFee) // lp01 mint 9000 ~ 11000 tick range std.TestSetPrevRealm("gno.land/r/position") std.TestSetOrigCaller(lp01) - Mint(pToken0, pToken1, pFee, posAddr, test_tickLower, test_tickUpper, test_liquidityExpect*100000) + Mint(fooPath, barPath, pFee, posAddr, test_tickLower, test_tickUpper, test_liquidityExpect*100000) std.TestSkipHeights(1) jsonStr := gjson.Parse(ApiGetPools()) shouldEQ(t, len(jsonStr.Get("response.data").Array()), 1) - shouldEQ(t, jsonStr.Get("response.data").Array()[0].String(), "bar_foo_500") + shouldEQ(t, jsonStr.Get("response.data").Array()[0].String(), "gno.land/r/bar:gno.land/r/foo:500") - jsonStr = gjson.Parse(ApiGetPool("bar_foo_500")) + jsonStr = gjson.Parse(ApiGetPool("gno.land/r/bar:gno.land/r/foo:500")) shouldEQ(t, jsonStr.Get("response.data.token0_balance").Int(), 2957550) shouldEQ(t, jsonStr.Get("response.data.token1_balance").Int(), 8041577) shouldEQ(t, jsonStr.Get("response.data.liquidity").Int(), 100000000) @@ -105,17 +89,17 @@ func TestSwap(t *testing.T) { // SWAP std.TestSetOrigCaller(tr01) - Swap(pToken0, pToken1, pFee, tr01, true, bigint(150000), test_price_01) - jsonStr = gjson.Parse(ApiGetPool("bar_foo_500")) + Swap(fooPath, barPath, pFee, tr01, true, bigint(150000), test_price_01) + jsonStr = gjson.Parse(ApiGetPool("gno.land/r/bar:gno.land/r/foo:500")) shouldEQ(t, jsonStr.Get("response.data.token0_balance").Int(), 3107550) shouldEQ(t, jsonStr.Get("response.data.token1_balance").Int(), 7635058) shouldEQ(t, jsonStr.Get("response.data.liquidity").Int(), 100000000) shouldEQ(t, len(jsonStr.Get("response.data.positions").Array()), 1) - Swap(pToken0, pToken1, pFee, tr01, false, bigint(601851), test_price_10) - // Swap(pToken0, pToken1, pFee, tr01, true, bigint(1500000), test_price_01) // two iteration // s0: 1_500_000 // s1: -3_626_984 // currentTick: 7668 + Swap(fooPath, barPath, pFee, tr01, false, bigint(601851), test_price_10) + // Swap(fooPath, barPath, pFee, tr01, true, bigint(1500000), test_price_01) // two iteration // s0: 1_500_000 // s1: -3_626_984 // currentTick: 7668 std.TestSkipHeights(1) - jsonStr = gjson.Parse(ApiGetPool("bar_foo_500")) + jsonStr = gjson.Parse(ApiGetPool("gno.land/r/bar:gno.land/r/foo:500")) shouldEQ(t, jsonStr.Get("response.data.token0_balance").Int(), 1496418) shouldEQ(t, jsonStr.Get("response.data.token1_balance").Int(), 8236909) shouldEQ(t, jsonStr.Get("response.data.liquidity").Int(), 100000000) @@ -124,13 +108,13 @@ func TestSwap(t *testing.T) { // To collect fee without removing liquidity // burn 0 => collect std.TestSetOrigCaller(lp01) - Burn(pToken0, pToken1, pFee, test_tickLower, test_tickUpper, 0) + Burn(fooPath, barPath, pFee, test_tickLower, test_tickUpper, 0) std.TestSkipHeights(1) std.TestSetOrigCaller(lp01) - Collect(pToken0, pToken1, pFee, lp01, test_tickLower, test_tickUpper, 100000000, 100000000) + Collect(fooPath, barPath, pFee, lp01, test_tickLower, test_tickUpper, 100000000, 100000000) std.TestSkipHeights(1) - jsonStr = gjson.Parse(ApiGetPool("bar_foo_500")) + jsonStr = gjson.Parse(ApiGetPool("gno.land/r/bar:gno.land/r/foo:500")) shouldEQ(t, jsonStr.Get("response.data.token0_balance").Int(), 1496344) shouldEQ(t, jsonStr.Get("response.data.token1_balance").Int(), 8236609) shouldEQ(t, jsonStr.Get("response.data.liquidity").Int(), 100000000) @@ -161,12 +145,6 @@ func shouldEQ(t *testing.T, got, expected interface{}) { } } -func shouldNEQ(t *testing.T, got, expected interface{}) { - if got == expected { - t.Errorf("got %v, didn't expected %v", got, expected) - } -} - func shouldPanic(t *testing.T, f func()) { defer func() { if r := recover(); r == nil { diff --git a/pool/_TEST_pool_multi_lp_fee_test.gno b/pool/_TEST_pool_multi_lp_fee_test.gno new file mode 100644 index 00000000..83ea0ce4 --- /dev/null +++ b/pool/_TEST_pool_multi_lp_fee_test.gno @@ -0,0 +1,163 @@ +package pool + +import ( + "encoding/gjson" + "std" + "testing" + + "gno.land/p/demo/testutils" + + _ "gno.land/r/grc20_wrapper" +) + +var ( + gsa = testutils.TestAddress("gsa") // Gnoswap Admin + + lp01 = testutils.TestAddress("lp01") // Liquidity Provider 01 + lp02 = testutils.TestAddress("lp02") // Liquidity Provider 02 + + tr01 = testutils.TestAddress("tr01") // Trader 01 + + poolAddr = std.DerivePkgAddr("gno.land/r/pool") + posAddr = std.DerivePkgAddr("gno.land/r/position") +) + +var ( + // Common + fooPath = "gno.land/r/foo" // token1 + barPath = "gno.land/r/bar" // token2 + pFee = uint16(500) + + test_tickLower = int32(9000) + test_tickUpper = int32(11000) + test_liquidityExpect = bigint(1000) +) + +// 1. Init & Create Pool using Factory Contract by gsa +func TestFactoryCreatePool(t *testing.T) { + std.TestSetOrigCaller(gsa) + InitManual() + CreatePool(fooPath, barPath, pFee, 130622891405341611593710811006) + // 130621891405341611593710811006 // 9999 + // 130622891405341611593710811006 // 10000 + + // fee + // 500 = 0.05% // USv3 default + // 3000 = 0.3% // USv3 default + // 10000 = 1% // USv3 default + + // sqrtPrice + // 130621891405341611593710811006 // tick = 10000 + shouldPanic(t, func() { CreatePool(fooPath, barPath, 500, 130621891405341611593710811006) }) +} + +// Swap by tr01, Mint by lp01 ~ 02 +func TestSwap(t *testing.T) { + pool := GetPool(fooPath, barPath, pFee) + + // lp01 mint 9000 ~ 11000 tick range + std.TestSetPrevRealm("gno.land/r/position") + std.TestSetOrigCaller(lp01) + Mint(fooPath, barPath, pFee, posAddr, int32(-887270), int32(887270), test_liquidityExpect*10000) + + // lp02 mint (almost) full range + // fee 500 will use tickSpacing 10 + // MIN_TICK bigint = -887272 + // MAX_TICK bigint = 887272 + + std.TestSetOrigCaller(lp02) + Mint(fooPath, barPath, pFee, posAddr, int32(9990), int32(10010), test_liquidityExpect*10000) + + { + // balance before swap + poolOldToken0Bal := balanceOfByRegisterCall(fooPath, poolAddr) + poolOldToken1Bal := balanceOfByRegisterCall(barPath, poolAddr) + + userOldToken0Bal := balanceOfByRegisterCall(fooPath, tr01) + userOldToken1Bal := balanceOfByRegisterCall(barPath, tr01) + + tr01OldToken0Bal := balanceOfByRegisterCall(fooPath, tr01) + tr01OldToken1Bal := balanceOfByRegisterCall(barPath, tr01) + + lp01OldToken0Bal := balanceOfByRegisterCall(fooPath, lp01) + lp01OldToken1Bal := balanceOfByRegisterCall(barPath, lp01) + + lp02OldToken0Bal := balanceOfByRegisterCall(fooPath, lp02) + lp02OldToken1Bal := balanceOfByRegisterCall(barPath, lp02) + + // SWAP + + std.TestSetOrigCaller(tr01) + s0, s1 := Swap(fooPath, barPath, pFee, tr01, true, bigint(100000), MIN_PRICE) // one iteration - yes fee(only lp02) + shouldNEQ(t, s0, bigint(0)) + shouldNEQ(t, s1, bigint(0)) + + // balance after swap + poolNewToken0Bal := balanceOfByRegisterCall(fooPath, GetOrigPkgAddr()) + poolNewToken1Bal := balanceOfByRegisterCall(barPath, GetOrigPkgAddr()) + + tr01NewToken0Bal := balanceOfByRegisterCall(fooPath, tr01) + tr01NewToken1Bal := balanceOfByRegisterCall(barPath, tr01) + + lp01NewToken0Bal := balanceOfByRegisterCall(fooPath, lp01) + lp01NewToken1Bal := balanceOfByRegisterCall(barPath, lp01) + + lp02NewToken0Bal := balanceOfByRegisterCall(fooPath, lp02) + lp02NewToken1Bal := balanceOfByRegisterCall(barPath, lp02) + + // To collect fee wihout removing liquidity + // burn 0 + // then collect + std.TestSetOrigCaller(lp01) + Burn(fooPath, barPath, pFee, int32(-887270), int32(887270), 0) + lp01f0, lp01f1 := Collect(fooPath, barPath, pFee, lp01, int32(-887270), int32(887270), 100000000, 100000000) + + std.TestSetOrigCaller(lp02) + Burn(fooPath, barPath, pFee, int32(9990), int32(10010), 0) + lp02f0, lp02f1 := Collect(fooPath, barPath, pFee, lp02, int32(9990), int32(10010), 100000000, 100000000) + } +} + +/* GETTER_API TEST */ +func TestApiGetPools(t *testing.T) { + gpls := ApiGetPools() + jsonStr := gjson.Parse(gpls) + + shouldEQ(t, jsonStr.Get("stat.height").Int(), GetHeight()) + shouldEQ(t, jsonStr.Get("stat.timestamp").Int(), GetTimestamp()) + + shouldEQ(t, len(jsonStr.Get("response.data").Array()), 1) + shouldEQ(t, jsonStr.Get("response.data").Array()[0].String(), "gno.land/r/bar:gno.land/r/foo:500") +} + +func TestApiGetPool(t *testing.T) { + gpl := ApiGetPool("gno.land/r/bar:gno.land/r/foo:500") + jsonStr := gjson.Parse(gpl) + + shouldEQ(t, jsonStr.Get("stat.height").Int(), GetHeight()) + shouldEQ(t, jsonStr.Get("stat.timestamp").Int(), GetTimestamp()) + + shouldEQ(t, len(jsonStr.Get("response.data.positions").Array()), 2) +} + +/* HELPER */ +func shouldEQ(t *testing.T, got, expected interface{}) { + if got != expected { + t.Errorf("got %v, expected %v", got, expected) + } +} + +func shouldNEQ(t *testing.T, got, expected interface{}) { + if got == expected { + t.Errorf("got %v, didn't expected %v", got, expected) + } +} + +func shouldPanic(t *testing.T, f func()) { + defer func() { + if r := recover(); r == nil { + t.Errorf("expected panic") + } + }() + f() +} diff --git a/pool/_TEST_pool_multi_lp_fee_test.gnoa b/pool/_TEST_pool_multi_lp_fee_test.gnoa deleted file mode 100644 index d7ff9ed0..00000000 --- a/pool/_TEST_pool_multi_lp_fee_test.gnoa +++ /dev/null @@ -1,191 +0,0 @@ -package pool - -import ( - "encoding/gjson" - "std" - "testing" - - "gno.land/p/demo/testutils" - "gno.land/r/demo/users" - - bar "gno.land/r/bar" - foo "gno.land/r/foo" -) - -var ( - gsa = testutils.TestAddress("gsa") // Gnoswap Admin - - lp01 = testutils.TestAddress("lp01") // Liquidity Provider 01 - lp02 = testutils.TestAddress("lp02") // Liquidity Provider 02 - - tr01 = testutils.TestAddress("tr01") // Trader 01 - - posAddr = std.DerivePkgAddr("gno.land/r/position") -) - -var ( - // Common - pToken0 = "foo" - pToken1 = "bar" - pFee = uint16(500) - - test_tickLower = int32(9000) - test_tickUpper = int32(11000) - test_liquidityExpect = bigint(1000) -) - -// 1. Init & Create Pool using Factory Contract by gsa -func TestFactoryCreatePool(t *testing.T) { - std.TestSetOrigCaller(gsa) - InitManual() - CreatePool("foo", "bar", pFee, 130622891405341611593710811006) - // 130621891405341611593710811006 // 9999 - // 130622891405341611593710811006 // 10000 - - // fee - // 500 = 0.05% // USv3 default - // 3000 = 0.3% // USv3 default - // 10000 = 1% // USv3 default - - // sqrtPrice - // 130621891405341611593710811006 // tick = 10000 - shouldPanic(t, func() { CreatePool("foo", "bar", 500, 130621891405341611593710811006) }) - - // Approve - std.TestSetOrigCaller(lp01) - foo.Approve(users.AddressOrName(GetOrigPkgAddr()), 50000000) - bar.Approve(users.AddressOrName(GetOrigPkgAddr()), 50000000) - foo.Approve(users.AddressOrName(lp01), 50000000) - bar.Approve(users.AddressOrName(lp01), 50000000) - - std.TestSetOrigCaller(lp02) - foo.Approve(users.AddressOrName(GetOrigPkgAddr()), 50000000) - bar.Approve(users.AddressOrName(GetOrigPkgAddr()), 50000000) - foo.Approve(users.AddressOrName(lp02), 50000000) - bar.Approve(users.AddressOrName(lp02), 50000000) - - std.TestSetOrigCaller(tr01) - foo.Approve(users.AddressOrName(GetOrigPkgAddr()), 50000000) - bar.Approve(users.AddressOrName(GetOrigPkgAddr()), 50000000) - foo.Approve(users.AddressOrName(tr01), 50000000) - bar.Approve(users.AddressOrName(tr01), 50000000) -} - -// Swap by tr01, Mint by lp01 ~ 02 -func TestSwap(t *testing.T) { - pool := GetPool(pToken0, pToken1, pFee) - - // lp01 mint 9000 ~ 11000 tick range - std.TestSetPrevRealm("gno.land/r/position") - std.TestSetOrigCaller(lp01) - Mint(pToken0, pToken1, pFee, posAddr, test_tickLower, test_tickUpper, test_liquidityExpect*10000) - - // lp02 mint (almost) full range - // fee 500 will use tickSpacing 10 - // MIN_TICK bigint = -887272 - // MAX_TICK bigint = 887272 - - std.TestSetOrigCaller(lp02) - Mint(pToken0, pToken1, pFee, posAddr, MIN_TICK+2, MAX_TICK-2, test_liquidityExpect*10000) - - test_price_01 := bigint(MIN_SQRT_RATIO + 1) // maximum price - test_price_10 := bigint(MAX_SQRT_RATIO - 1) // minimum price - - { - // balance before swap - poolOldToken0Bal := balanceOf(pool.token0, GetOrigPkgAddr()) - poolOldToken1Bal := balanceOf(pool.token1, GetOrigPkgAddr()) - - tr01OldToken0Bal := balanceOf(pool.token0, tr01) - tr01OldToken1Bal := balanceOf(pool.token1, tr01) - - lp01OldToken0Bal := balanceOf(pool.token0, lp01) - lp01OldToken1Bal := balanceOf(pool.token1, lp01) - - lp02OldToken0Bal := balanceOf(pool.token0, lp02) - lp02OldToken1Bal := balanceOf(pool.token1, lp02) - - // SWAP - std.TestSetOrigCaller(tr01) - // s0, s1 := Swap(pToken0, pToken1, pFee, tr01, true, bigint(600000), test_price_01) // 9034 - // s0, s1 := Swap(pToken0, pToken1, pFee, tr01, true, bigint(650000), test_price_01) // 8955 - // println("Current Tick:", pool.GetPoolSlot0Tick()) - - // s0, s1 := Swap(pToken0, pToken1, pFee, tr01, true, bigint(620000), test_price_01) // one iteration - yes fee - s0, s1 := Swap(pToken0, pToken1, pFee, tr01, true, bigint(630000), test_price_01) // one iteration - yes fee(only lp02) - - // s0, s1 := Swap(pToken0, pToken1, pFee, tr01, true, bigint(1400000), test_price_01) // one iteration - no fee - // s0, s1 := Swap(pToken0, pToken1, pFee, tr01, true, bigint(1500000), test_price_01) // two iteration - shouldNEQ(t, s0, bigint(0)) - shouldNEQ(t, s1, bigint(0)) - - // balance after swap - poolNewToken0Bal := balanceOf(pool.token0, GetOrigPkgAddr()) - poolNewToken1Bal := balanceOf(pool.token1, GetOrigPkgAddr()) - - tr01NewToken0Bal := balanceOf(pool.token0, tr01) - tr01NewToken1Bal := balanceOf(pool.token1, tr01) - - lp01NewToken0Bal := balanceOf(pool.token0, lp01) - lp01NewToken1Bal := balanceOf(pool.token1, lp01) - - lp02NewToken0Bal := balanceOf(pool.token0, lp02) - lp02NewToken1Bal := balanceOf(pool.token1, lp02) - - // To collect fee wihout removing liquidity - // burn 0 - // then collect - - std.TestSetOrigCaller(lp01) - Burn(pToken0, pToken1, pFee, test_tickLower, test_tickUpper, 0) - Collect(pToken0, pToken1, pFee, lp01, test_tickLower, test_tickUpper, 100000000, 100000000) - - std.TestSetOrigCaller(lp02) - Burn(pToken0, pToken1, pFee, MIN_TICK+2, MAX_TICK-2, 0) - Collect(pToken0, pToken1, pFee, lp02, MIN_TICK+2, MAX_TICK-2, 100000000, 100000000) - } -} - -/* GETTER_API TEST */ -func TestApiGetPools(t *testing.T) { - gpls := ApiGetPools() - jsonStr := gjson.Parse(gpls) - - shouldEQ(t, jsonStr.Get("stat.height").Int(), GetHeight()) - shouldEQ(t, jsonStr.Get("stat.timestamp").Int(), GetTimestamp()) - - shouldEQ(t, len(jsonStr.Get("response.data").Array()), 1) - shouldEQ(t, jsonStr.Get("response.data").Array()[0].String(), "bar_foo_500") -} - -func TestApiGetPool(t *testing.T) { - gpl := ApiGetPool("bar_foo_500") - jsonStr := gjson.Parse(gpl) - - shouldEQ(t, jsonStr.Get("stat.height").Int(), GetHeight()) - shouldEQ(t, jsonStr.Get("stat.timestamp").Int(), GetTimestamp()) - - shouldEQ(t, len(jsonStr.Get("response.data.positions").Array()), 2) -} - -/* HELPER */ -func shouldEQ(t *testing.T, got, expected interface{}) { - if got != expected { - t.Errorf("got %v, expected %v", got, expected) - } -} - -func shouldNEQ(t *testing.T, got, expected interface{}) { - if got == expected { - t.Errorf("got %v, didn't expected %v", got, expected) - } -} - -func shouldPanic(t *testing.T, f func()) { - defer func() { - if r := recover(); r == nil { - t.Errorf("expected panic") - } - }() - f() -} diff --git a/pool/_TEST_pool_multi_lp_test.gnoa b/pool/_TEST_pool_multi_lp_test.gnoa index 0bb323dc..c23a5310 100644 --- a/pool/_TEST_pool_multi_lp_test.gnoa +++ b/pool/_TEST_pool_multi_lp_test.gnoa @@ -6,10 +6,8 @@ import ( "testing" "gno.land/p/demo/testutils" - "gno.land/r/demo/users" - bar "gno.land/r/bar" - foo "gno.land/r/foo" + _ "gno.land/r/grc20_wrapper" ) var ( @@ -21,13 +19,14 @@ var ( tr01 = testutils.TestAddress("tr01") // Trader 01 - posAddr = std.DerivePkgAddr("gno.land/r/position") + poolAddr = std.DerivePkgAddr("gno.land/r/pool") + posAddr = std.DerivePkgAddr("gno.land/r/position") ) var ( // Common - pToken0 = "foo" - pToken1 = "bar" + fooPath = "gno.land/r/foo" // token1 + barPath = "gno.land/r/bar" // token2 pFee = uint16(500) test_tickLower = int32(9000) @@ -42,50 +41,21 @@ var ( func TestFactoryCreatePool(t *testing.T) { std.TestSetOrigCaller(gsa) InitManual() - CreatePool("foo", "bar", pFee, 130621891405341611593710811006) - - // fee - // 500 = 0.05% // USv3 default - // 3000 = 0.3% // USv3 default - // 10000 = 1% // USv3 default + CreatePool(fooPath, barPath, pFee, 130621891405341611593710811006) // sqrtPrice // 130621891405341611593710811006 // tick = 10000 - shouldPanic(t, func() { CreatePool("foo", "bar", 500, 130621891405341611593710811006) }) - - // Approve - std.TestSetOrigCaller(lp01) - foo.Approve(users.AddressOrName(GetOrigPkgAddr()), 50000000) - bar.Approve(users.AddressOrName(GetOrigPkgAddr()), 50000000) - foo.Approve(users.AddressOrName(lp01), 50000000) - bar.Approve(users.AddressOrName(lp01), 50000000) - - std.TestSetOrigCaller(lp02) - foo.Approve(users.AddressOrName(GetOrigPkgAddr()), 50000000) - bar.Approve(users.AddressOrName(GetOrigPkgAddr()), 50000000) - foo.Approve(users.AddressOrName(lp02), 50000000) - bar.Approve(users.AddressOrName(lp02), 50000000) - - std.TestSetOrigCaller(lp03) - foo.Approve(users.AddressOrName(GetOrigPkgAddr()), 50000000) - bar.Approve(users.AddressOrName(GetOrigPkgAddr()), 50000000) - foo.Approve(users.AddressOrName(lp03), 50000000) - bar.Approve(users.AddressOrName(lp03), 50000000) - - std.TestSetOrigCaller(tr01) - foo.Approve(users.AddressOrName(GetOrigPkgAddr()), 50000000) - bar.Approve(users.AddressOrName(GetOrigPkgAddr()), 50000000) - foo.Approve(users.AddressOrName(tr01), 50000000) - bar.Approve(users.AddressOrName(tr01), 50000000) + shouldPanic(t, func() { CreatePool(fooPath, barPath, 500, 130621891405341611593710811006) }) } // 2. Mint by lp01 func TestMint(t *testing.T) { std.TestSetPrevRealm("gno.land/r/position") std.TestSetOrigCaller(lp01) + Mint( - pToken0, - pToken1, + fooPath, + barPath, pFee, posAddr, test_tickLower, @@ -93,20 +63,20 @@ func TestMint(t *testing.T) { test_liquidityExpect, ) - pool := GetPool(pToken0, pToken1, pFee) + pool := GetPool(fooPath, barPath, pFee) test_liquidity := pool.GetLiquidity() shouldEQ(t, test_liquidity, test_liquidityExpect) - Mint(pToken0, pToken1, pFee, posAddr, test_tickLower, test_tickUpper, test_liquidityExpect) - Mint(pToken0, pToken1, pFee, posAddr, test_tickLower, test_tickUpper, test_liquidityExpect) - Mint(pToken0, pToken1, pFee, posAddr, test_tickLower, test_tickUpper, test_liquidityExpect) - Mint(pToken0, pToken1, pFee, posAddr, test_tickLower, test_tickUpper, test_liquidityExpect) - Mint(pToken0, pToken1, pFee, posAddr, test_tickLower, test_tickUpper, test_liquidityExpect) - Mint(pToken0, pToken1, pFee, posAddr, test_tickLower, test_tickUpper, test_liquidityExpect) + Mint(fooPath, barPath, pFee, posAddr, test_tickLower, test_tickUpper, test_liquidityExpect) + Mint(fooPath, barPath, pFee, posAddr, test_tickLower, test_tickUpper, test_liquidityExpect) + Mint(fooPath, barPath, pFee, posAddr, test_tickLower, test_tickUpper, test_liquidityExpect) + Mint(fooPath, barPath, pFee, posAddr, test_tickLower, test_tickUpper, test_liquidityExpect) + Mint(fooPath, barPath, pFee, posAddr, test_tickLower, test_tickUpper, test_liquidityExpect) + Mint(fooPath, barPath, pFee, posAddr, test_tickLower, test_tickUpper, test_liquidityExpect) - m81, m82 := Mint(pToken0, pToken1, pFee, posAddr, test_tickLower, test_tickUpper, test_liquidityExpect) - Mint(pToken0, pToken1, pFee, posAddr, test_tickLower, test_tickUpper, test_liquidityExpect) - m101, m102 := Mint(pToken0, pToken1, pFee, posAddr, test_tickLower, test_tickUpper, test_liquidityExpect) + m81, m82 := Mint(fooPath, barPath, pFee, posAddr, test_tickLower, test_tickUpper, test_liquidityExpect) + Mint(fooPath, barPath, pFee, posAddr, test_tickLower, test_tickUpper, test_liquidityExpect) + m101, m102 := Mint(fooPath, barPath, pFee, posAddr, test_tickLower, test_tickUpper, test_liquidityExpect) shouldNEQ(t, m81, bigint(0)) shouldNEQ(t, m82, bigint(0)) @@ -117,27 +87,27 @@ func TestMint(t *testing.T) { shouldEQ(t, test_liquidity, test_liquidityExpect*10) // tickLower > currentTick == don't add to current liquidity - Mint(pToken0, pToken1, pFee, posAddr, test_tickLower2, test_tickUpper2, test_liquidityExpect) + Mint(fooPath, barPath, pFee, posAddr, test_tickLower2, test_tickUpper2, test_liquidityExpect) // tickUpper < current tick == don't add to current liquidity - Mint(pToken0, pToken1, pFee, posAddr, -test_tickUpper2, -test_tickLower2, test_liquidityExpect) + Mint(fooPath, barPath, pFee, posAddr, -test_tickUpper2, -test_tickLower2, test_liquidityExpect) // tickUpper < tickLower == don't add to current liquidity - Mint(pToken0, pToken1, pFee, posAddr, -test_tickUpper, -test_tickLower, test_liquidityExpect) + Mint(fooPath, barPath, pFee, posAddr, -test_tickUpper, -test_tickLower, test_liquidityExpect) test_liquidity = pool.GetLiquidity() shouldEQ(t, test_liquidity, test_liquidityExpect*10) - Mint(pToken0, pToken1, pFee, posAddr, test_tickLower, test_tickUpper, test_liquidityExpect) - Mint(pToken0, pToken1, pFee, posAddr, test_tickLower, test_tickUpper, test_liquidityExpect) - Mint(pToken0, pToken1, pFee, posAddr, test_tickLower, test_tickUpper, test_liquidityExpect) - Mint(pToken0, pToken1, pFee, posAddr, test_tickLower, test_tickUpper, test_liquidityExpect) - Mint(pToken0, pToken1, pFee, posAddr, test_tickLower, test_tickUpper, test_liquidityExpect) - Mint(pToken0, pToken1, pFee, posAddr, test_tickLower, test_tickUpper, test_liquidityExpect) - Mint(pToken0, pToken1, pFee, posAddr, test_tickLower, test_tickUpper, test_liquidityExpect) - Mint(pToken0, pToken1, pFee, posAddr, test_tickLower, test_tickUpper, test_liquidityExpect) - Mint(pToken0, pToken1, pFee, posAddr, test_tickLower, test_tickUpper, test_liquidityExpect) - Mint(pToken0, pToken1, pFee, posAddr, test_tickLower, test_tickUpper, test_liquidityExpect) + Mint(fooPath, barPath, pFee, posAddr, test_tickLower, test_tickUpper, test_liquidityExpect) + Mint(fooPath, barPath, pFee, posAddr, test_tickLower, test_tickUpper, test_liquidityExpect) + Mint(fooPath, barPath, pFee, posAddr, test_tickLower, test_tickUpper, test_liquidityExpect) + Mint(fooPath, barPath, pFee, posAddr, test_tickLower, test_tickUpper, test_liquidityExpect) + Mint(fooPath, barPath, pFee, posAddr, test_tickLower, test_tickUpper, test_liquidityExpect) + Mint(fooPath, barPath, pFee, posAddr, test_tickLower, test_tickUpper, test_liquidityExpect) + Mint(fooPath, barPath, pFee, posAddr, test_tickLower, test_tickUpper, test_liquidityExpect) + Mint(fooPath, barPath, pFee, posAddr, test_tickLower, test_tickUpper, test_liquidityExpect) + Mint(fooPath, barPath, pFee, posAddr, test_tickLower, test_tickUpper, test_liquidityExpect) + Mint(fooPath, barPath, pFee, posAddr, test_tickLower, test_tickUpper, test_liquidityExpect) test_liquidity = pool.GetLiquidity() shouldEQ(t, test_liquidity, test_liquidityExpect*20) @@ -148,34 +118,34 @@ func TestBurn(t *testing.T) { std.TestSetPrevRealm("gno.land/r/position") std.TestSetOrigCaller(lp01) - b11, b12 := Burn(pToken0, pToken1, pFee, test_tickLower, test_tickUpper, test_liquidityExpect) - b21, b22 := Burn(pToken0, pToken1, pFee, test_tickLower, test_tickUpper, test_liquidityExpect) + b11, b12 := Burn(fooPath, barPath, pFee, test_tickLower, test_tickUpper, test_liquidityExpect) + b21, b22 := Burn(fooPath, barPath, pFee, test_tickLower, test_tickUpper, test_liquidityExpect) shouldEQ(t, b11, b21) shouldEQ(t, b12, b22) - pool := GetPool(pToken0, pToken1, pFee) + pool := GetPool(fooPath, barPath, pFee) test_liquidity := pool.GetLiquidity() shouldEQ(t, test_liquidity, test_liquidityExpect*18) - Burn(pToken0, pToken1, pFee, test_tickLower, test_tickUpper, test_liquidityExpect*8) + Burn(fooPath, barPath, pFee, test_tickLower, test_tickUpper, test_liquidityExpect*8) test_liquidity = pool.GetLiquidity() shouldEQ(t, test_liquidity, test_liquidityExpect*10) - Burn(pToken0, pToken1, pFee, test_tickLower, test_tickUpper, 1) + Burn(fooPath, barPath, pFee, test_tickLower, test_tickUpper, 1) test_liquidity = pool.GetLiquidity() shouldEQ(t, test_liquidity, bigint(9999)) - Burn(pToken0, pToken1, pFee, test_tickLower, test_tickUpper, 999) + Burn(fooPath, barPath, pFee, test_tickLower, test_tickUpper, 999) test_liquidity = pool.GetLiquidity() shouldEQ(t, test_liquidity, test_liquidityExpect*9) - Burn(pToken0, pToken1, pFee, test_tickLower, test_tickUpper, test_liquidityExpect*9) + Burn(fooPath, barPath, pFee, test_tickLower, test_tickUpper, test_liquidityExpect*9) test_liquidity = pool.GetLiquidity() shouldEQ(t, test_liquidity, bigint(0)) // can't burn when liq is 0 - // Burn(pToken0, pToken1, pFee, test_tickLower, test_tickUpper, test_liquidityExpect) - shouldPanic(t, func() { Burn(pToken0, pToken1, pFee, test_tickLower, test_tickUpper, test_liquidityExpect) }) + // Burn(fooPath, barPath, pFee, test_tickLower, test_tickUpper, test_liquidityExpect) + shouldPanic(t, func() { Burn(fooPath, barPath, pFee, test_tickLower, test_tickUpper, test_liquidityExpect) }) } // 4. Collect @@ -184,44 +154,44 @@ func TestCollect(t *testing.T) { std.TestSetOrigCaller(lp01) // withdraw all token before test `Collect` - Collect(pToken0, pToken1, pFee, lp01, test_tickLower, test_tickUpper, 50000000, 50000000) + Collect(fooPath, barPath, pFee, lp01, test_tickLower, test_tickUpper, 50000000, 50000000) // pool should have zero liquidity - pool := GetPool(pToken0, pToken1, pFee) + pool := GetPool(fooPath, barPath, pFee) test_liquidity := pool.GetLiquidity() shouldEQ(t, test_liquidity, bigint(0)) - Mint(pToken0, pToken1, pFee, posAddr, test_tickLower, test_tickUpper, test_liquidityExpect*15) - Burn(pToken0, pToken1, pFee, test_tickLower, test_tickUpper, test_liquidityExpect*15) - c11, c12 := Collect(pToken0, pToken1, pFee, lp01, test_tickLower, test_tickUpper, 50000000, 50000000) + Mint(fooPath, barPath, pFee, posAddr, test_tickLower, test_tickUpper, test_liquidityExpect*15) + Burn(fooPath, barPath, pFee, test_tickLower, test_tickUpper, test_liquidityExpect*15) + c11, c12 := Collect(fooPath, barPath, pFee, lp01, test_tickLower, test_tickUpper, 50000000, 50000000) - Mint(pToken0, pToken1, pFee, posAddr, test_tickLower, test_tickUpper, test_liquidityExpect*15) - Burn(pToken0, pToken1, pFee, test_tickLower, test_tickUpper, test_liquidityExpect*15) - c21, c22 := Collect(pToken0, pToken1, pFee, lp01, test_tickLower, test_tickUpper, 50000000, 50000000) + Mint(fooPath, barPath, pFee, posAddr, test_tickLower, test_tickUpper, test_liquidityExpect*15) + Burn(fooPath, barPath, pFee, test_tickLower, test_tickUpper, test_liquidityExpect*15) + c21, c22 := Collect(fooPath, barPath, pFee, lp01, test_tickLower, test_tickUpper, 50000000, 50000000) shouldEQ(t, c11, c21) shouldEQ(t, c12, c22) - Mint(pToken0, pToken1, pFee, posAddr, test_tickLower, test_tickUpper, test_liquidityExpect*15) - Burn(pToken0, pToken1, pFee, test_tickLower, test_tickUpper, test_liquidityExpect*15) - c31, c32 := Collect(pToken0, pToken1, pFee, lp01, test_tickLower, test_tickUpper, 100, 100) + Mint(fooPath, barPath, pFee, posAddr, test_tickLower, test_tickUpper, test_liquidityExpect*15) + Burn(fooPath, barPath, pFee, test_tickLower, test_tickUpper, test_liquidityExpect*15) + c31, c32 := Collect(fooPath, barPath, pFee, lp01, test_tickLower, test_tickUpper, 100, 100) shouldEQ(t, c31, bigint(100)) shouldEQ(t, c32, bigint(100)) - c41, c42 := Collect(pToken0, pToken1, pFee, lp01, test_tickLower, test_tickUpper, 50000000, 50000000) + c41, c42 := Collect(fooPath, barPath, pFee, lp01, test_tickLower, test_tickUpper, 50000000, 50000000) shouldEQ(t, c41, c21-bigint(100)) shouldEQ(t, c42, c22-bigint(100)) // Mint > No Burn => nothing to collect - Mint(pToken0, pToken1, pFee, posAddr, test_tickLower, test_tickUpper, test_liquidityExpect*15) - // Burn(pToken0, pToken1, pFee, test_tickLower, test_tickUpper, test_liquidityExpect*15) - c51, c52 := Collect(pToken0, pToken1, pFee, lp01, test_tickLower, test_tickUpper, 50000000, 50000000) + Mint(fooPath, barPath, pFee, posAddr, test_tickLower, test_tickUpper, test_liquidityExpect*15) + // Burn(fooPath, barPath, pFee, test_tickLower, test_tickUpper, test_liquidityExpect*15) + c51, c52 := Collect(fooPath, barPath, pFee, lp01, test_tickLower, test_tickUpper, 50000000, 50000000) shouldEQ(t, c51, bigint(0)) shouldEQ(t, c52, bigint(0)) // Burn Now => something to collect - Burn(pToken0, pToken1, pFee, test_tickLower, test_tickUpper, test_liquidityExpect*15) - c61, c62 := Collect(pToken0, pToken1, pFee, lp01, test_tickLower, test_tickUpper, 50000000, 50000000) + Burn(fooPath, barPath, pFee, test_tickLower, test_tickUpper, test_liquidityExpect*15) + c61, c62 := Collect(fooPath, barPath, pFee, lp01, test_tickLower, test_tickUpper, 50000000, 50000000) shouldNEQ(t, c61, bigint(0)) shouldNEQ(t, c62, bigint(0)) } @@ -229,150 +199,147 @@ func TestCollect(t *testing.T) { // 5. Swap by tr01, Mint by lp02 ~ lp03 func TestSwap(t *testing.T) { std.TestSetPrevRealm("gno.land/r/position") - pool := GetPool(pToken0, pToken1, pFee) + pool := GetPool(fooPath, barPath, pFee) std.TestSetOrigCaller(lp02) - Mint(pToken0, pToken1, pFee, posAddr, test_tickLower, test_tickUpper, test_liquidityExpect*10000) + Mint(fooPath, barPath, pFee, posAddr, test_tickLower, test_tickUpper, test_liquidityExpect*10000) std.TestSetOrigCaller(lp03) - Mint(pToken0, pToken1, pFee, posAddr, test_tickLower, test_tickUpper, test_liquidityExpect*10000) - - // Swap several times - test_price := bigint(MIN_SQRT_RATIO + 1) // maximum price + Mint(fooPath, barPath, pFee, posAddr, test_tickLower, test_tickUpper, test_liquidityExpect*10000) { - poolOldToken0Bal := balanceOf(pool.token0, GetOrigPkgAddr()) - poolOldToken1Bal := balanceOf(pool.token1, GetOrigPkgAddr()) + poolOldToken0Bal := balanceOfByRegisterCall(fooPath, poolAddr) + poolOldToken1Bal := balanceOfByRegisterCall(barPath, poolAddr) - userOldToken0Bal := balanceOf(pool.token0, tr01) - userOldToken1Bal := balanceOf(pool.token1, tr01) + userOldToken0Bal := balanceOfByRegisterCall(fooPath, tr01) + userOldToken1Bal := balanceOfByRegisterCall(barPath, tr01) - lp02OldToken0Bal := balanceOf(pool.token0, lp02) - lp02OldToken1Bal := balanceOf(pool.token1, lp02) + lp02OldToken0Bal := balanceOfByRegisterCall(fooPath, lp02) + lp02OldToken1Bal := balanceOfByRegisterCall(barPath, lp02) - lp03OldToken0Bal := balanceOf(pool.token0, lp03) - lp03OldToken1Bal := balanceOf(pool.token1, lp03) + lp03OldToken0Bal := balanceOfByRegisterCall(fooPath, lp03) + lp03OldToken1Bal := balanceOfByRegisterCall(barPath, lp03) std.TestSetOrigCaller(tr01) amount0, amount1 := Swap( - pToken0, - pToken1, + fooPath, + barPath, pFee, tr01, true, bigint(10000), - test_price, + MIN_PRICE, ) shouldNEQ(t, amount0, bigint(0)) shouldNEQ(t, amount1, bigint(0)) - poolNewToken0Bal := balanceOf(pool.token0, GetOrigPkgAddr()) - poolNewToken1Bal := balanceOf(pool.token1, GetOrigPkgAddr()) + poolNewToken0Bal := balanceOfByRegisterCall(fooPath, poolAddr) + poolNewToken1Bal := balanceOfByRegisterCall(barPath, poolAddr) - userNewToken0Bal := balanceOf(pool.token0, tr01) - userNewToken1Bal := balanceOf(pool.token1, tr01) + userNewToken0Bal := balanceOfByRegisterCall(fooPath, tr01) + userNewToken1Bal := balanceOfByRegisterCall(barPath, tr01) - shouldEQ(t, userOldToken0Bal-amount0, userNewToken0Bal) - shouldEQ(t, userOldToken1Bal-amount1, userNewToken1Bal) - shouldEQ(t, poolOldToken0Bal+amount0, poolNewToken0Bal) - shouldEQ(t, poolOldToken1Bal+amount1, poolNewToken1Bal) + shouldEQ(t, userOldToken0Bal-userNewToken0Bal, int64(amount0)) + shouldEQ(t, userNewToken1Bal-userOldToken1Bal, int64(-amount1)) + shouldEQ(t, poolNewToken0Bal-poolOldToken0Bal, int64(amount0)) + shouldEQ(t, poolOldToken1Bal-poolNewToken1Bal, int64(-amount1)) std.TestSetOrigCaller(lp02) - Burn(pToken0, pToken1, pFee, test_tickLower, test_tickUpper, test_liquidityExpect*1000) - Collect(pToken0, pToken1, pFee, lp02, test_tickLower, test_tickUpper, 50000000, 50000000) + Burn(fooPath, barPath, pFee, test_tickLower, test_tickUpper, test_liquidityExpect*1000) + Collect(fooPath, barPath, pFee, lp02, test_tickLower, test_tickUpper, 50000000, 50000000) - lp02NewToken0Bal := balanceOf(pool.token0, lp02) - lp02NewToken1Bal := balanceOf(pool.token1, lp02) + lp02NewToken0Bal := balanceOfByRegisterCall(fooPath, lp02) + lp02NewToken1Bal := balanceOfByRegisterCall(barPath, lp02) std.TestSetOrigCaller(lp03) - Burn(pToken0, pToken1, pFee, test_tickLower, test_tickUpper, test_liquidityExpect*1000) - Collect(pToken0, pToken1, pFee, lp03, test_tickLower, test_tickUpper, 50000000, 50000000) + Burn(fooPath, barPath, pFee, test_tickLower, test_tickUpper, test_liquidityExpect*1000) + Collect(fooPath, barPath, pFee, lp03, test_tickLower, test_tickUpper, 50000000, 50000000) - lp03NewToken0Bal := balanceOf(pool.token0, lp03) - lp03NewToken1Bal := balanceOf(pool.token1, lp03) + lp03NewToken0Bal := balanceOfByRegisterCall(fooPath, lp03) + lp03NewToken1Bal := balanceOfByRegisterCall(barPath, lp03) } // Mint again std.TestSetOrigCaller(lp02) - Mint(pToken0, pToken1, pFee, lp02, test_tickLower, test_tickUpper, test_liquidityExpect*1000) + Mint(fooPath, barPath, pFee, lp02, test_tickLower, test_tickUpper, test_liquidityExpect*1000) std.TestSetOrigCaller(lp03) - Mint(pToken0, pToken1, pFee, lp03, test_tickLower, test_tickUpper, test_liquidityExpect*1000) + Mint(fooPath, barPath, pFee, lp03, test_tickLower, test_tickUpper, test_liquidityExpect*1000) { - poolOldToken0Bal := balanceOf(pool.token0, GetOrigPkgAddr()) - poolOldToken1Bal := balanceOf(pool.token1, GetOrigPkgAddr()) + poolOldToken0Bal := balanceOfByRegisterCall(fooPath, poolAddr) + poolOldToken1Bal := balanceOfByRegisterCall(barPath, poolAddr) - userOldToken0Bal := balanceOf(pool.token0, tr01) - userOldToken1Bal := balanceOf(pool.token1, tr01) + userOldToken0Bal := balanceOfByRegisterCall(fooPath, tr01) + userOldToken1Bal := balanceOfByRegisterCall(barPath, tr01) std.TestSetOrigCaller(tr01) - amount0, amount1 := Swap(pToken0, pToken1, pFee, tr01, true, bigint(5000), test_price) + amount0, amount1 := Swap(fooPath, barPath, pFee, tr01, true, bigint(5000), MIN_PRICE) shouldNEQ(t, amount0, bigint(0)) shouldNEQ(t, amount1, bigint(0)) - poolNewToken0Bal := balanceOf(pool.token0, GetOrigPkgAddr()) - poolNewToken1Bal := balanceOf(pool.token1, GetOrigPkgAddr()) + poolNewToken0Bal := balanceOfByRegisterCall(fooPath, poolAddr) + poolNewToken1Bal := balanceOfByRegisterCall(barPath, poolAddr) - userNewToken0Bal := balanceOf(pool.token0, tr01) - userNewToken1Bal := balanceOf(pool.token1, tr01) + userNewToken0Bal := balanceOfByRegisterCall(fooPath, tr01) + userNewToken1Bal := balanceOfByRegisterCall(barPath, tr01) - shouldEQ(t, userOldToken0Bal-amount0, userNewToken0Bal) - shouldEQ(t, userOldToken1Bal-amount1, userNewToken1Bal) - shouldEQ(t, poolOldToken0Bal+amount0, poolNewToken0Bal) - shouldEQ(t, poolOldToken1Bal+amount1, poolNewToken1Bal) + shouldEQ(t, userOldToken0Bal-userNewToken0Bal, int64(amount0)) + shouldEQ(t, userNewToken1Bal-userOldToken1Bal, int64(-amount1)) + shouldEQ(t, poolNewToken0Bal-poolOldToken0Bal, int64(amount0)) + shouldEQ(t, poolOldToken1Bal-poolNewToken1Bal, int64(-amount1)) } { - poolOldToken0Bal := balanceOf(pool.token0, GetOrigPkgAddr()) - poolOldToken1Bal := balanceOf(pool.token1, GetOrigPkgAddr()) + poolOldToken0Bal := balanceOfByRegisterCall(fooPath, poolAddr) + poolOldToken1Bal := balanceOfByRegisterCall(barPath, poolAddr) - userOldToken0Bal := balanceOf(pool.token0, tr01) - userOldToken1Bal := balanceOf(pool.token1, tr01) + userOldToken0Bal := balanceOfByRegisterCall(fooPath, tr01) + userOldToken1Bal := balanceOfByRegisterCall(barPath, tr01) std.TestSetOrigCaller(tr01) - amount0, amount1 := Swap(pToken0, pToken1, pFee, tr01, true, bigint(1000), test_price) + amount0, amount1 := Swap(fooPath, barPath, pFee, tr01, true, bigint(1000), MIN_PRICE) shouldNEQ(t, amount0, bigint(0)) shouldNEQ(t, amount1, bigint(0)) - poolNewToken0Bal := balanceOf(pool.token0, GetOrigPkgAddr()) - poolNewToken1Bal := balanceOf(pool.token1, GetOrigPkgAddr()) + poolNewToken0Bal := balanceOfByRegisterCall(fooPath, poolAddr) + poolNewToken1Bal := balanceOfByRegisterCall(barPath, poolAddr) - userNewToken0Bal := balanceOf(pool.token0, tr01) - userNewToken1Bal := balanceOf(pool.token1, tr01) + userNewToken0Bal := balanceOfByRegisterCall(fooPath, tr01) + userNewToken1Bal := balanceOfByRegisterCall(barPath, tr01) - shouldEQ(t, userOldToken0Bal-amount0, userNewToken0Bal) - shouldEQ(t, userOldToken1Bal-amount1, userNewToken1Bal) - shouldEQ(t, poolOldToken0Bal+amount0, poolNewToken0Bal) - shouldEQ(t, poolOldToken1Bal+amount1, poolNewToken1Bal) + shouldEQ(t, userOldToken0Bal-userNewToken0Bal, int64(amount0)) + shouldEQ(t, userNewToken1Bal-userOldToken1Bal, int64(-amount1)) + shouldEQ(t, poolNewToken0Bal-poolOldToken0Bal, int64(amount0)) + shouldEQ(t, poolOldToken1Bal-poolNewToken1Bal, int64(-amount1)) } { - poolOldToken0Bal := balanceOf(pool.token0, GetOrigPkgAddr()) - poolOldToken1Bal := balanceOf(pool.token1, GetOrigPkgAddr()) + poolOldToken0Bal := balanceOfByRegisterCall(fooPath, poolAddr) + poolOldToken1Bal := balanceOfByRegisterCall(barPath, poolAddr) - userOldToken0Bal := balanceOf(pool.token0, tr01) - userOldToken1Bal := balanceOf(pool.token1, tr01) + userOldToken0Bal := balanceOfByRegisterCall(fooPath, tr01) + userOldToken1Bal := balanceOfByRegisterCall(barPath, tr01) std.TestSetOrigCaller(tr01) - amount0, amount1 := Swap(pToken0, pToken1, pFee, tr01, false, bigint(16000), (MAX_SQRT_RATIO - 1)) + amount0, amount1 := Swap(fooPath, barPath, pFee, tr01, false, bigint(16000), (MAX_SQRT_RATIO - 1)) shouldNEQ(t, amount0, bigint(0)) shouldNEQ(t, amount1, bigint(0)) - poolNewToken0Bal := balanceOf(pool.token0, GetOrigPkgAddr()) - poolNewToken1Bal := balanceOf(pool.token1, GetOrigPkgAddr()) + poolNewToken0Bal := balanceOfByRegisterCall(fooPath, poolAddr) + poolNewToken1Bal := balanceOfByRegisterCall(barPath, poolAddr) - userNewToken0Bal := balanceOf(pool.token0, tr01) - userNewToken1Bal := balanceOf(pool.token1, tr01) + userNewToken0Bal := balanceOfByRegisterCall(fooPath, tr01) + userNewToken1Bal := balanceOfByRegisterCall(barPath, tr01) - shouldEQ(t, userOldToken0Bal-amount0, userNewToken0Bal) - shouldEQ(t, userOldToken1Bal-amount1, userNewToken1Bal) - shouldEQ(t, poolOldToken0Bal+amount0, poolNewToken0Bal) - shouldEQ(t, poolOldToken1Bal+amount1, poolNewToken1Bal) + shouldEQ(t, userOldToken0Bal-userNewToken0Bal, int64(amount0)) + shouldEQ(t, userNewToken1Bal-userOldToken1Bal, int64(-amount1)) + shouldEQ(t, poolNewToken0Bal-poolOldToken0Bal, int64(amount0)) + shouldEQ(t, poolOldToken1Bal-poolNewToken1Bal, int64(-amount1)) } } @@ -394,42 +361,48 @@ func TestCollectProtocol(t *testing.T) { std.TestSetOrigCaller(gsa) SetFeeProtocol(6, 8) - pool := GetPool(pToken0, pToken1, pFee) + pool := GetPool(fooPath, barPath, pFee) test_slot0 := pool.GetSlot0() shouldEQ(t, test_slot0.feeProtocol, bigint(134)) // Make ProtocolFee via Swap by tr01 ( Mint by lp01 ) std.TestSetOrigCaller(lp01) { - gsaOldToken0Bal := balanceOf(pool.token0, gsa) - gsaOldToken1Bal := balanceOf(pool.token1, gsa) + gsaOldToken0Bal := balanceOfByRegisterCall(fooPath, gsa) + gsaOldToken1Bal := balanceOfByRegisterCall(barPath, gsa) std.TestSetOrigCaller(tr01) - Swap(pToken0, pToken1, pFee, tr01, true, 100000, MIN_SQRT_RATIO+1) // swap token0 -> token1 => fee only in token0 - Swap(pToken0, pToken1, pFee, tr01, true, 100000, MIN_SQRT_RATIO+1) // more protocol fee + Swap(fooPath, barPath, pFee, tr01, true, 100000, MIN_PRICE) // swap token0 -> token1 => fee only in token0 + Swap(fooPath, barPath, pFee, tr01, true, 100000, MIN_PRICE) // more protocol fee // Gnoswap Admin std.TestSetOrigCaller(gsa) - amount0, amount1 := CollectProtocol(pToken0, pToken1, pFee, gsa, 100000, 100000) + amount0, amount1 := CollectProtocol(fooPath, barPath, pFee, gsa, 100000, 100000) - gsaNewToken0Bal := balanceOf(pool.token0, gsa) - gsaNewToken1Bal := balanceOf(pool.token1, gsa) + gsaNewToken0Bal := balanceOfByRegisterCall(fooPath, gsa) + gsaNewToken1Bal := balanceOfByRegisterCall(barPath, gsa) + + shouldEQ(t, gsaNewToken0Bal-gsaOldToken0Bal, int64(amount0)) + shouldEQ(t, gsaNewToken1Bal-gsaOldToken1Bal, int64(amount1)) } { - gsaOldToken0Bal := balanceOf(pool.token0, gsa) - gsaOldToken1Bal := balanceOf(pool.token1, gsa) + gsaOldToken0Bal := balanceOfByRegisterCall(fooPath, gsa) + gsaOldToken1Bal := balanceOfByRegisterCall(barPath, gsa) std.TestSetOrigCaller(tr01) - Swap(pToken0, pToken1, pFee, tr01, false, 100000, MAX_SQRT_RATIO-1) // swap token0 -> token1 => fee only in token0 - Swap(pToken0, pToken1, pFee, tr01, false, 100000, MAX_SQRT_RATIO-1) // more protocol fee + Swap(fooPath, barPath, pFee, tr01, false, 100000, MAX_PRICE) // swap token0 -> token1 => fee only in token0 + Swap(fooPath, barPath, pFee, tr01, false, 100000, MAX_PRICE) // more protocol fee // Gnoswap Admin std.TestSetOrigCaller(gsa) - amount0, amount1 := CollectProtocol(pToken0, pToken1, pFee, gsa, 100000, 100000) + amount0, amount1 := CollectProtocol(fooPath, barPath, pFee, gsa, 100000, 100000) + + gsaNewToken0Bal := balanceOfByRegisterCall(fooPath, gsa) + gsaNewToken1Bal := balanceOfByRegisterCall(barPath, gsa) - gsaNewToken0Bal := balanceOf(pool.token0, gsa) - gsaNewToken1Bal := balanceOf(pool.token1, gsa) + shouldEQ(t, gsaNewToken0Bal-gsaOldToken0Bal, int64(amount0)) + shouldEQ(t, gsaNewToken1Bal-gsaOldToken1Bal, int64(amount1)) } } @@ -442,7 +415,7 @@ func TestApiGetPools(t *testing.T) { shouldEQ(t, jsonStr.Get("stat.timestamp").Int(), GetTimestamp()) shouldEQ(t, len(jsonStr.Get("response.data").Array()), 1) - shouldEQ(t, jsonStr.Get("response.data").Array()[0].String(), "bar_foo_500") + shouldEQ(t, jsonStr.Get("response.data").Array()[0].String(), "gno.land/r/bar:gno.land/r/foo:500") } /* HELPER */ diff --git a/pool/_TEST_pool_multi_token_test.gno b/pool/_TEST_pool_multi_token_test.gno deleted file mode 100644 index 482f7be5..00000000 --- a/pool/_TEST_pool_multi_token_test.gno +++ /dev/null @@ -1,182 +0,0 @@ -package pool - -import ( - "std" - "testing" - - "gno.land/p/demo/testutils" - - _ "gno.land/r/grc20_wrapper" -) - -var ( - gsa = testutils.TestAddress("gsa") // Gnoswap Admin - 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") - - poolPath = "gno.land/r/pool" -) - -var ( - // Common - fooPath = "gno.land/r/foo" - barPath = "gno.land/r/bar" - bazPath = "gno.land/r/baz" - - pFee = uint16(500) - - test_tickLower = int32(9000) - test_tickUpper = int32(11000) - test_liquidityExpect = bigint(100000000) - - test_tickLower2 = int32(50000) - test_tickUpper2 = int32(100000) -) - -// 1. Init Pool -func TestInit(t *testing.T) { - std.TestSetOrigCaller(gsa) - InitManual() -} - -// 2. Create Foo:Bar Pool -func TestCreateFooBarPool(t *testing.T) { - CreatePool(fooPath, barPath, pFee, 130621891405341611593710811006) - shouldEQ(t, len(pools), 1) -} - -// 3. Create Bar:Baz Pool -func TestCreateBarBazPool(t *testing.T) { - CreatePool(barPath, bazPath, pFee, 130621891405341611593710811006) - shouldEQ(t, len(pools), 2) -} - -// 4. Mint Foo:Bar Liquidity by lp01 -func TestMintFooBarLiquidity(t *testing.T) { - std.TestSetOrigCaller(lp01) - - std.TestSetPrevRealm("gno.land/r/position") - - Mint( - fooPath, - barPath, - pFee, - posAddr, - test_tickLower, - test_tickUpper, - test_liquidityExpect, - ) -} - -// 5. Mint Bar:Baz Liquidity by lp01 -func TestMintBarBazLiquidity(t *testing.T) { - std.TestSetOrigCaller(lp01) - - std.TestSetPrevRealm("gno.land/r/position") - - Mint( - barPath, - bazPath, - pFee, - posAddr, - test_tickLower, - test_tickUpper, - test_liquidityExpect, - ) -} - -// 6. Swap Foo:Bar Foo > Bar by tr01 -func TestSwapFooBarFooToBar(t *testing.T) { - std.TestSetOrigCaller(tr01) - - oldTr01FooBalance := balanceOfByRegisterCall(fooPath, tr01) - oldTr01BarBalance := balanceOfByRegisterCall(barPath, tr01) - oldPoolFooBalance := balanceOfByRegisterCall(fooPath, poolAddr) - oldPoolBarBalance := balanceOfByRegisterCall(barPath, poolAddr) - - poolIn, poolOut := Swap( - fooPath, - barPath, - pFee, - tr01, - true, - bigint(16000), - MIN_PRICE, - ) - shouldEQ(t, poolIn, bigint(16000)) - shouldEQ(t, poolOut, bigint(-43457)) - - newTr01FooBalance := balanceOfByRegisterCall(fooPath, tr01) - newTr01BarBalance := balanceOfByRegisterCall(barPath, tr01) - newPoolFooBalance := balanceOfByRegisterCall(fooPath, poolAddr) - newPoolBarBalance := balanceOfByRegisterCall(barPath, poolAddr) - - shouldEQ(t, oldTr01FooBalance-newTr01FooBalance, bigint(16000)) - shouldEQ(t, newTr01BarBalance-oldTr01BarBalance, bigint(43457)) - shouldEQ(t, newPoolFooBalance-oldPoolFooBalance, bigint(16000)) - shouldEQ(t, oldPoolBarBalance-newPoolBarBalance, bigint(43457)) -} - -// 7. Swap Bar:Baz Bar > Baz by tr01 -func TestSwapBarBazBarToBaz(t *testing.T) { - std.TestSetOrigCaller(tr01) - - oldTr01BarBalance := balanceOfByRegisterCall(barPath, tr01) - oldTr01BazBalance := balanceOfByRegisterCall(bazPath, tr01) - oldPoolBarBalance := balanceOfByRegisterCall(barPath, poolAddr) - oldPoolBazBalance := balanceOfByRegisterCall(bazPath, poolAddr) - - poolIn, poolOut := Swap( - barPath, - bazPath, - pFee, - tr01, - true, - bigint(16000), - MIN_PRICE, - ) - shouldEQ(t, poolIn, bigint(16000)) - shouldEQ(t, poolOut, bigint(-43457)) - - newTr01BarBalance := balanceOfByRegisterCall(barPath, tr01) - newTr01BazBalance := balanceOfByRegisterCall(bazPath, tr01) - newPoolBarBalance := balanceOfByRegisterCall(barPath, poolAddr) - newPoolBazBalance := balanceOfByRegisterCall(bazPath, poolAddr) - - shouldEQ(t, oldTr01BarBalance-newTr01BarBalance, bigint(16000)) - shouldEQ(t, newTr01BazBalance-oldTr01BazBalance, bigint(43457)) - shouldEQ(t, newPoolBarBalance-oldPoolBarBalance, bigint(16000)) - shouldEQ(t, oldPoolBazBalance-newPoolBazBalance, bigint(43457)) -} - -// 8. Burn Foo:Bar Liquidity by lp01 -func TestBurnFooBarLiquidity(t *testing.T) { - std.TestSetOrigCaller(lp01) - - -} - -/* HELPER */ -func shouldEQ(t *testing.T, got, expected interface{}) { - if got != expected { - t.Errorf("got %v, expected %v", got, expected) - } -} - -func shouldNEQ(t *testing.T, got, expected interface{}) { - if got == expected { - t.Errorf("got %v, didn't expected %v", got, expected) - } -} - -func shouldPanic(t *testing.T, f func()) { - defer func() { - if r := recover(); r == nil { - t.Errorf("expected panic") - } - }() - f() -} diff --git a/pool/_TEST_pool_multi_token_test.gnoa b/pool/_TEST_pool_multi_token_test.gnoa new file mode 100644 index 00000000..b3e9e6f0 --- /dev/null +++ b/pool/_TEST_pool_multi_token_test.gnoa @@ -0,0 +1,358 @@ +package pool + +import ( + "std" + "testing" + + "gno.land/p/demo/testutils" + + _ "gno.land/r/grc20_wrapper" +) + +var ( + gsa = testutils.TestAddress("gsa") // Gnoswap Admin + 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") +) + +var ( + // Common + fooPath = "gno.land/r/foo" // token1 + barPath = "gno.land/r/bar" // token2 + bazPath = "gno.land/r/baz" // token3 + + pFee = uint16(500) + + test_tickLower = int32(9000) + test_tickUpper = int32(11000) + test_liquidityExpect = bigint(100000000) + + test_tickLower2 = int32(50000) + test_tickUpper2 = int32(100000) +) + +// 1. Init Pool +func TestInit(t *testing.T) { + std.TestSetOrigCaller(gsa) + InitManual() +} + +// 2. Create Foo:Bar Pool +func TestCreateFooBarPool(t *testing.T) { + CreatePool(fooPath, barPath, pFee, 130621891405341611593710811006) + shouldEQ(t, len(pools), 1) +} + +// 3. Create Bar:Baz Pool +func TestCreateBarBazPool(t *testing.T) { + CreatePool(barPath, bazPath, pFee, 130621891405341611593710811006) + shouldEQ(t, len(pools), 2) +} + +// 4. Mint Foo:Bar Liquidity by lp01 +func TestMintFooBarLiquidity(t *testing.T) { + std.TestSetPrevRealm("gno.land/r/position") + std.TestSetOrigCaller(lp01) + + Mint( + fooPath, + barPath, + pFee, + posAddr, + test_tickLower, + test_tickUpper, + test_liquidityExpect, + ) +} + +// 5. Mint Bar:Baz Liquidity by lp01 +func TestMintBarBazLiquidity(t *testing.T) { + std.TestSetPrevRealm("gno.land/r/position") + std.TestSetOrigCaller(lp01) + + Mint( + barPath, + bazPath, + pFee, + posAddr, + test_tickLower, + test_tickUpper, + test_liquidityExpect, + ) +} + +// 6. Swap Foo:Bar Foo > Bar by tr01 +func TestSwapFooBarFooToBar(t *testing.T) { + std.TestSetOrigCaller(tr01) + + oldTr01FooBalance := balanceOfByRegisterCall(fooPath, tr01) + oldTr01BarBalance := balanceOfByRegisterCall(barPath, tr01) + oldPoolFooBalance := balanceOfByRegisterCall(fooPath, poolAddr) + oldPoolBarBalance := balanceOfByRegisterCall(barPath, poolAddr) + + poolIn, poolOut := Swap( + fooPath, + barPath, + pFee, + tr01, + true, + bigint(16000), + MIN_PRICE, + ) + shouldEQ(t, poolIn, bigint(16000)) + shouldEQ(t, poolOut, bigint(-43457)) + + newTr01FooBalance := balanceOfByRegisterCall(fooPath, tr01) + newTr01BarBalance := balanceOfByRegisterCall(barPath, tr01) + newPoolFooBalance := balanceOfByRegisterCall(fooPath, poolAddr) + newPoolBarBalance := balanceOfByRegisterCall(barPath, poolAddr) + + shouldEQ(t, oldTr01FooBalance-newTr01FooBalance, bigint(16000)) + shouldEQ(t, newTr01BarBalance-oldTr01BarBalance, bigint(43457)) + shouldEQ(t, newPoolFooBalance-oldPoolFooBalance, bigint(16000)) + shouldEQ(t, oldPoolBarBalance-newPoolBarBalance, bigint(43457)) +} + +// 7. Swap Bar:Baz Bar > Baz by tr01 +func TestSwapBarBazBarToBaz(t *testing.T) { + std.TestSetOrigCaller(tr01) + + oldTr01BarBalance := balanceOfByRegisterCall(barPath, tr01) + oldTr01BazBalance := balanceOfByRegisterCall(bazPath, tr01) + oldPoolBarBalance := balanceOfByRegisterCall(barPath, poolAddr) + oldPoolBazBalance := balanceOfByRegisterCall(bazPath, poolAddr) + + poolIn, poolOut := Swap( + barPath, + bazPath, + pFee, + tr01, + true, + bigint(16000), + MIN_PRICE, + ) + shouldEQ(t, poolIn, bigint(16000)) + shouldEQ(t, poolOut, bigint(-43457)) + + newTr01BarBalance := balanceOfByRegisterCall(barPath, tr01) + newTr01BazBalance := balanceOfByRegisterCall(bazPath, tr01) + newPoolBarBalance := balanceOfByRegisterCall(barPath, poolAddr) + newPoolBazBalance := balanceOfByRegisterCall(bazPath, poolAddr) + + shouldEQ(t, oldTr01BarBalance-newTr01BarBalance, bigint(16000)) + shouldEQ(t, newTr01BazBalance-oldTr01BazBalance, bigint(43457)) + shouldEQ(t, newPoolBarBalance-oldPoolBarBalance, bigint(16000)) + shouldEQ(t, oldPoolBazBalance-newPoolBazBalance, bigint(43457)) +} + +// 8. Collect Foo:Bar Fees by lp01 +func TestCollectFooBarFees(t *testing.T) { + std.TestSetOrigCaller(lp01) + std.TestSetPrevRealm("gno.land/r/position") + + oldLp01FooBalance := balanceOfByRegisterCall(fooPath, lp01) + oldLp01BarBalance := balanceOfByRegisterCall(barPath, lp01) + oldPoolFooBalance := balanceOfByRegisterCall(fooPath, poolAddr) + oldPoolBarBalance := balanceOfByRegisterCall(barPath, poolAddr) + + // burn 0 to collect swap fees + Burn(fooPath, barPath, pFee, test_tickLower, test_tickUpper, 0) + + c0, c1 := Collect( + fooPath, + barPath, + pFee, + lp01, + test_tickLower, + test_tickUpper, + 100_000, + 100_000, + ) + + shouldNEQ(t, c0, bigint(0)) // swap was foo > bar, so only foo has fees + shouldEQ(t, c1, bigint(0)) // swap was foo > bar, so bar has no fees + + newLp01FooBalance := balanceOfByRegisterCall(fooPath, lp01) + newLp01BarBalance := balanceOfByRegisterCall(barPath, lp01) + newPoolFooBalance := balanceOfByRegisterCall(fooPath, poolAddr) + newPoolBarBalance := balanceOfByRegisterCall(barPath, poolAddr) + + shouldEQ(t, newLp01FooBalance-oldLp01FooBalance, uint64(c0)) + shouldEQ(t, newLp01BarBalance-oldLp01BarBalance, uint64(c1)) + shouldEQ(t, oldPoolFooBalance-newPoolFooBalance, uint64(c0)) + shouldEQ(t, oldPoolBarBalance-newPoolBarBalance, uint64(c1)) +} + +// 9. Collect Bar:Baz Fees by lp01 +func TestCollectBarBazFees(t *testing.T) { + std.TestSetOrigCaller(lp01) + std.TestSetPrevRealm("gno.land/r/position") + + oldLp01BarBalance := balanceOfByRegisterCall(barPath, lp01) + oldLp01BazBalance := balanceOfByRegisterCall(bazPath, lp01) + oldPoolBarBalance := balanceOfByRegisterCall(barPath, poolAddr) + oldPoolBazBalance := balanceOfByRegisterCall(bazPath, poolAddr) + + // burn 0 to collect swap fees + Burn(barPath, bazPath, pFee, test_tickLower, test_tickUpper, 0) + + c0, c1 := Collect( + barPath, + bazPath, + pFee, + lp01, + test_tickLower, + test_tickUpper, + 100_000, + 100_000, + ) + + shouldNEQ(t, c0, bigint(0)) // swap was foo > bar, so only foo has fees + shouldEQ(t, c1, bigint(0)) // swap was foo > bar, so bar has no fees + + newLp01BarBalance := balanceOfByRegisterCall(barPath, lp01) + newLp01BazBalance := balanceOfByRegisterCall(bazPath, lp01) + newPoolBarBalance := balanceOfByRegisterCall(barPath, poolAddr) + newPoolBazBalance := balanceOfByRegisterCall(bazPath, poolAddr) + + shouldEQ(t, newLp01BarBalance-oldLp01BarBalance, uint64(c0)) + shouldEQ(t, newLp01BazBalance-oldLp01BazBalance, uint64(c1)) + shouldEQ(t, oldPoolBarBalance-newPoolBarBalance, uint64(c0)) + shouldEQ(t, oldPoolBazBalance-newPoolBazBalance, uint64(c1)) +} + +// 10. Burn Foo:Bar Liquidity by lp01 +func TestBurnFooBarLiquidity(t *testing.T) { + std.TestSetOrigCaller(lp01) + std.TestSetPrevRealm("gno.land/r/position") + + pool := GetPool(fooPath, barPath, pFee) + poolOldLiquidity := pool.GetLiquidity() + + b0, b1 := Burn( + fooPath, + barPath, + pFee, + test_tickLower, + test_tickUpper, + test_liquidityExpect, + ) + + shouldNEQ(t, b0, bigint(0)) + shouldNEQ(t, b1, bigint(0)) + + poolNewLiquidity := pool.GetLiquidity() + + shouldEQ(t, poolOldLiquidity-poolNewLiquidity, test_liquidityExpect) +} + +// 11. Burn Bar:Baz Liquidity by lp01 +func TestBurnBarBazLiquidity(t *testing.T) { + std.TestSetOrigCaller(lp01) + std.TestSetPrevRealm("gno.land/r/position") + + pool := GetPool(barPath, bazPath, pFee) + poolOldLiquidity := pool.GetLiquidity() + + b0, b1 := Burn( + barPath, + bazPath, + pFee, + test_tickLower, + test_tickUpper, + test_liquidityExpect, + ) + + shouldNEQ(t, b0, bigint(0)) + shouldNEQ(t, b1, bigint(0)) + + poolNewLiquidity := pool.GetLiquidity() + + shouldEQ(t, poolOldLiquidity-poolNewLiquidity, test_liquidityExpect) +} + +// 12. Collect Foo:Bar burned Liquidity by lp01 +func TestCollectFooBarLiquidity(t *testing.T) { + std.TestSetOrigCaller(lp01) + std.TestSetPrevRealm("gno.land/r/position") + + oldLp01FooBalance := balanceOfByRegisterCall(fooPath, lp01) + oldLp01BarBalance := balanceOfByRegisterCall(barPath, lp01) + oldPoolFooBalance := balanceOfByRegisterCall(fooPath, poolAddr) + oldPoolBarBalance := balanceOfByRegisterCall(barPath, poolAddr) + + c0, c1 := Collect( + fooPath, + barPath, + pFee, + lp01, + test_tickLower, + test_tickUpper, + 100_000, + 100_000, + ) + + shouldNEQ(t, c0, bigint(0)) + shouldNEQ(t, c1, bigint(0)) + + newLp01FooBalance := balanceOfByRegisterCall(fooPath, lp01) + newLp01BarBalance := balanceOfByRegisterCall(barPath, lp01) + newPoolFooBalance := balanceOfByRegisterCall(fooPath, poolAddr) + newPoolBarBalance := balanceOfByRegisterCall(barPath, poolAddr) + + shouldEQ(t, newLp01FooBalance-oldLp01FooBalance, uint64(c0)) + shouldEQ(t, newLp01BarBalance-oldLp01BarBalance, uint64(c1)) + shouldEQ(t, oldPoolFooBalance-newPoolFooBalance, uint64(c0)) + shouldEQ(t, oldPoolBarBalance-newPoolBarBalance, uint64(c1)) +} + +// 13. Collect Bar:Baz burned Liquidity by lp01 +func TestCollectBarBazLiquidity(t *testing.T) { + std.TestSetOrigCaller(lp01) + std.TestSetPrevRealm("gno.land/r/position") + + oldLp01BarBalance := balanceOfByRegisterCall(barPath, lp01) + oldLp01BazBalance := balanceOfByRegisterCall(bazPath, lp01) + oldPoolBarBalance := balanceOfByRegisterCall(barPath, poolAddr) + oldPoolBazBalance := balanceOfByRegisterCall(bazPath, poolAddr) + + c0, c1 := Collect( + barPath, + bazPath, + pFee, + lp01, + test_tickLower, + test_tickUpper, + 100_000, + 100_000, + ) + + shouldNEQ(t, c0, bigint(0)) + shouldNEQ(t, c1, bigint(0)) + + newLp01BarBalance := balanceOfByRegisterCall(barPath, lp01) + newLp01BazBalance := balanceOfByRegisterCall(bazPath, lp01) + newPoolBarBalance := balanceOfByRegisterCall(barPath, poolAddr) + newPoolBazBalance := balanceOfByRegisterCall(bazPath, poolAddr) + + shouldEQ(t, newLp01BarBalance-oldLp01BarBalance, uint64(c0)) + shouldEQ(t, newLp01BazBalance-oldLp01BazBalance, uint64(c1)) + shouldEQ(t, oldPoolBarBalance-newPoolBarBalance, uint64(c0)) + shouldEQ(t, oldPoolBazBalance-newPoolBazBalance, uint64(c1)) +} + +/* HELPER */ +func shouldEQ(t *testing.T, got, expected interface{}) { + if got != expected { + t.Errorf("got %v, expected %v", got, expected) + } +} + +func shouldNEQ(t *testing.T, got, expected interface{}) { + if got == expected { + t.Errorf("got %v, didn't expected %v", got, expected) + } +} diff --git a/pool/_TEST_pool_router_test.gnoa b/pool/_TEST_pool_router_test.gnoa deleted file mode 100644 index 1a036894..00000000 --- a/pool/_TEST_pool_router_test.gnoa +++ /dev/null @@ -1,161 +0,0 @@ -package pool - -import ( - "std" - "testing" - - "encoding/gjson" - - "gno.land/p/demo/testutils" - "gno.land/r/demo/users" - - bar "gno.land/r/bar" - foo "gno.land/r/foo" - - pos "gno.land/r/position" -) - -var ( - gsa = testutils.TestAddress("gsa") // Gnoswap Admin - lp01 = testutils.TestAddress("lp01") // Liquidity Provider 01 - - poolAddr = std.DerivePkgAddr("gno.land/r/pool") -) - -// 1. Init -func TestInitManual(t *testing.T) { - std.TestSetOrigCaller(gsa) - InitManual() - std.TestSkipHeights(1) -} - -func TestCreatePool(t *testing.T) { - CreatePool("foo", "bar", uint16(500), 130621891405341611593710811006) // 2.7181459268 - CreatePool("foo", "bar", uint16(3000), 130621891405341611593710811006) // 2.7181459268 - std.TestSkipHeights(1) -} - -func TestPositionMint500(t *testing.T) { - std.TestSetOrigCaller(lp01) - - _, _, m0, m1 := pos.Mint( - "foo", // token0 - "bar", // token1 - uint16(500), // fee 3000 ~= tickSpacing 60 - 6600, // tickLower - 10200, // tickUpper - 100, // amount0Desired - 100, // amount1Desired - 0, // amount0Min - 0, // amount1Min - 9999999999, // deadline - ) - shouldEQ(t, m0, bigint(2)) - shouldEQ(t, m1, bigint(99)) -} - -func TestPositionMint3000(t *testing.T) { - std.TestSetOrigCaller(lp01) - - _, _, m0, m1 := pos.Mint( - "foo", // token0 - "bar", // token1 - uint16(3000), // fee 3000 ~= tickSpacing 60 - 6600, // tickLower - 10200, // tickUpper - 1000, // amount0Desired - 1000, // amount1Desired - 0, // amount0Min - 0, // amount1Min - 9999999999, // deadline - ) - shouldEQ(t, m0, bigint(23)) - shouldEQ(t, m1, bigint(999)) -} - -func TestFindBestPoolTruePositive(t *testing.T) { - bestPoolPathDetail := FindBestPool( - "foo", // tokenA - "bar", // tokenB - true, // zeroForOne - 200, // amountSpecified - ) - jsonStr := gjson.Parse(bestPoolPathDetail) - shouldEQ(t, jsonStr.Get("response.data.pool_path").String(), "bar_foo_3000") - - // should return empty if not enough balance - { - bestPoolPathDetail := FindBestPool( - "foo", // tokenA - "bar", // tokenB - true, // zeroForOne - 438, // amountSpecified - ) - - jsonStr := gjson.Parse(bestPoolPathDetail) - shouldEQ(t, jsonStr.Get("response.data.pool_path").String(), "") - } -} - -func TestFindBestPoolTrueNegative(t *testing.T) { - bestPoolPathDetail := FindBestPool( - "foo", // tokenA - "bar", // tokenB - true, // zeroForOne - -888, // amountSpecified - ) - jsonStr := gjson.Parse(bestPoolPathDetail) - shouldEQ(t, jsonStr.Get("response.data.pool_path").String(), "bar_foo_3000") -} - -func TestFindBestPoolFalsePositive(t *testing.T) { - bestPoolPathDetail := FindBestPool( - "foo", // tokenA - "bar", // tokenB - false, // zeroForOne - 5, // amountSpecified - ) - jsonStr := gjson.Parse(bestPoolPathDetail) - shouldEQ(t, jsonStr.Get("response.data.pool_path").String(), "bar_foo_3000") -} - -func TestFindBestPoolFalseNegative(t *testing.T) { - bestPoolPathDetail := FindBestPool( - "foo", // tokenA - "bar", // tokenB - false, // zeroForOne - -11, // amountSpecified - ) - jsonStr := gjson.Parse(bestPoolPathDetail) - shouldEQ(t, jsonStr.Get("response.data.pool_path").String(), "bar_foo_3000") -} - -func TestFindBestPoolWrong(t *testing.T) { - shouldPanic(t, func() { FindBestPool("foo", "bar", true, 0) }) -} - -/* HELPER */ -func shouldEQ(t *testing.T, got, expected interface{}) { - if got != expected { - t.Errorf("got %v, expected %v", got, expected) - } -} - -func shouldPanic(t *testing.T, f func()) { - defer func() { - if r := recover(); r == nil { - t.Errorf("expected panic") - } - }() - f() -} - -func fooBalance(addr std.Address) uint64 { - user := users.AddressOrName(addr) - return foo.BalanceOf(user) -} - -func barBalance(addr std.Address) uint64 { - user := users.AddressOrName(addr) - return bar.BalanceOf(user) -} diff --git a/pool/_TEST_pool_same_token_pair_diff_fee_test.gnoa b/pool/_TEST_pool_same_token_pair_diff_fee_test.gnoa index 5969ddf2..28b87dd5 100644 --- a/pool/_TEST_pool_same_token_pair_diff_fee_test.gnoa +++ b/pool/_TEST_pool_same_token_pair_diff_fee_test.gnoa @@ -6,10 +6,8 @@ import ( "testing" "gno.land/p/demo/testutils" - "gno.land/r/demo/users" - bar "gno.land/r/bar" - foo "gno.land/r/foo" + _ "gno.land/r/grc20_wrapper" ) var ( @@ -17,13 +15,14 @@ var ( lp01 = testutils.TestAddress("lp01") // Liquidity Provider 01 tr01 = testutils.TestAddress("tr01") // Trader 01 - posAddr = std.DerivePkgAddr("gno.land/r/position") + poolAddr = std.DerivePkgAddr("gno.land/r/pool") + posAddr = std.DerivePkgAddr("gno.land/r/position") ) var ( // Common - pToken0 = "foo" - pToken1 = "bar" + fooPath = "gno.land/r/foo" // token1 + barPath = "gno.land/r/bar" // token2 pFee1 = uint16(500) test_tickLower1 = int32(9000) @@ -36,155 +35,169 @@ var ( test_liquidityExpect = bigint(1000) ) -// foo & bar & 500 func TestFirstPool(t *testing.T) { std.TestSetOrigCaller(gsa) InitManual() - CreatePool(pToken0, pToken1, pFee1, 130621891405341611593710811006) + CreatePool(fooPath, barPath, pFee1, 130621891405341611593710811006) - shouldPanic(t, func() { CreatePool(pToken0, pToken1, pFee1, 130621891405341611593710811006) }) + shouldPanic(t, func() { CreatePool(fooPath, barPath, pFee1, 130621891405341611593710811006) }) // Test Only Swap - pool := GetPool(pToken0, pToken1, pFee1) + pool := GetPool(fooPath, barPath, pFee1) test_liquidity := pool.GetLiquidity() shouldEQ(t, test_liquidity, bigint(0)) std.TestSetPrevRealm("gno.land/r/position") std.TestSetOrigCaller(lp01) - Mint(pToken0, pToken1, pFee1, posAddr, test_tickLower1, test_tickUpper1, test_liquidityExpect*20000) + Mint(fooPath, barPath, pFee1, posAddr, test_tickLower1, test_tickUpper1, test_liquidityExpect*100000) + + // clear prev realm std.TestSetPrevRealm("") // Swap several times std.TestSetOrigCaller(tr01) - test_price := bigint(MIN_SQRT_RATIO + 1) // maximum price - { - poolOldToken0Bal := balanceOf(pool.token0, GetOrigPkgAddr()) - poolOldToken1Bal := balanceOf(pool.token1, GetOrigPkgAddr()) + poolOldToken0Bal := balanceOfByRegisterCall(fooPath, poolAddr) + poolOldToken1Bal := balanceOfByRegisterCall(barPath, poolAddr) - userOldToken0Bal := balanceOf(pool.token0, tr01) - userOldToken1Bal := balanceOf(pool.token1, tr01) + userOldToken0Bal := balanceOfByRegisterCall(fooPath, tr01) + userOldToken1Bal := balanceOfByRegisterCall(barPath, tr01) - amount0, amount1 := Swap(pToken0, pToken1, pFee1, tr01, true, bigint(10000), test_price) + amount0, amount1 := Swap(fooPath, barPath, pFee1, tr01, true, bigint(10000), MIN_PRICE) shouldNEQ(t, amount0, bigint(0)) shouldNEQ(t, amount1, bigint(0)) - poolNewToken0Bal := balanceOf(pool.token0, GetOrigPkgAddr()) - poolNewToken1Bal := balanceOf(pool.token1, GetOrigPkgAddr()) + poolNewToken0Bal := balanceOfByRegisterCall(fooPath, poolAddr) + poolNewToken1Bal := balanceOfByRegisterCall(barPath, poolAddr) - userNewToken0Bal := balanceOf(pool.token0, tr01) - userNewToken1Bal := balanceOf(pool.token1, tr01) + userNewToken0Bal := balanceOfByRegisterCall(fooPath, tr01) + userNewToken1Bal := balanceOfByRegisterCall(barPath, tr01) - shouldEQ(t, userOldToken0Bal-amount0, userNewToken0Bal) - shouldEQ(t, userOldToken1Bal-amount1, userNewToken1Bal) - shouldEQ(t, poolOldToken0Bal+amount0, poolNewToken0Bal) - shouldEQ(t, poolOldToken1Bal+amount1, poolNewToken1Bal) + shouldEQ(t, userOldToken0Bal-userNewToken0Bal, int64(amount0)) + shouldEQ(t, userNewToken1Bal-userOldToken1Bal, int64(-amount1)) + shouldEQ(t, poolNewToken0Bal-poolOldToken0Bal, int64(amount0)) + shouldEQ(t, poolOldToken1Bal-poolNewToken1Bal, int64(-amount1)) } { - poolOldToken0Bal := balanceOf(pool.token0, GetOrigPkgAddr()) - poolOldToken1Bal := balanceOf(pool.token1, GetOrigPkgAddr()) + poolOldToken0Bal := balanceOfByRegisterCall(fooPath, poolAddr) + poolOldToken1Bal := balanceOfByRegisterCall(barPath, poolAddr) + + userOldToken0Bal := balanceOfByRegisterCall(fooPath, tr01) + userOldToken1Bal := balanceOfByRegisterCall(barPath, tr01) - userOldToken0Bal := balanceOf(pool.token0, tr01) - userOldToken1Bal := balanceOf(pool.token1, tr01) + amount0, amount1 := Swap(fooPath, barPath, pFee1, tr01, true, bigint(5000), MIN_PRICE) - amount0, amount1 := Swap(pToken0, pToken1, pFee1, tr01, true, 5000, test_price) // give enough amount to take fees away + shouldNEQ(t, amount0, bigint(0)) + shouldNEQ(t, amount1, bigint(0)) - poolNewToken0Bal := balanceOf(pool.token0, GetOrigPkgAddr()) - poolNewToken1Bal := balanceOf(pool.token1, GetOrigPkgAddr()) + poolNewToken0Bal := balanceOfByRegisterCall(fooPath, poolAddr) + poolNewToken1Bal := balanceOfByRegisterCall(barPath, poolAddr) - userNewToken0Bal := balanceOf(pool.token0, tr01) - userNewToken1Bal := balanceOf(pool.token1, tr01) + userNewToken0Bal := balanceOfByRegisterCall(fooPath, tr01) + userNewToken1Bal := balanceOfByRegisterCall(barPath, tr01) - shouldEQ(t, userOldToken0Bal-amount0, userNewToken0Bal) - shouldEQ(t, userOldToken1Bal-amount1, userNewToken1Bal) - shouldEQ(t, poolOldToken0Bal+amount0, poolNewToken0Bal) - shouldEQ(t, poolOldToken1Bal+amount1, poolNewToken1Bal) + shouldEQ(t, userOldToken0Bal-userNewToken0Bal, int64(amount0)) + shouldEQ(t, userNewToken1Bal-userOldToken1Bal, int64(-amount1)) + shouldEQ(t, poolNewToken0Bal-poolOldToken0Bal, int64(amount0)) + shouldEQ(t, poolOldToken1Bal-poolNewToken1Bal, int64(-amount1)) } { - poolOldToken0Bal := balanceOf(pool.token0, GetOrigPkgAddr()) - poolOldToken1Bal := balanceOf(pool.token1, GetOrigPkgAddr()) + poolOldToken0Bal := balanceOfByRegisterCall(fooPath, poolAddr) + poolOldToken1Bal := balanceOfByRegisterCall(barPath, poolAddr) - userOldToken0Bal := balanceOf(pool.token0, tr01) - userOldToken1Bal := balanceOf(pool.token1, tr01) + userOldToken0Bal := balanceOfByRegisterCall(fooPath, tr01) + userOldToken1Bal := balanceOfByRegisterCall(barPath, tr01) - amount0, amount1 := Swap(pToken0, pToken1, pFee1, tr01, true, 1000, test_price) // give enough amount to take fees away + amount0, amount1 := Swap(fooPath, barPath, pFee1, tr01, true, bigint(1000), MIN_PRICE) - poolNewToken0Bal := balanceOf(pool.token0, GetOrigPkgAddr()) - poolNewToken1Bal := balanceOf(pool.token1, GetOrigPkgAddr()) + shouldNEQ(t, amount0, bigint(0)) + shouldNEQ(t, amount1, bigint(0)) - userNewToken0Bal := balanceOf(pool.token0, tr01) - userNewToken1Bal := balanceOf(pool.token1, tr01) + poolNewToken0Bal := balanceOfByRegisterCall(fooPath, poolAddr) + poolNewToken1Bal := balanceOfByRegisterCall(barPath, poolAddr) - shouldEQ(t, userOldToken0Bal-amount0, userNewToken0Bal) - shouldEQ(t, userOldToken1Bal-amount1, userNewToken1Bal) - shouldEQ(t, poolOldToken0Bal+amount0, poolNewToken0Bal) - shouldEQ(t, poolOldToken1Bal+amount1, poolNewToken1Bal) + userNewToken0Bal := balanceOfByRegisterCall(fooPath, tr01) + userNewToken1Bal := balanceOfByRegisterCall(barPath, tr01) + + shouldEQ(t, userOldToken0Bal-userNewToken0Bal, int64(amount0)) + shouldEQ(t, userNewToken1Bal-userOldToken1Bal, int64(-amount1)) + shouldEQ(t, poolNewToken0Bal-poolOldToken0Bal, int64(amount0)) + shouldEQ(t, poolOldToken1Bal-poolNewToken1Bal, int64(-amount1)) } - // Swap toek1n -> token0 + // Swap token1 -> token0 { - poolOldToken0Bal := balanceOf(pool.token0, GetOrigPkgAddr()) - poolOldToken1Bal := balanceOf(pool.token1, GetOrigPkgAddr()) + poolOldToken0Bal := balanceOfByRegisterCall(fooPath, poolAddr) + poolOldToken1Bal := balanceOfByRegisterCall(barPath, poolAddr) + + userOldToken0Bal := balanceOfByRegisterCall(fooPath, tr01) + userOldToken1Bal := balanceOfByRegisterCall(barPath, tr01) - userOldToken0Bal := balanceOf(pool.token0, tr01) - userOldToken1Bal := balanceOf(pool.token1, tr01) + amount0, amount1 := Swap(fooPath, barPath, pFee1, tr01, false, bigint(160000), MAX_PRICE) - amount0, amount1 := Swap(pToken0, pToken1, pFee1, tr01, false, 16000, (MAX_SQRT_RATIO - 1)) // give enough amount to take fees away + shouldNEQ(t, amount0, bigint(0)) + shouldNEQ(t, amount1, bigint(0)) - poolNewToken0Bal := balanceOf(pool.token0, GetOrigPkgAddr()) - poolNewToken1Bal := balanceOf(pool.token1, GetOrigPkgAddr()) + poolNewToken0Bal := balanceOfByRegisterCall(fooPath, poolAddr) + poolNewToken1Bal := balanceOfByRegisterCall(barPath, poolAddr) - userNewToken0Bal := balanceOf(pool.token0, tr01) - userNewToken1Bal := balanceOf(pool.token1, tr01) + userNewToken0Bal := balanceOfByRegisterCall(fooPath, tr01) + userNewToken1Bal := balanceOfByRegisterCall(barPath, tr01) - shouldEQ(t, userOldToken0Bal-amount0, userNewToken0Bal) - shouldEQ(t, userOldToken1Bal-amount1, userNewToken1Bal) - shouldEQ(t, poolOldToken0Bal+amount0, poolNewToken0Bal) - shouldEQ(t, poolOldToken1Bal+amount1, poolNewToken1Bal) + shouldEQ(t, userOldToken0Bal-userNewToken0Bal, int64(amount0)) + shouldEQ(t, userNewToken1Bal-userOldToken1Bal, int64(-amount1)) + shouldEQ(t, poolNewToken0Bal-poolOldToken0Bal, int64(amount0)) + shouldEQ(t, poolOldToken1Bal-poolNewToken1Bal, int64(-amount1)) } // Swap with Protocol std.TestSetOrigCaller(gsa) - SetFeeProtocol(6, 8) + test_slot0 := pool.GetSlot0() shouldEQ(t, test_slot0.feeProtocol, bigint(134)) // Make ProtocolFee via Swap by tr01 ( Mint by lp01 ) std.TestSetOrigCaller(lp01) { - gsaOldToken0Bal := balanceOf(pool.token0, gsa) - gsaOldToken1Bal := balanceOf(pool.token1, gsa) + gsaOldToken0Bal := balanceOfByRegisterCall(fooPath, gsa) + gsaOldToken1Bal := balanceOfByRegisterCall(barPath, gsa) std.TestSetOrigCaller(tr01) - Swap(pToken0, pToken1, pFee1, tr01, true, 100000, MIN_SQRT_RATIO+1) // swap token0 -> token1 => fee only in token0 - Swap(pToken0, pToken1, pFee1, tr01, true, 100000, MIN_SQRT_RATIO+1) // more protocol fee + Swap(fooPath, barPath, pFee1, tr01, true, 100000, MIN_PRICE) // swap token0 -> token1 => fee only in token0 + Swap(fooPath, barPath, pFee1, tr01, true, 100000, MIN_PRICE) // more protocol fee // Gnoswap Admin will collect protocol fee std.TestSetOrigCaller(gsa) - amount0, amount1 := CollectProtocol(pToken0, pToken1, pFee1, gsa, 100000, 100000) + amount0, amount1 := CollectProtocol(fooPath, barPath, pFee1, gsa, 100000, 100000) + + gsaNewToken0Bal := balanceOfByRegisterCall(fooPath, gsa) + gsaNewToken1Bal := balanceOfByRegisterCall(barPath, gsa) - gsaNewToken0Bal := balanceOf(pool.token0, gsa) - gsaNewToken1Bal := balanceOf(pool.token1, gsa) + shouldEQ(t, gsaNewToken0Bal-gsaOldToken0Bal, int64(amount0)) + shouldEQ(t, gsaNewToken1Bal-gsaOldToken1Bal, int64(amount1)) } { - gsaOldToken0Bal := balanceOf(pool.token0, gsa) - gsaOldToken1Bal := balanceOf(pool.token1, gsa) + gsaOldToken0Bal := balanceOfByRegisterCall(fooPath, gsa) + gsaOldToken1Bal := balanceOfByRegisterCall(barPath, gsa) std.TestSetOrigCaller(tr01) - Swap(pToken0, pToken1, pFee1, tr01, false, 100000, MAX_SQRT_RATIO-1) // swap token0 -> token1 => fee only in token0 - Swap(pToken0, pToken1, pFee1, tr01, false, 100000, MAX_SQRT_RATIO-1) // more protocol fee + Swap(fooPath, barPath, pFee1, tr01, false, 100000, MAX_PRICE) // swap token0 -> token1 => fee only in token0 + Swap(fooPath, barPath, pFee1, tr01, false, 100000, MAX_PRICE) // more protocol fee // Gnoswap Admin will collect protocol fee std.TestSetOrigCaller(gsa) - amount0, amount1 := CollectProtocol(pToken0, pToken1, pFee1, gsa, 100000, 100000) + amount0, amount1 := CollectProtocol(fooPath, barPath, pFee1, gsa, 100000, 100000) - gsaNewToken0Bal := balanceOf(pool.token0, gsa) - gsaNewToken1Bal := balanceOf(pool.token1, gsa) + gsaNewToken0Bal := balanceOfByRegisterCall(fooPath, gsa) + gsaNewToken1Bal := balanceOfByRegisterCall(barPath, gsa) + + shouldEQ(t, gsaNewToken0Bal-gsaOldToken0Bal, int64(amount0)) + shouldEQ(t, gsaNewToken1Bal-gsaOldToken1Bal, int64(amount1)) } } @@ -192,164 +205,166 @@ func TestFirstPool(t *testing.T) { func TestSecondPool(t *testing.T) { std.TestSetOrigCaller(gsa) // Init() - CreatePool(pToken0, pToken1, pFee2, 130621891405341611593710811006) - - shouldPanic(t, func() { CreatePool(pToken0, pToken1, pFee2, 130621891405341611593710811006) }) - - // Approve - std.TestSetOrigCaller(lp01) - foo.Approve(users.AddressOrName(GetOrigPkgAddr()), 50000000) - bar.Approve(users.AddressOrName(GetOrigPkgAddr()), 50000000) - foo.Approve(users.AddressOrName(lp01), 50000000) - bar.Approve(users.AddressOrName(lp01), 50000000) + CreatePool(fooPath, barPath, pFee2, 130621891405341611593710811006) - std.TestSetOrigCaller(tr01) - foo.Approve(users.AddressOrName(GetOrigPkgAddr()), 50000000) - bar.Approve(users.AddressOrName(GetOrigPkgAddr()), 50000000) - foo.Approve(users.AddressOrName(tr01), 50000000) - bar.Approve(users.AddressOrName(tr01), 50000000) + shouldPanic(t, func() { CreatePool(fooPath, barPath, pFee2, 130621891405341611593710811006) }) // Test Only Swap - pool := GetPool(pToken0, pToken1, pFee2) + pool := GetPool(fooPath, barPath, pFee2) test_liquidity := pool.GetLiquidity() shouldEQ(t, test_liquidity, bigint(0)) - std.TestSetOrigCaller(lp01) std.TestSetPrevRealm("gno.land/r/position") - m1, m2 := Mint(pToken0, pToken1, pFee2, posAddr, test_tickLower2, test_tickUpper2, test_liquidityExpect*50000) + std.TestSetOrigCaller(lp01) + Mint(fooPath, barPath, pFee2, posAddr, test_tickLower2, test_tickUpper2, test_liquidityExpect*50000) + + // clear prev realm std.TestSetPrevRealm("") // Swap several times std.TestSetOrigCaller(tr01) - test_price := bigint(MIN_SQRT_RATIO + 1) // maximum price - { - poolOldToken0Bal := balanceOf(pool.token0, GetOrigPkgAddr()) - poolOldToken1Bal := balanceOf(pool.token1, GetOrigPkgAddr()) + poolOldToken0Bal := balanceOfByRegisterCall(fooPath, poolAddr) + poolOldToken1Bal := balanceOfByRegisterCall(barPath, poolAddr) - userOldToken0Bal := balanceOf(pool.token0, tr01) - userOldToken1Bal := balanceOf(pool.token1, tr01) + userOldToken0Bal := balanceOfByRegisterCall(fooPath, tr01) + userOldToken1Bal := balanceOfByRegisterCall(barPath, tr01) - amount0, amount1 := Swap(pToken0, pToken1, pFee2, tr01, true, 50000, test_price) + amount0, amount1 := Swap(fooPath, barPath, pFee2, tr01, true, bigint(10000), MIN_PRICE) shouldNEQ(t, amount0, bigint(0)) shouldNEQ(t, amount1, bigint(0)) - poolNewToken0Bal := balanceOf(pool.token0, GetOrigPkgAddr()) - poolNewToken1Bal := balanceOf(pool.token1, GetOrigPkgAddr()) + poolNewToken0Bal := balanceOfByRegisterCall(fooPath, poolAddr) + poolNewToken1Bal := balanceOfByRegisterCall(barPath, poolAddr) - userNewToken0Bal := balanceOf(pool.token0, tr01) - userNewToken1Bal := balanceOf(pool.token1, tr01) + userNewToken0Bal := balanceOfByRegisterCall(fooPath, tr01) + userNewToken1Bal := balanceOfByRegisterCall(barPath, tr01) - shouldEQ(t, userOldToken0Bal-amount0, userNewToken0Bal) - shouldEQ(t, userOldToken1Bal-amount1, userNewToken1Bal) - shouldEQ(t, poolOldToken0Bal+amount0, poolNewToken0Bal) - shouldEQ(t, poolOldToken1Bal+amount1, poolNewToken1Bal) + shouldEQ(t, userOldToken0Bal-userNewToken0Bal, int64(amount0)) + shouldEQ(t, userNewToken1Bal-userOldToken1Bal, int64(-amount1)) + shouldEQ(t, poolNewToken0Bal-poolOldToken0Bal, int64(amount0)) + shouldEQ(t, poolOldToken1Bal-poolNewToken1Bal, int64(-amount1)) } { - poolOldToken0Bal := balanceOf(pool.token0, GetOrigPkgAddr()) - poolOldToken1Bal := balanceOf(pool.token1, GetOrigPkgAddr()) + poolOldToken0Bal := balanceOfByRegisterCall(fooPath, poolAddr) + poolOldToken1Bal := balanceOfByRegisterCall(barPath, poolAddr) - userOldToken0Bal := balanceOf(pool.token0, tr01) - userOldToken1Bal := balanceOf(pool.token1, tr01) + userOldToken0Bal := balanceOfByRegisterCall(fooPath, tr01) + userOldToken1Bal := balanceOfByRegisterCall(barPath, tr01) - amount0, amount1 := Swap(pToken0, pToken1, pFee2, tr01, true, 5000, test_price) // give enough amount to take fees away + amount0, amount1 := Swap(fooPath, barPath, pFee2, tr01, true, bigint(5000), MIN_PRICE) - poolNewToken0Bal := balanceOf(pool.token0, GetOrigPkgAddr()) - poolNewToken1Bal := balanceOf(pool.token1, GetOrigPkgAddr()) + shouldNEQ(t, amount0, bigint(0)) + shouldNEQ(t, amount1, bigint(0)) - userNewToken0Bal := balanceOf(pool.token0, tr01) - userNewToken1Bal := balanceOf(pool.token1, tr01) + poolNewToken0Bal := balanceOfByRegisterCall(fooPath, poolAddr) + poolNewToken1Bal := balanceOfByRegisterCall(barPath, poolAddr) - shouldEQ(t, userOldToken0Bal-amount0, userNewToken0Bal) - shouldEQ(t, userOldToken1Bal-amount1, userNewToken1Bal) - shouldEQ(t, poolOldToken0Bal+amount0, poolNewToken0Bal) - shouldEQ(t, poolOldToken1Bal+amount1, poolNewToken1Bal) + userNewToken0Bal := balanceOfByRegisterCall(fooPath, tr01) + userNewToken1Bal := balanceOfByRegisterCall(barPath, tr01) + + shouldEQ(t, userOldToken0Bal-userNewToken0Bal, int64(amount0)) + shouldEQ(t, userNewToken1Bal-userOldToken1Bal, int64(-amount1)) + shouldEQ(t, poolNewToken0Bal-poolOldToken0Bal, int64(amount0)) + shouldEQ(t, poolOldToken1Bal-poolNewToken1Bal, int64(-amount1)) } { - poolOldToken0Bal := balanceOf(pool.token0, GetOrigPkgAddr()) - poolOldToken1Bal := balanceOf(pool.token1, GetOrigPkgAddr()) + poolOldToken0Bal := balanceOfByRegisterCall(fooPath, poolAddr) + poolOldToken1Bal := balanceOfByRegisterCall(barPath, poolAddr) + + userOldToken0Bal := balanceOfByRegisterCall(fooPath, tr01) + userOldToken1Bal := balanceOfByRegisterCall(barPath, tr01) - userOldToken0Bal := balanceOf(pool.token0, tr01) - userOldToken1Bal := balanceOf(pool.token1, tr01) + amount0, amount1 := Swap(fooPath, barPath, pFee2, tr01, true, bigint(1000), MIN_PRICE) - amount0, amount1 := Swap(pToken0, pToken1, pFee2, tr01, true, 1000, test_price) // give enough amount to take fees away + shouldNEQ(t, amount0, bigint(0)) + shouldNEQ(t, amount1, bigint(0)) - poolNewToken0Bal := balanceOf(pool.token0, GetOrigPkgAddr()) - poolNewToken1Bal := balanceOf(pool.token1, GetOrigPkgAddr()) + poolNewToken0Bal := balanceOfByRegisterCall(fooPath, poolAddr) + poolNewToken1Bal := balanceOfByRegisterCall(barPath, poolAddr) - userNewToken0Bal := balanceOf(pool.token0, tr01) - userNewToken1Bal := balanceOf(pool.token1, tr01) + userNewToken0Bal := balanceOfByRegisterCall(fooPath, tr01) + userNewToken1Bal := balanceOfByRegisterCall(barPath, tr01) - shouldEQ(t, userOldToken0Bal-amount0, userNewToken0Bal) - shouldEQ(t, userOldToken1Bal-amount1, userNewToken1Bal) - shouldEQ(t, poolOldToken0Bal+amount0, poolNewToken0Bal) - shouldEQ(t, poolOldToken1Bal+amount1, poolNewToken1Bal) + shouldEQ(t, userOldToken0Bal-userNewToken0Bal, int64(amount0)) + shouldEQ(t, userNewToken1Bal-userOldToken1Bal, int64(-amount1)) + shouldEQ(t, poolNewToken0Bal-poolOldToken0Bal, int64(amount0)) + shouldEQ(t, poolOldToken1Bal-poolNewToken1Bal, int64(-amount1)) } - // Swap toek1n -> token0 + // Swap token1 -> token0 { - poolOldToken0Bal := balanceOf(pool.token0, GetOrigPkgAddr()) - poolOldToken1Bal := balanceOf(pool.token1, GetOrigPkgAddr()) + poolOldToken0Bal := balanceOfByRegisterCall(fooPath, poolAddr) + poolOldToken1Bal := balanceOfByRegisterCall(barPath, poolAddr) + + userOldToken0Bal := balanceOfByRegisterCall(fooPath, tr01) + userOldToken1Bal := balanceOfByRegisterCall(barPath, tr01) - userOldToken0Bal := balanceOf(pool.token0, tr01) - userOldToken1Bal := balanceOf(pool.token1, tr01) + amount0, amount1 := Swap(fooPath, barPath, pFee2, tr01, false, bigint(160000), MAX_PRICE) - amount0, amount1 := Swap(pToken0, pToken1, pFee2, tr01, false, 16000, (MAX_SQRT_RATIO - 1)) // give enough amount to take fees away + shouldNEQ(t, amount0, bigint(0)) + shouldNEQ(t, amount1, bigint(0)) - poolNewToken0Bal := balanceOf(pool.token0, GetOrigPkgAddr()) - poolNewToken1Bal := balanceOf(pool.token1, GetOrigPkgAddr()) + poolNewToken0Bal := balanceOfByRegisterCall(fooPath, poolAddr) + poolNewToken1Bal := balanceOfByRegisterCall(barPath, poolAddr) - userNewToken0Bal := balanceOf(pool.token0, tr01) - userNewToken1Bal := balanceOf(pool.token1, tr01) + userNewToken0Bal := balanceOfByRegisterCall(fooPath, tr01) + userNewToken1Bal := balanceOfByRegisterCall(barPath, tr01) - shouldEQ(t, userOldToken0Bal-amount0, userNewToken0Bal) - shouldEQ(t, userOldToken1Bal-amount1, userNewToken1Bal) - shouldEQ(t, poolOldToken0Bal+amount0, poolNewToken0Bal) - shouldEQ(t, poolOldToken1Bal+amount1, poolNewToken1Bal) + shouldEQ(t, userOldToken0Bal-userNewToken0Bal, int64(amount0)) + shouldEQ(t, userNewToken1Bal-userOldToken1Bal, int64(-amount1)) + shouldEQ(t, poolNewToken0Bal-poolOldToken0Bal, int64(amount0)) + shouldEQ(t, poolOldToken1Bal-poolNewToken1Bal, int64(-amount1)) } // Swap with Protocol std.TestSetOrigCaller(gsa) - SetFeeProtocol(6, 8) + test_slot0 := pool.GetSlot0() shouldEQ(t, test_slot0.feeProtocol, bigint(134)) // Make ProtocolFee via Swap by tr01 ( Mint by lp01 ) std.TestSetOrigCaller(lp01) { - gsaOldToken0Bal := balanceOf(pool.token0, gsa) - gsaOldToken1Bal := balanceOf(pool.token1, gsa) + gsaOldToken0Bal := balanceOfByRegisterCall(fooPath, gsa) + gsaOldToken1Bal := balanceOfByRegisterCall(barPath, gsa) std.TestSetOrigCaller(tr01) - Swap(pToken0, pToken1, pFee2, tr01, true, 100000, MIN_SQRT_RATIO+1) // swap token0 -> token1 => fee only in token0 - Swap(pToken0, pToken1, pFee2, tr01, true, 100000, MIN_SQRT_RATIO+1) // more protocol fee + Swap(fooPath, barPath, pFee2, tr01, true, 100000, MIN_PRICE) // swap token0 -> token1 => fee only in token0 + Swap(fooPath, barPath, pFee2, tr01, true, 100000, MIN_PRICE) // more protocol fee // Gnoswap Admin will collect protocol fee std.TestSetOrigCaller(gsa) - amount0, amount1 := CollectProtocol(pToken0, pToken1, pFee2, gsa, 100000, 100000) + amount0, amount1 := CollectProtocol(fooPath, barPath, pFee1, gsa, 100000, 100000) - gsaNewToken0Bal := balanceOf(pool.token0, gsa) - gsaNewToken1Bal := balanceOf(pool.token1, gsa) + gsaNewToken0Bal := balanceOfByRegisterCall(fooPath, gsa) + gsaNewToken1Bal := balanceOfByRegisterCall(barPath, gsa) + + shouldEQ(t, gsaNewToken0Bal-gsaOldToken0Bal, int64(amount0)) + shouldEQ(t, gsaNewToken1Bal-gsaOldToken1Bal, int64(amount1)) } { - gsaOldToken0Bal := balanceOf(pool.token0, gsa) - gsaOldToken1Bal := balanceOf(pool.token1, gsa) + gsaOldToken0Bal := balanceOfByRegisterCall(fooPath, gsa) + gsaOldToken1Bal := balanceOfByRegisterCall(barPath, gsa) std.TestSetOrigCaller(tr01) - Swap(pToken0, pToken1, pFee2, tr01, false, 100000, MAX_SQRT_RATIO-1) // swap token0 -> token1 => fee only in token0 - Swap(pToken0, pToken1, pFee2, tr01, false, 100000, MAX_SQRT_RATIO-1) // more protocol fee + Swap(fooPath, barPath, pFee2, tr01, false, 100000, MAX_PRICE) // swap token0 -> token1 => fee only in token0 + Swap(fooPath, barPath, pFee2, tr01, false, 100000, MAX_PRICE) // more protocol fee // Gnoswap Admin will collect protocol fee std.TestSetOrigCaller(gsa) - amount0, amount1 := CollectProtocol(pToken0, pToken1, pFee2, gsa, 100000, 100000) + amount0, amount1 := CollectProtocol(fooPath, barPath, pFee2, gsa, 100000, 100000) + + gsaNewToken0Bal := balanceOfByRegisterCall(fooPath, gsa) + gsaNewToken1Bal := balanceOfByRegisterCall(barPath, gsa) - gsaNewToken0Bal := balanceOf(pool.token0, gsa) - gsaNewToken1Bal := balanceOf(pool.token1, gsa) + shouldEQ(t, gsaNewToken0Bal-gsaOldToken0Bal, int64(amount0)) + shouldEQ(t, gsaNewToken1Bal-gsaOldToken1Bal, int64(amount1)) } } @@ -362,12 +377,12 @@ func TestApiGetPools(t *testing.T) { shouldEQ(t, jsonStr.Get("stat.timestamp").Int(), GetTimestamp()) shouldEQ(t, len(jsonStr.Get("response.data").Array()), 2) - shouldEQ(t, jsonStr.Get("response.data").Array()[0].String(), "bar_foo_500") - shouldEQ(t, jsonStr.Get("response.data").Array()[1].String(), "bar_foo_3000") + shouldEQ(t, jsonStr.Get("response.data").Array()[0].String(), "gno.land/r/bar:gno.land/r/foo:500") + shouldEQ(t, jsonStr.Get("response.data").Array()[1].String(), "gno.land/r/bar:gno.land/r/foo:3000") } func TestApiGetPool(t *testing.T) { - gpl := ApiGetPool("bar_foo_500") + gpl := ApiGetPool("gno.land/r/bar:gno.land/r/foo:500") jsonStr := gjson.Parse(gpl) shouldEQ(t, jsonStr.Get("stat.height").Int(), GetHeight()) diff --git a/pool/_TEST_pool_single_lp_test.gnoa b/pool/_TEST_pool_single_lp_test.gnoa index df8532f9..8547e9e0 100644 --- a/pool/_TEST_pool_single_lp_test.gnoa +++ b/pool/_TEST_pool_single_lp_test.gnoa @@ -6,6 +6,8 @@ import ( "testing" "gno.land/p/demo/testutils" + + _ "gno.land/r/grc20_wrapper" ) var ( @@ -13,15 +15,14 @@ var ( lp01 = testutils.TestAddress("lp01") // Liquidity Provider 01 tr01 = testutils.TestAddress("tr01") // Trader 01 - posAddr = std.DerivePkgAddr("gno.land/r/position") - - poolPath = "gno.land/r/pool" + poolAddr = std.DerivePkgAddr("gno.land/r/pool") + posAddr = std.DerivePkgAddr("gno.land/r/position") ) var ( // Common - pToken0 = "foo" - pToken1 = "bar" + fooPath = "gno.land/r/foo" // token1 + barPath = "gno.land/r/bar" // token2 pFee = uint16(500) test_tickLower = int32(9000) @@ -36,16 +37,11 @@ var ( func TestInitCreatePool(t *testing.T) { std.TestSetOrigCaller(gsa) InitManual() - CreatePool("foo", "bar", pFee, 130621891405341611593710811006) - - // fee - // 500 = 0.05% // USv3 default - // 3000 = 0.3% // USv3 default - // 10000 = 1% // USv3 default + CreatePool(fooPath, barPath, pFee, 130621891405341611593710811006) // sqrtPrice // 130621891405341611593710811006 // tick = 10000 - shouldPanic(t, func() { CreatePool("foo", "bar", 500, 130621891405341611593710811006) }) + shouldPanic(t, func() { CreatePool(fooPath, barPath, 500, 130621891405341611593710811006) }) } // 2. Mint by lp01 @@ -54,8 +50,8 @@ func TestMint(t *testing.T) { std.TestSetOrigCaller(lp01) Mint( - pToken0, - pToken1, + fooPath, + barPath, pFee, posAddr, test_tickLower, @@ -63,20 +59,20 @@ func TestMint(t *testing.T) { test_liquidityExpect, ) - pool := GetPool(pToken0, pToken1, pFee) + pool := GetPool(fooPath, barPath, pFee) test_liquidity := pool.GetLiquidity() shouldEQ(t, test_liquidity, test_liquidityExpect) - Mint(pToken0, pToken1, pFee, posAddr, test_tickLower, test_tickUpper, test_liquidityExpect) - Mint(pToken0, pToken1, pFee, posAddr, test_tickLower, test_tickUpper, test_liquidityExpect) - Mint(pToken0, pToken1, pFee, posAddr, test_tickLower, test_tickUpper, test_liquidityExpect) - Mint(pToken0, pToken1, pFee, posAddr, test_tickLower, test_tickUpper, test_liquidityExpect) - Mint(pToken0, pToken1, pFee, posAddr, test_tickLower, test_tickUpper, test_liquidityExpect) - Mint(pToken0, pToken1, pFee, posAddr, test_tickLower, test_tickUpper, test_liquidityExpect) + Mint(fooPath, barPath, pFee, posAddr, test_tickLower, test_tickUpper, test_liquidityExpect) + Mint(fooPath, barPath, pFee, posAddr, test_tickLower, test_tickUpper, test_liquidityExpect) + Mint(fooPath, barPath, pFee, posAddr, test_tickLower, test_tickUpper, test_liquidityExpect) + Mint(fooPath, barPath, pFee, posAddr, test_tickLower, test_tickUpper, test_liquidityExpect) + Mint(fooPath, barPath, pFee, posAddr, test_tickLower, test_tickUpper, test_liquidityExpect) + Mint(fooPath, barPath, pFee, posAddr, test_tickLower, test_tickUpper, test_liquidityExpect) - m81, m82 := Mint(pToken0, pToken1, pFee, posAddr, test_tickLower, test_tickUpper, test_liquidityExpect) - Mint(pToken0, pToken1, pFee, posAddr, test_tickLower, test_tickUpper, test_liquidityExpect) - m101, m102 := Mint(pToken0, pToken1, pFee, posAddr, test_tickLower, test_tickUpper, test_liquidityExpect) + m81, m82 := Mint(fooPath, barPath, pFee, posAddr, test_tickLower, test_tickUpper, test_liquidityExpect) + Mint(fooPath, barPath, pFee, posAddr, test_tickLower, test_tickUpper, test_liquidityExpect) + m101, m102 := Mint(fooPath, barPath, pFee, posAddr, test_tickLower, test_tickUpper, test_liquidityExpect) shouldNEQ(t, m81, bigint(0)) shouldNEQ(t, m82, bigint(0)) @@ -87,27 +83,27 @@ func TestMint(t *testing.T) { shouldEQ(t, test_liquidity, test_liquidityExpect*10) // tickLower > currentTick == don't add to current liquidity - Mint(pToken0, pToken1, pFee, posAddr, test_tickLower2, test_tickUpper2, test_liquidityExpect) + Mint(fooPath, barPath, pFee, posAddr, test_tickLower2, test_tickUpper2, test_liquidityExpect) // tickUpper < current tick == don't add to current liquidity - Mint(pToken0, pToken1, pFee, posAddr, -test_tickUpper2, -test_tickLower2, test_liquidityExpect) + Mint(fooPath, barPath, pFee, posAddr, -test_tickUpper2, -test_tickLower2, test_liquidityExpect) // tickUpper < tickLower == don't add to current liquidity - Mint(pToken0, pToken1, pFee, posAddr, -test_tickUpper, -test_tickLower, test_liquidityExpect) + Mint(fooPath, barPath, pFee, posAddr, -test_tickUpper, -test_tickLower, test_liquidityExpect) test_liquidity = pool.GetLiquidity() shouldEQ(t, test_liquidity, test_liquidityExpect*10) - Mint(pToken0, pToken1, pFee, posAddr, test_tickLower, test_tickUpper, test_liquidityExpect) - Mint(pToken0, pToken1, pFee, posAddr, test_tickLower, test_tickUpper, test_liquidityExpect) - Mint(pToken0, pToken1, pFee, posAddr, test_tickLower, test_tickUpper, test_liquidityExpect) - Mint(pToken0, pToken1, pFee, posAddr, test_tickLower, test_tickUpper, test_liquidityExpect) - Mint(pToken0, pToken1, pFee, posAddr, test_tickLower, test_tickUpper, test_liquidityExpect) - Mint(pToken0, pToken1, pFee, posAddr, test_tickLower, test_tickUpper, test_liquidityExpect) - Mint(pToken0, pToken1, pFee, posAddr, test_tickLower, test_tickUpper, test_liquidityExpect) - Mint(pToken0, pToken1, pFee, posAddr, test_tickLower, test_tickUpper, test_liquidityExpect) - Mint(pToken0, pToken1, pFee, posAddr, test_tickLower, test_tickUpper, test_liquidityExpect) - Mint(pToken0, pToken1, pFee, posAddr, test_tickLower, test_tickUpper, test_liquidityExpect) + Mint(fooPath, barPath, pFee, posAddr, test_tickLower, test_tickUpper, test_liquidityExpect) + Mint(fooPath, barPath, pFee, posAddr, test_tickLower, test_tickUpper, test_liquidityExpect) + Mint(fooPath, barPath, pFee, posAddr, test_tickLower, test_tickUpper, test_liquidityExpect) + Mint(fooPath, barPath, pFee, posAddr, test_tickLower, test_tickUpper, test_liquidityExpect) + Mint(fooPath, barPath, pFee, posAddr, test_tickLower, test_tickUpper, test_liquidityExpect) + Mint(fooPath, barPath, pFee, posAddr, test_tickLower, test_tickUpper, test_liquidityExpect) + Mint(fooPath, barPath, pFee, posAddr, test_tickLower, test_tickUpper, test_liquidityExpect) + Mint(fooPath, barPath, pFee, posAddr, test_tickLower, test_tickUpper, test_liquidityExpect) + Mint(fooPath, barPath, pFee, posAddr, test_tickLower, test_tickUpper, test_liquidityExpect) + Mint(fooPath, barPath, pFee, posAddr, test_tickLower, test_tickUpper, test_liquidityExpect) test_liquidity = pool.GetLiquidity() shouldEQ(t, test_liquidity, test_liquidityExpect*20) @@ -118,34 +114,34 @@ func TestBurn(t *testing.T) { std.TestSetPrevRealm("gno.land/r/position") std.TestSetOrigCaller(lp01) - b11, b12 := Burn(pToken0, pToken1, pFee, test_tickLower, test_tickUpper, test_liquidityExpect) - b21, b22 := Burn(pToken0, pToken1, pFee, test_tickLower, test_tickUpper, test_liquidityExpect) + b11, b12 := Burn(fooPath, barPath, pFee, test_tickLower, test_tickUpper, test_liquidityExpect) + b21, b22 := Burn(fooPath, barPath, pFee, test_tickLower, test_tickUpper, test_liquidityExpect) shouldEQ(t, b11, b21) shouldEQ(t, b12, b22) - pool := GetPool(pToken0, pToken1, pFee) + pool := GetPool(fooPath, barPath, pFee) test_liquidity := pool.GetLiquidity() shouldEQ(t, test_liquidity, test_liquidityExpect*18) - Burn(pToken0, pToken1, pFee, test_tickLower, test_tickUpper, test_liquidityExpect*8) + Burn(fooPath, barPath, pFee, test_tickLower, test_tickUpper, test_liquidityExpect*8) test_liquidity = pool.GetLiquidity() shouldEQ(t, test_liquidity, test_liquidityExpect*10) - Burn(pToken0, pToken1, pFee, test_tickLower, test_tickUpper, 1) + Burn(fooPath, barPath, pFee, test_tickLower, test_tickUpper, 1) test_liquidity = pool.GetLiquidity() shouldEQ(t, test_liquidity, bigint(9999)) - Burn(pToken0, pToken1, pFee, test_tickLower, test_tickUpper, 999) + Burn(fooPath, barPath, pFee, test_tickLower, test_tickUpper, 999) test_liquidity = pool.GetLiquidity() shouldEQ(t, test_liquidity, test_liquidityExpect*9) - Burn(pToken0, pToken1, pFee, test_tickLower, test_tickUpper, test_liquidityExpect*9) + Burn(fooPath, barPath, pFee, test_tickLower, test_tickUpper, test_liquidityExpect*9) test_liquidity = pool.GetLiquidity() shouldEQ(t, test_liquidity, bigint(0)) // can't burn when liq is 0 - // Burn(pToken0, pToken1, pFee, test_tickLower, test_tickUpper, test_liquidityExpect) - shouldPanic(t, func() { Burn(pToken0, pToken1, pFee, test_tickLower, test_tickUpper, test_liquidityExpect) }) + // Burn(fooPath, barPath, pFee, test_tickLower, test_tickUpper, test_liquidityExpect) + shouldPanic(t, func() { Burn(fooPath, barPath, pFee, test_tickLower, test_tickUpper, test_liquidityExpect) }) } // 4. Collect @@ -154,72 +150,72 @@ func TestCollect(t *testing.T) { std.TestSetOrigCaller(lp01) // withdraw all token before test `Collect` - Collect(pToken0, pToken1, pFee, lp01, test_tickLower, test_tickUpper, 50000000, 50000000) + Collect(fooPath, barPath, pFee, lp01, test_tickLower, test_tickUpper, 50000000, 50000000) // pool should have zero liquidity - pool := GetPool(pToken0, pToken1, pFee) + pool := GetPool(fooPath, barPath, pFee) test_liquidity := pool.GetLiquidity() shouldEQ(t, test_liquidity, bigint(0)) - Mint(pToken0, pToken1, pFee, posAddr, test_tickLower, test_tickUpper, test_liquidityExpect*15) - Burn(pToken0, pToken1, pFee, test_tickLower, test_tickUpper, test_liquidityExpect*15) - c11, c12 := Collect(pToken0, pToken1, pFee, lp01, test_tickLower, test_tickUpper, 50000000, 50000000) + Mint(fooPath, barPath, pFee, posAddr, test_tickLower, test_tickUpper, test_liquidityExpect*15) + Burn(fooPath, barPath, pFee, test_tickLower, test_tickUpper, test_liquidityExpect*15) + c11, c12 := Collect(fooPath, barPath, pFee, lp01, test_tickLower, test_tickUpper, 50000000, 50000000) - Mint(pToken0, pToken1, pFee, posAddr, test_tickLower, test_tickUpper, test_liquidityExpect*15) - Burn(pToken0, pToken1, pFee, test_tickLower, test_tickUpper, test_liquidityExpect*15) - c21, c22 := Collect(pToken0, pToken1, pFee, lp01, test_tickLower, test_tickUpper, 50000000, 50000000) + Mint(fooPath, barPath, pFee, posAddr, test_tickLower, test_tickUpper, test_liquidityExpect*15) + Burn(fooPath, barPath, pFee, test_tickLower, test_tickUpper, test_liquidityExpect*15) + c21, c22 := Collect(fooPath, barPath, pFee, lp01, test_tickLower, test_tickUpper, 50000000, 50000000) shouldEQ(t, c11, c21) shouldEQ(t, c12, c22) - Mint(pToken0, pToken1, pFee, posAddr, test_tickLower, test_tickUpper, test_liquidityExpect*15) - Burn(pToken0, pToken1, pFee, test_tickLower, test_tickUpper, test_liquidityExpect*15) - c31, c32 := Collect(pToken0, pToken1, pFee, lp01, test_tickLower, test_tickUpper, 100, 100) + Mint(fooPath, barPath, pFee, posAddr, test_tickLower, test_tickUpper, test_liquidityExpect*15) + Burn(fooPath, barPath, pFee, test_tickLower, test_tickUpper, test_liquidityExpect*15) + c31, c32 := Collect(fooPath, barPath, pFee, lp01, test_tickLower, test_tickUpper, 100, 100) shouldEQ(t, c31, bigint(100)) shouldEQ(t, c32, bigint(100)) - c41, c42 := Collect(pToken0, pToken1, pFee, lp01, test_tickLower, test_tickUpper, 50000000, 50000000) + c41, c42 := Collect(fooPath, barPath, pFee, lp01, test_tickLower, test_tickUpper, 50000000, 50000000) shouldEQ(t, c41, c21-bigint(100)) shouldEQ(t, c42, c22-bigint(100)) // Mint > No Burn => nothing to collect - Mint(pToken0, pToken1, pFee, posAddr, test_tickLower, test_tickUpper, test_liquidityExpect*15) - // Burn(pToken0, pToken1, pFee, test_tickLower, test_tickUpper, test_liquidityExpect*15) - c51, c52 := Collect(pToken0, pToken1, pFee, lp01, test_tickLower, test_tickUpper, 50000000, 50000000) + Mint(fooPath, barPath, pFee, posAddr, test_tickLower, test_tickUpper, test_liquidityExpect*15) + // Burn(fooPath, barPath, pFee, test_tickLower, test_tickUpper, test_liquidityExpect*15) + c51, c52 := Collect(fooPath, barPath, pFee, lp01, test_tickLower, test_tickUpper, 50000000, 50000000) shouldEQ(t, c51, bigint(0)) shouldEQ(t, c52, bigint(0)) // Burn Now => something to collect - Burn(pToken0, pToken1, pFee, test_tickLower, test_tickUpper, test_liquidityExpect*15) - c61, c62 := Collect(pToken0, pToken1, pFee, lp01, test_tickLower, test_tickUpper, 50000000, 50000000) + Burn(fooPath, barPath, pFee, test_tickLower, test_tickUpper, test_liquidityExpect*15) + c61, c62 := Collect(fooPath, barPath, pFee, lp01, test_tickLower, test_tickUpper, 50000000, 50000000) shouldNEQ(t, c61, bigint(0)) shouldNEQ(t, c62, bigint(0)) } // 5. Swap by tr01 func TestSwap(t *testing.T) { - pool := GetPool(pToken0, pToken1, pFee) + pool := GetPool(fooPath, barPath, pFee) test_liquidity := pool.GetLiquidity() shouldEQ(t, test_liquidity, bigint(0)) std.TestSetPrevRealm("gno.land/r/position") std.TestSetOrigCaller(lp01) - Mint(pToken0, pToken1, pFee, posAddr, test_tickLower, test_tickUpper, test_liquidityExpect*20000) + Mint(fooPath, barPath, pFee, posAddr, test_tickLower, test_tickUpper, test_liquidityExpect*20000) // Swap several times std.TestSetOrigCaller(tr01) - test_price := bigint(MIN_SQRT_RATIO + 1) // maximum price + test_price := bigint(MIN_PRICE) { - poolOldToken0Bal := balanceOf(pool.token0, GetOrigPkgAddr()) - poolOldToken1Bal := balanceOf(pool.token1, GetOrigPkgAddr()) + poolOldToken0Bal := balanceOfByRegisterCall(fooPath, poolAddr) + poolOldToken1Bal := balanceOfByRegisterCall(barPath, poolAddr) - userOldToken0Bal := balanceOf(pool.token0, tr01) - userOldToken1Bal := balanceOf(pool.token1, tr01) + userOldToken0Bal := balanceOfByRegisterCall(fooPath, tr01) + userOldToken1Bal := balanceOfByRegisterCall(barPath, tr01) amount0, amount1 := Swap( - pToken0, - pToken1, + fooPath, + barPath, pFee, tr01, true, @@ -230,80 +226,80 @@ func TestSwap(t *testing.T) { shouldNEQ(t, amount0, bigint(0)) shouldNEQ(t, amount1, bigint(0)) - poolNewToken0Bal := balanceOf(pool.token0, GetOrigPkgAddr()) - poolNewToken1Bal := balanceOf(pool.token1, GetOrigPkgAddr()) + poolNewToken0Bal := balanceOfByRegisterCall(fooPath, poolAddr) + poolNewToken1Bal := balanceOfByRegisterCall(barPath, poolAddr) - userNewToken0Bal := balanceOf(pool.token0, tr01) - userNewToken1Bal := balanceOf(pool.token1, tr01) + userNewToken0Bal := balanceOfByRegisterCall(fooPath, tr01) + userNewToken1Bal := balanceOfByRegisterCall(barPath, tr01) - shouldEQ(t, userOldToken0Bal-amount0, userNewToken0Bal) - shouldEQ(t, userOldToken1Bal-amount1, userNewToken1Bal) - shouldEQ(t, poolOldToken0Bal+amount0, poolNewToken0Bal) - shouldEQ(t, poolOldToken1Bal+amount1, poolNewToken1Bal) + shouldEQ(t, userOldToken0Bal-userNewToken0Bal, int64(amount0)) + shouldEQ(t, userNewToken1Bal-userOldToken1Bal, int64(-amount1)) + shouldEQ(t, poolNewToken0Bal-poolOldToken0Bal, int64(amount0)) + shouldEQ(t, poolOldToken1Bal-poolNewToken1Bal, int64(-amount1)) } { - poolOldToken0Bal := balanceOf(pool.token0, GetOrigPkgAddr()) - poolOldToken1Bal := balanceOf(pool.token1, GetOrigPkgAddr()) + poolOldToken0Bal := balanceOfByRegisterCall(fooPath, poolAddr) + poolOldToken1Bal := balanceOfByRegisterCall(barPath, poolAddr) - userOldToken0Bal := balanceOf(pool.token0, tr01) - userOldToken1Bal := balanceOf(pool.token1, tr01) + userOldToken0Bal := balanceOfByRegisterCall(fooPath, tr01) + userOldToken1Bal := balanceOfByRegisterCall(barPath, tr01) - amount0, amount1 := Swap(pToken0, pToken1, pFee, tr01, true, 5000, test_price) // give enough amount to take fees away + amount0, amount1 := Swap(fooPath, barPath, pFee, tr01, true, 5000, test_price) // give enough amount to take fees away - poolNewToken0Bal := balanceOf(pool.token0, GetOrigPkgAddr()) - poolNewToken1Bal := balanceOf(pool.token1, GetOrigPkgAddr()) + poolNewToken0Bal := balanceOfByRegisterCall(fooPath, poolAddr) + poolNewToken1Bal := balanceOfByRegisterCall(barPath, poolAddr) - userNewToken0Bal := balanceOf(pool.token0, tr01) - userNewToken1Bal := balanceOf(pool.token1, tr01) + userNewToken0Bal := balanceOfByRegisterCall(fooPath, tr01) + userNewToken1Bal := balanceOfByRegisterCall(barPath, tr01) - shouldEQ(t, userOldToken0Bal-amount0, userNewToken0Bal) - shouldEQ(t, userOldToken1Bal-amount1, userNewToken1Bal) - shouldEQ(t, poolOldToken0Bal+amount0, poolNewToken0Bal) - shouldEQ(t, poolOldToken1Bal+amount1, poolNewToken1Bal) + shouldEQ(t, userOldToken0Bal-userNewToken0Bal, int64(amount0)) + shouldEQ(t, userNewToken1Bal-userOldToken1Bal, int64(-amount1)) + shouldEQ(t, poolNewToken0Bal-poolOldToken0Bal, int64(amount0)) + shouldEQ(t, poolOldToken1Bal-poolNewToken1Bal, int64(-amount1)) } { - poolOldToken0Bal := balanceOf(pool.token0, GetOrigPkgAddr()) - poolOldToken1Bal := balanceOf(pool.token1, GetOrigPkgAddr()) + poolOldToken0Bal := balanceOfByRegisterCall(fooPath, poolAddr) + poolOldToken1Bal := balanceOfByRegisterCall(barPath, poolAddr) - userOldToken0Bal := balanceOf(pool.token0, tr01) - userOldToken1Bal := balanceOf(pool.token1, tr01) + userOldToken0Bal := balanceOfByRegisterCall(fooPath, tr01) + userOldToken1Bal := balanceOfByRegisterCall(barPath, tr01) - amount0, amount1 := Swap(pToken0, pToken1, pFee, tr01, true, 1000, test_price) // give enough amount to take fees away + amount0, amount1 := Swap(fooPath, barPath, pFee, tr01, true, 1000, test_price) // give enough amount to take fees away - poolNewToken0Bal := balanceOf(pool.token0, GetOrigPkgAddr()) - poolNewToken1Bal := balanceOf(pool.token1, GetOrigPkgAddr()) + poolNewToken0Bal := balanceOfByRegisterCall(fooPath, poolAddr) + poolNewToken1Bal := balanceOfByRegisterCall(barPath, poolAddr) - userNewToken0Bal := balanceOf(pool.token0, tr01) - userNewToken1Bal := balanceOf(pool.token1, tr01) + userNewToken0Bal := balanceOfByRegisterCall(fooPath, tr01) + userNewToken1Bal := balanceOfByRegisterCall(barPath, tr01) - shouldEQ(t, userOldToken0Bal-amount0, userNewToken0Bal) - shouldEQ(t, userOldToken1Bal-amount1, userNewToken1Bal) - shouldEQ(t, poolOldToken0Bal+amount0, poolNewToken0Bal) - shouldEQ(t, poolOldToken1Bal+amount1, poolNewToken1Bal) + shouldEQ(t, userOldToken0Bal-userNewToken0Bal, int64(amount0)) + shouldEQ(t, userNewToken1Bal-userOldToken1Bal, int64(-amount1)) + shouldEQ(t, poolNewToken0Bal-poolOldToken0Bal, int64(amount0)) + shouldEQ(t, poolOldToken1Bal-poolNewToken1Bal, int64(-amount1)) } // Swap token1 -> token0 { - poolOldToken0Bal := balanceOf(pool.token0, GetOrigPkgAddr()) - poolOldToken1Bal := balanceOf(pool.token1, GetOrigPkgAddr()) + poolOldToken0Bal := balanceOfByRegisterCall(fooPath, poolAddr) + poolOldToken1Bal := balanceOfByRegisterCall(barPath, poolAddr) - userOldToken0Bal := balanceOf(pool.token0, tr01) - userOldToken1Bal := balanceOf(pool.token1, tr01) + userOldToken0Bal := balanceOfByRegisterCall(fooPath, tr01) + userOldToken1Bal := balanceOfByRegisterCall(barPath, tr01) - amount0, amount1 := Swap(pToken0, pToken1, pFee, tr01, false, 16000, (MAX_SQRT_RATIO - 1)) // give enough amount to take fees away + amount0, amount1 := Swap(fooPath, barPath, pFee, tr01, false, 16000, MAX_PRICE) // give enough amount to take fees away - poolNewToken0Bal := balanceOf(pool.token0, GetOrigPkgAddr()) - poolNewToken1Bal := balanceOf(pool.token1, GetOrigPkgAddr()) + poolNewToken0Bal := balanceOfByRegisterCall(fooPath, poolAddr) + poolNewToken1Bal := balanceOfByRegisterCall(barPath, poolAddr) - userNewToken0Bal := balanceOf(pool.token0, tr01) - userNewToken1Bal := balanceOf(pool.token1, tr01) + userNewToken0Bal := balanceOfByRegisterCall(fooPath, tr01) + userNewToken1Bal := balanceOfByRegisterCall(barPath, tr01) - shouldEQ(t, userOldToken0Bal-amount0, userNewToken0Bal) - shouldEQ(t, userOldToken1Bal-amount1, userNewToken1Bal) - shouldEQ(t, poolOldToken0Bal+amount0, poolNewToken0Bal) - shouldEQ(t, poolOldToken1Bal+amount1, poolNewToken1Bal) + shouldEQ(t, userOldToken0Bal-userNewToken0Bal, int64(amount0)) + shouldEQ(t, userNewToken1Bal-userOldToken1Bal, int64(-amount1)) + shouldEQ(t, poolNewToken0Bal-poolOldToken0Bal, int64(amount0)) + shouldEQ(t, poolOldToken1Bal-poolNewToken1Bal, int64(-amount1)) } } @@ -327,40 +323,43 @@ func TestCollectProtocol(t *testing.T) { std.TestSetOrigCaller(gsa) SetFeeProtocol(6, 8) - pool := GetPool(pToken0, pToken1, pFee) + pool := GetPool(fooPath, barPath, pFee) test_slot0 := pool.GetSlot0() shouldEQ(t, test_slot0.feeProtocol, bigint(134)) // Make ProtocolFee via Swap by tr01 ( Mint by lp01 ) std.TestSetOrigCaller(lp01) { - gsaOldToken0Bal := balanceOf(pool.token0, gsa) - gsaOldToken1Bal := balanceOf(pool.token1, gsa) + gsaOldToken0Bal := balanceOfByRegisterCall(fooPath, gsa) + gsaOldToken1Bal := balanceOfByRegisterCall(barPath, gsa) std.TestSetOrigCaller(tr01) - Swap(pToken0, pToken1, pFee, tr01, true, 200000, MIN_SQRT_RATIO+1) // swap token0 -> token1 => fee only in token0 + Swap(fooPath, barPath, pFee, tr01, true, 200000, MIN_PRICE) // swap token0 -> token1 => fee only in token0 // Gnoswap Admin will collect protocol fee std.TestSetOrigCaller(gsa) - amount0, amount1 := CollectProtocol(pToken0, pToken1, pFee, gsa, 100000, 100000) + amount0, amount1 := CollectProtocol(fooPath, barPath, pFee, gsa, 100000, 100000) + + gsaNewToken0Bal := balanceOfByRegisterCall(fooPath, gsa) + gsaNewToken1Bal := balanceOfByRegisterCall(barPath, gsa) - gsaNewToken0Bal := balanceOf(pool.token0, gsa) - gsaNewToken1Bal := balanceOf(pool.token1, gsa) + shouldEQ(t, gsaNewToken0Bal-gsaOldToken0Bal, int64(amount0)) + shouldEQ(t, gsaNewToken1Bal-gsaOldToken1Bal, int64(amount1)) } { - gsaOldToken0Bal := balanceOf(pool.token0, gsa) - gsaOldToken1Bal := balanceOf(pool.token1, gsa) + gsaOldToken0Bal := balanceOfByRegisterCall(fooPath, gsa) + gsaOldToken1Bal := balanceOfByRegisterCall(fooPath, gsa) std.TestSetOrigCaller(tr01) - Swap(pToken0, pToken1, pFee, tr01, false, 200000, MAX_SQRT_RATIO-1) // swap token0 -> token1 => fee only in token0 + Swap(fooPath, barPath, pFee, tr01, false, 200000, MAX_SQRT_RATIO-1) // swap token0 -> token1 => fee only in token0 // Gnoswap Admin will collect protocol fee std.TestSetOrigCaller(gsa) - amount0, amount1 := CollectProtocol(pToken0, pToken1, pFee, gsa, 100000, 100000) + amount0, amount1 := CollectProtocol(fooPath, barPath, pFee, gsa, 100000, 100000) - gsaNewToken0Bal := balanceOf(pool.token0, gsa) - gsaNewToken1Bal := balanceOf(pool.token1, gsa) + gsaNewToken0Bal := balanceOfByRegisterCall(fooPath, gsa) + gsaNewToken1Bal := balanceOfByRegisterCall(fooPath, gsa) } } @@ -373,11 +372,11 @@ func TestApiGetPools(t *testing.T) { shouldEQ(t, jsonStr.Get("stat.timestamp").Int(), GetTimestamp()) shouldEQ(t, len(jsonStr.Get("response.data").Array()), 1) - shouldEQ(t, jsonStr.Get("response.data").Array()[0].String(), "bar_foo_500") + shouldEQ(t, jsonStr.Get("response.data").Array()[0].String(), "gno.land/r/bar:gno.land/r/foo:500") } func TestApiGetPool(t *testing.T) { - gpl := ApiGetPool("bar_foo_500") + gpl := ApiGetPool("gno.land/r/bar:gno.land/r/foo:500") jsonStr := gjson.Parse(gpl) shouldEQ(t, jsonStr.Get("stat.height").Int(), GetHeight()) diff --git a/pool/consts.gno b/pool/consts.gno index 23af38fc..db66afe7 100644 --- a/pool/consts.gno +++ b/pool/consts.gno @@ -33,7 +33,7 @@ const ( MAX_INT256 bigint = 57896044618658097711785492504343953926634992332820282019728792003956564819967 MAX_UINT256 bigint = 115792089237316195423570985008687907853269984665640564039457584007913129639935 - // TICK Related + // Tick Related MIN_TICK int32 = -887272 MAX_TICK int32 = 887272 @@ -57,4 +57,7 @@ const ( MAX_UINT96 bigint = 79228162514264337593543950335 MAX_UINT160 bigint = 1461501637330902918203684832716283019655932542975 + + ADDR_ROUTER std.Address = std.DerivePkgAddr("gno.land/r/router") + ADDR_POOL std.Address = std.DerivePkgAddr("gno.land/r/pool") ) diff --git a/pool/getter_pool.gno b/pool/getter_pool.gno index d5f5e6d4..5b04557a 100644 --- a/pool/getter_pool.gno +++ b/pool/getter_pool.gno @@ -81,6 +81,10 @@ func (pool *Pool) GetTickBitmaps() TickBitmaps { return pool.tickBitmaps } -func (pool *Pool) GetSqrtPriceX96() bigint { +func (pool *Pool) GetSlotSqrtPriceX96() bigint { return pool.slot0.sqrtPriceX96 } + +func (pool *Pool) GetSlotTick() int32 { + return pool.slot0.tick +} diff --git a/pool/getter_position.gno b/pool/getter_position.gno index 512a166f..c3dcbfb8 100644 --- a/pool/getter_position.gno +++ b/pool/getter_position.gno @@ -1,9 +1,14 @@ // POSITION CONTRACT USES BELOW FUNCTIONS IN ITS LOGIC package pool -import ( - "std" -) +func GetPoolList() []string { + poolPaths := []string{} + for poolPath, _ := range pools { + poolPaths = append(poolPaths, poolPath) + } + + return poolPaths +} func (pool *Pool) GetPoolSlot0SqrtPriceX96() bigint { return pool.slot0.sqrtPriceX96 @@ -29,11 +34,6 @@ func (pool *Pool) GetPoolPositionFeeGrowthInside1LastX128(key string) bigint { return pool.positions[key].feeGrowthInside1LastX128 } -func GetPoolAddress() std.Address { - return std.DerivePkgAddr("gno.land/r/pool") - // XXX return std.GetOrigPkgAddr() -} - func (pool *Pool) GetPoolSlot0Tick() int32 { return pool.slot0.tick } diff --git a/pool/math_logic.gno b/pool/math_logic.gno index dd8da390..c31e7de7 100644 --- a/pool/math_logic.gno +++ b/pool/math_logic.gno @@ -2,8 +2,6 @@ package pool import ( "std" - - "gno.land/p/demo/ufmt" ) // get sqrtX96 from tick @@ -28,26 +26,26 @@ func DrySwap( zeroForOne bool, amountSpecified bigint, sqrtPriceLimitX96 bigint, -) (bigint, bigint) { - require(amountSpecified != 0, "[POOL] math_logic.gno__DrySwap() || amountSpecified can't be zero") +) (bigint, bigint, bool) { + if !(amountSpecified != 0) { + return 0, 0, false + } pool := GetPool(pToken0, pToken1, pFee) - require(pool.liquidity > 0, ufmt.Sprintf("[POOL] math_logic.gno__DrySwap() || pool.liquidity(%d) must be > 0", pool.liquidity)) + if !(pool.liquidity > 0) { + return 0, 0, false + } slot0Start := pool.slot0 if zeroForOne { - require( - sqrtPriceLimitX96 < slot0Start.sqrtPriceX96 && sqrtPriceLimitX96 > MIN_SQRT_RATIO, - ufmt.Sprintf("[POOL] math_logic.gno__DrySwap() || SPL-zeroForOne(T)__sqrtPriceLimitX96(%s) < slot0Start.sqrtPriceX96(%s) && sqrtPriceLimitX96(%s) > MIN_SQRT_RATIO(%s)", - sqrtPriceLimitX96, slot0Start.sqrtPriceX96, sqrtPriceLimitX96, MIN_SQRT_RATIO), - ) + if !(sqrtPriceLimitX96 < slot0Start.sqrtPriceX96 && sqrtPriceLimitX96 > MIN_SQRT_RATIO) { + return 0, 0, false + } } else { - require( - sqrtPriceLimitX96 > slot0Start.sqrtPriceX96 && sqrtPriceLimitX96 < MAX_SQRT_RATIO, - ufmt.Sprintf("[POOL] math_logic.gno__DrySwap() || SPL-zeroForOne(F)__sqrtPriceLimitX96(%s) > slot0Start.sqrtPriceX96(%s) && sqrtPriceLimitX96(%s) < MAX_SQRT_RATIO(%s)", - sqrtPriceLimitX96, slot0Start.sqrtPriceX96, sqrtPriceLimitX96, MAX_SQRT_RATIO), - ) + if !(sqrtPriceLimitX96 > slot0Start.sqrtPriceX96 && sqrtPriceLimitX96 < MAX_SQRT_RATIO) { + return 0, 0, false + } } cache := SwapCache{ @@ -122,10 +120,13 @@ func DrySwap( uint32(pool.fee), ) - require(step.amountIn != 0 && step.amountOut != 0, ufmt.Sprintf("[POOL] math_logic.gno__DrySwap() || step.amountIn(%d) != 0 && step.amountOut(%d) != 0", step.amountIn, step.amountOut)) + if !(step.amountIn != 0 && step.amountOut != 0) { + // NOT ENOUGH MINTED + return 0, 0, false + } if exactInput { - state.amountSpecifiedRemaining -= step.amountIn + step.feeAmount + state.amountSpecifiedRemaining -= (step.amountIn + step.feeAmount) state.amountCalculated -= step.amountOut } else { state.amountSpecifiedRemaining += step.amountOut @@ -144,10 +145,17 @@ func DrySwap( } if zeroForOne { - require(pool.balances.token1 > (-1*amount1), ufmt.Sprintf("[POOL] math_logic.gno__DrySwap()_ZFO_T || pool.balances.token1(%s) > amount1(%s)", pool.balances.token1, (-1*amount1))) + if !(pool.balances.token1 > (-1 * amount1)) { + // NOT ENOUGH BALANCE for token1 + return 0, 0, false + } + } else { - require(pool.balances.token0 > (-1*amount0), ufmt.Sprintf("[POOL] math_logic.gno__DrySwap()_ZFO_F || pool.balances.token0(%s) > amount0(%s)", pool.balances.token0, (-1*amount0))) + if !(pool.balances.token0 > (-1 * amount0)) { + // NOT ENOUGH BALANCE for token0 + return 0, 0, false + } } - return amount0, amount1 + return amount0, amount1, true } diff --git a/pool/pool.gno b/pool/pool.gno index c596c99e..f4718b77 100644 --- a/pool/pool.gno +++ b/pool/pool.gno @@ -3,9 +3,7 @@ package pool import ( "std" - "gno.land/p/demo/grc/grc20" "gno.land/p/demo/ufmt" - "gno.land/r/demo/users" g "gno.land/r/gov" ) @@ -48,7 +46,7 @@ func Mint( ok := transferFromByRegisterCall(pToken0Path, from, to, uint64(amount0)) if !ok { - panic("[POOL] pool.gno__Mint() || transferFromByRegisterCall(pToken0Path, from, to, uint64(amount0)) failed") + panic("[POOL] pool.gno__Mint() || transferFromByRegisterCall(pToken0Path, from, to, uint64(amount0)) failed") } require( @@ -97,7 +95,7 @@ func Burn( ) (bigint, bigint) { require(PrevRealmPath() == "gno.land/r/position", ufmt.Sprintf("[POOL] pool.gno__Burn() || caller(%s) must be position contract", PrevRealmPath())) - requireUnsigned(amount, ufmt.Sprintf("[POOL] pool.gno__Burn() || amount(%s) >= 0", amount)) + requireUnsigned(amount, ufmt.Sprintf("[POOL] pool.gno__Burn() || amount(%d) >= 0", amount)) pool := GetPool(pToken0Path, pToken1Path, pFee) @@ -111,8 +109,8 @@ func Burn( ) amount0, amount1 := -amount0Int, -amount1Int - requireUnsigned(amount0, ufmt.Sprintf("pool.gno__Burn() || amount0(%s) >= 0", amount0)) - requireUnsigned(amount1, ufmt.Sprintf("pool.gno__Burn() || amount1(%s) >= 0", amount1)) + requireUnsigned(amount0, ufmt.Sprintf("pool.gno__Burn() || amount0(%d) >= 0", amount0)) + requireUnsigned(amount1, ufmt.Sprintf("pool.gno__Burn() || amount1(%d) >= 0", amount1)) if amount0 > 0 || amount1 > 0 { position.tokensOwed0 += amount0 @@ -125,7 +123,7 @@ func Burn( return amount0, amount1 } -// // only position contract can call this function +// only position contract can call this function func Collect( pToken0Path string, pToken1Path string, @@ -154,10 +152,10 @@ func Collect( requireUnsigned(amount1, ufmt.Sprintf("[POOL] pool.gno__Collect() || amount1(%s) >= 0", amount1)) require(pool.balances.token0 >= amount0, ufmt.Sprintf("[POOL] pool.gno__Collect() || pool.balances.token0(%s) >= amount0(%s)", pool.balances.token0, amount0)) - transferFromByRegisterCall(pToken0Path, GetOrigPkgAddr(), recipient, uint64(amount0)) + transferByRegisterCall(pToken0Path, recipient, uint64(amount0)) require(pool.balances.token1 >= amount1, ufmt.Sprintf("[POOL] pool.gno__Collect() || pool.balances.token1(%s) >= amount1(%s)", pool.balances.token1, amount1)) - transferFromByRegisterCall(pToken1Path, GetOrigPkgAddr(), recipient, uint64(amount1)) + transferByRegisterCall(pToken1Path, recipient, uint64(amount1)) // adjust position position.tokensOwed0 -= amount0 @@ -182,7 +180,9 @@ func Swap( zeroForOne bool, amountSpecified bigint, sqrtPriceLimitX96 bigint, + payer std.Address, // router ) (bigint, bigint) { + // r3v4_xxx: ONLY ROUTER CAN CALL THIS require(amountSpecified != 0, "[POOL] pool.gno__Swap() || amountSpecified can't be zero") pool := GetPool(pToken0Path, pToken1Path, pFee) @@ -374,67 +374,61 @@ func Swap( amount1 = amountSpecified - state.amountSpecifiedRemaining } + // TOKEN TRANFSER if zeroForOne { if amount1 < 0 { require(pool.balances.token1 > (-amount1), ufmt.Sprintf("[POOL] pool.gno__Swap() || pool.balances.token1(%s) > (-1 * amount1)(%s)", pool.balances.token1, (-amount1))) - ok := transferByRegisterCall(pToken1Path, recipient, uint64(-amount1)) + ok := transferByRegisterCall(pool.token1Path, recipient, uint64(-amount1)) if !ok { - panic("[POOL] pool.gno__Swap() || transferByRegisterCall(pToken1Path, recipient, uint64(-amount1)) failed") + panic("[POOL] pool.gno__Swap() || transferByRegisterCall(pool.token1Path, recipient, uint64(-amount1)) failed") } pool.balances.token1 += amount1 } - balance0Before := bigint(balanceOfByRegisterCall(pToken0Path, GetOrigPkgAddr())) - - txOrigin := GetOrigCaller() // token should be transferred from actual user(GetOrigCaller), not from the realm(PrevRealm) - poolPkg := GetOrigPkgAddr() + balance0Before := bigint(balanceOfByRegisterCall(pool.token0Path, GetOrigPkgAddr())) - ok := transferFromByRegisterCall(pToken0Path, txOrigin, poolPkg, uint64(amount0)) + ok := transferFromByRegisterCall(pool.token0Path, payer, ADDR_POOL, uint64(amount0)) if !ok { - panic("[POOL] pool.gno__Swap() || transferFromByRegisterCall(pToken0Path, from, to, uint64(amount0)) failed") + panic("[POOL] pool.gno__Swap() || transferFromByRegisterCall(pool.token0Path, payer, ADDR_POOL, uint64(amount0)) failed") } require( - balance0Before+amount0 <= bigint(balanceOfByRegisterCall(pToken0Path, GetOrigPkgAddr())), + balance0Before+amount0 <= bigint(balanceOfByRegisterCall(pool.token0Path, GetOrigPkgAddr())), ufmt.Sprintf( - "[POOL] pool.gno__Swap() || balance0Before(%d) + amount0(%d) <= balanceOfByRegisterCall(pToken0Path, GetOrigPkgAddr())(%d)", - balance0Before, amount0, balanceOfByRegisterCall(pToken0Path, GetOrigPkgAddr()), + "[POOL] pool.gno__Swap() || balance0Before(%d) + amount0(%d) <= balanceOfByRegisterCall(pool.token0Path, GetOrigPkgAddr())(%d)", + balance0Before, amount0, balanceOfByRegisterCall(pool.token0Path, GetOrigPkgAddr()), ), ) pool.balances.token0 += amount0 require(pool.balances.token0 >= 0, ufmt.Sprintf("[POOL] pool.gno__Swap() || pool.balances.token0(%s) >= 0__#1", pool.balances.token0)) require(pool.balances.token1 >= 0, ufmt.Sprintf("[POOL] pool.gno__Swap() || pool.balances.token1(%s) >= 0__#1", pool.balances.token1)) - } else { if amount0 < 0 { require(pool.balances.token0 > (-amount0), ufmt.Sprintf("[POOL] pool.gno__Swap() || pool.balances.token0(%s) > (-1 * amount0)(%s)", pool.balances.token0, (-amount0))) - ok := transferByRegisterCall(pToken0Path, recipient, uint64(-amount0)) + ok := transferByRegisterCall(pool.token0Path, recipient, uint64(-amount0)) if !ok { - panic("[POOL] pool.gno__Swap() || transferByRegisterCall(pToken0Path, recipient, uint64(-amount0)) failed") + panic("[POOL] pool.gno__Swap() || transferByRegisterCall(pool.token0Path, recipient, uint64(-amount0)) failed") } pool.balances.token0 += amount0 } - balance1Before := bigint(balanceOfByRegisterCall(pToken1Path, GetOrigPkgAddr())) - - txOrigin := GetOrigCaller() // token should be transferred from actual user(GetOrigCaller), not from the realm(PrevRealm) - poolPkg := GetOrigPkgAddr() + balance1Before := bigint(balanceOfByRegisterCall(pool.token1Path, GetOrigPkgAddr())) - ok := transferFromByRegisterCall(pToken1Path, txOrigin, poolPkg, uint64(amount1)) + ok := transferFromByRegisterCall(pool.token1Path, payer, ADDR_POOL, uint64(amount1)) if !ok { - panic("[POOL] pool.gno__Swap() || transferFromByRegisterCall(pToken1Path, from, to, uint64(amount1)) failed") + panic("[POOL] pool.gno__Swap() || transferFromByRegisterCall(pool.token1Path, payer, ADDR_POOL, uint64(amount1)) failed") } require( - balance1Before+amount1 <= bigint(balanceOfByRegisterCall(pToken1Path, GetOrigPkgAddr())), + balance1Before+amount1 <= bigint(balanceOfByRegisterCall(pool.token1Path, GetOrigPkgAddr())), ufmt.Sprintf( - "[POOL] pool.gno__Mint() || balance1Before(%d) + amount1(%d) <= balanceOfByRegisterCall(pToken1Path, GetOrigPkgAddr())(%d)", - balance1Before, amount1, balanceOfByRegisterCall(pToken1Path, GetOrigPkgAddr()), + "[POOL] pool.gno__Swap() || balance1Before(%d) + amount1(%d) <= balanceOfByRegisterCall(pool.token1Path, GetOrigPkgAddr())(%d)", + balance1Before, amount1, balanceOfByRegisterCall(pool.token1Path, GetOrigPkgAddr()), ), ) @@ -469,38 +463,42 @@ func SetFeeProtocol( g.SetGovParameter("protocoL_fees", feeProtocol0+(feeProtocol1<<4)) } -// // ADMIN -// func CollectProtocol( -// pToken0 string, -// pToken1 string, -// pFee uint16, -// recipient std.Address, -// amount0Requested bigint, -// amount1Requested bigint, -// ) (bigint, bigint) { -// requireUnsigned(amount0Requested, ufmt.Sprintf("[POOL] pool.gno__CollectProtocol() || amount0Requested(%s) >= 0", amount0Requested)) -// requireUnsigned(amount1Requested, ufmt.Sprintf("[POOL] pool.gno__CollectProtocol() || amount1Requested(%s) >= 0", amount1Requested)) -// require(isAdmin(PrevRealmAddr()), ufmt.Sprintf("[POOL] pool.gno__CollectProtocol() || caller(%s) must be admin", PrevRealmAddr())) +// ADMIN +func CollectProtocol( + pToken0Path string, + pToken1Path string, + pFee uint16, + recipient std.Address, + amount0Requested bigint, + amount1Requested bigint, +) (bigint, bigint) { + requireUnsigned(amount0Requested, ufmt.Sprintf("[POOL] pool.gno__CollectProtocol() || amount0Requested(%s) >= 0", amount0Requested)) + requireUnsigned(amount1Requested, ufmt.Sprintf("[POOL] pool.gno__CollectProtocol() || amount1Requested(%s) >= 0", amount1Requested)) + require(isAdmin(PrevRealmAddr()), ufmt.Sprintf("[POOL] pool.gno__CollectProtocol() || caller(%s) must be admin", PrevRealmAddr())) -// pool := GetPool(pToken0, pToken1, pFee) + pool := GetPool(pToken0Path, pToken1Path, pFee) -// amount0 := min(amount0Requested, pool.protocolFees.token0) -// requireUnsigned(amount0, ufmt.Sprintf("[POOL] pool.gno__CollectProtocol() || amount0(%s) >= 0", amount0)) + amount0 := min(amount0Requested, pool.protocolFees.token0) + requireUnsigned(amount0, ufmt.Sprintf("[POOL] pool.gno__CollectProtocol() || amount0(%s) >= 0", amount0)) -// amount1 := min(amount1Requested, pool.protocolFees.token1) -// requireUnsigned(amount1, ufmt.Sprintf("[POOL] pool.gno__CollectProtocol() || amount1(%s) >= 0", amount1)) + amount1 := min(amount1Requested, pool.protocolFees.token1) + requireUnsigned(amount1, ufmt.Sprintf("[POOL] pool.gno__CollectProtocol() || amount1(%s) >= 0", amount1)) -// // without procotol fee -// amount0, amount1 = pool.saveProtocolFees(amount0, amount1) + // without procotol fee + amount0, amount1 = pool.saveProtocolFees(amount0, amount1) -// // pool.token0.Transfer(a2u(recipient), uint64(amount0)) -// foo.Transfer(a2u(recipient), uint64(amount0)) + ok := transferByRegisterCall(pool.token0Path, recipient, uint64(amount0)) + if !ok { + panic("[POOL] pool.gno__CollectProtocol() || transferByRegisterCall(pool.token0Path, recipient, uint64(amount0)) failed") + } -// // pool.token1.Transfer(a2u(recipient), uint64(amount1)) -// bar.Transfer(a2u(recipient), uint64(amount1)) + ok = transferByRegisterCall(pool.token1Path, recipient, uint64(amount1)) + if !ok { + panic("[POOL] pool.gno__CollectProtocol() || transferByRegisterCall(pool.token1Path, recipient, uint64(amount1)) failed") + } -// return amount0, amount1 -// } + return amount0, amount1 +} func (pool *Pool) modifyPosition(params ModifyPositionParams) (PositionInfo, bigint, bigint) { position := pool.updatePosition( @@ -590,6 +588,8 @@ func (pool *Pool) updatePosition( } } + // NO LIQ, ONLY BURN 0 + feeGrowthInside0X128, feeGrowthInside1X128 := pool.tickGetFeeGrowthInside( tickLower, tickUpper, @@ -619,37 +619,6 @@ func (pool *Pool) updatePosition( return position } -// r3v4_xxx -// unused for now -func transfer(token *grc20.AdminToken, amount bigint) { - balanceBefore, err := token.BalanceOf(GetOrigPkgAddr()) - if err != nil { - balanceBefore = 0 - } - from := GetOrigCaller() // token should be transferred from actual user(GetOrigCaller), not from the realm(PrevRealm) - to := GetOrigPkgAddr() - - token.TransferFrom(GetOrigPkgAddr(), from, to, uint64(amount)) - - balancAfter, err := token.BalanceOf(GetOrigPkgAddr()) - if err != nil { - balancAfter = 0 - } - require( - balanceBefore+uint64(amount) <= balancAfter, - ufmt.Sprintf( - "[POOL] pool.gno__mint() || balanceBefore(%s) + amount(%s) <= balancAfter(%s)", - ), - ) -} - -// r3v4_xxx -// unused for now -func (pool *Pool) collect(token *grc20.AdminToken, poolBalance, requestAmount bigint, recipient std.Address) { - require(poolBalance >= requestAmount, ufmt.Sprintf("[POOL] pool.gno__collect() || poolBalance(%s) >= requestAmount(%s)", poolBalance, requestAmount)) - token.Transfer(GetOrigPkgAddr(), recipient, uint64(requestAmount)) -} - func (pool *Pool) saveProtocolFees(amount0, amount1 bigint) (bigint, bigint) { if amount0 > 0 && amount0 == pool.protocolFees.token0 { amount0-- @@ -665,13 +634,3 @@ func (pool *Pool) saveProtocolFees(amount0, amount1 bigint) (bigint, bigint) { // return rest fee return amount0, amount1 } - -func checkTicks(tickLower, tickUpper bigint) { - require(tickLower < tickUpper, ufmt.Sprintf("[POOL] pool.gno__checkTicks() || tickLower(%s) < tickUpper(%s)", tickLower, tickUpper)) - require(tickLower >= MIN_TICK, ufmt.Sprintf("[POOL] pool.gno__checkTicks() || tickLower(%s) >= MIN_TICK(%s)", tickLower, MIN_TICK)) - require(tickUpper <= MAX_TICK, ufmt.Sprintf("[POOL] pool.gno__checkTicks() || tickUpper(%s) <= MAX_TICK(%s)", tickUpper, MAX_TICK)) -} - -func a2u(addr std.Address) users.AddressOrName { - return users.AddressOrName(addr) -} diff --git a/pool/pool_register.gno b/pool/pool_register.gno index 009774d9..265d9227 100644 --- a/pool/pool_register.gno +++ b/pool/pool_register.gno @@ -56,10 +56,9 @@ func RegisterGRC20Interface(pkgPath string, igrc20 GRC20Interface) { // } _, found := findGRC20(pkgPath) - if found { + if !found { + appendGRC20Interface(pkgPath, igrc20) } - - appendGRC20Interface(pkgPath, igrc20) } func UnregisterGRC20Interface(pkgPath string) { diff --git a/pool/pool_router.gno b/pool/pool_router.gno deleted file mode 100644 index bce84494..00000000 --- a/pool/pool_router.gno +++ /dev/null @@ -1,155 +0,0 @@ -package pool - -import ( - "strconv" - "strings" - - "gno.land/p/demo/ufmt" -) - -func FindBestPool( - tokenA string, - tokenB string, - zeroForOne bool, - amountSpecified bigint, -) string { - if tokenA == tokenB { - panic("token pair cannot be the same") - } - - if tokenA > tokenB { - tokenA, tokenB = tokenB, tokenA - } - - partialPath := tokenA + "_" + tokenB + "_" - foundPool := []string{} - - for poolPath, _ := range pools { - if strings.HasPrefix(poolPath, partialPath) { - foundPool = append(foundPool, poolPath) - } - } - - // check if amount is enough - firstSelectedPool := []string{} - if false { - continue - } else if zeroForOne == true && amountSpecified > 0 { - for _, singlePool := range foundPool { - func() { - defer func() { - if r := recover(); r != nil { - continue - } - }() - - _, _, fee := poolPathDivide(singlePool) - es0, es1 := DrySwap( - tokenA, - tokenB, - fee, - "", - true, - amountSpecified, - MIN_PRICE, - ) - - pool := GetPoolFromPoolKey(singlePool) - if pool.balances.token1 > (-1 * es1) { // must be bigger (can't be equal due to fee) - firstSelectedPool = append(firstSelectedPool, singlePool) - } - }() - } - - } else if zeroForOne == true && amountSpecified < 0 { - for _, singlePool := range foundPool { - pool := GetPoolFromPoolKey(singlePool) - - if pool.balances.token1 > (-1 * amountSpecified) { // must be bigger (can't be equal due to fee) - firstSelectedPool = append(firstSelectedPool, singlePool) - } - } - - } else if zeroForOne == false && amountSpecified > 0 { - for _, singlePool := range foundPool { - func() { - defer func() { - if r := recover(); r != nil { - continue - } - }() - - _, _, fee := poolPathDivide(singlePool) - es0, es1 := DrySwap( - tokenA, - tokenB, - fee, - "", - false, - amountSpecified, - MAX_PRICE, - ) - - pool := GetPoolFromPoolKey(singlePool) - if pool.balances.token1 > (-1 * es0) { // must be bigger (can't be equal due to fee) - firstSelectedPool = append(firstSelectedPool, singlePool) - } - }() - } - - } else if zeroForOne == false && amountSpecified < 0 { - for _, singlePool := range foundPool { - pool := GetPoolFromPoolKey(singlePool) - if pool.balances.token0 > (-1 * amountSpecified) { // must be bigger (can't be equal due to fee) - firstSelectedPool = append(firstSelectedPool, singlePool) - } - } - } else { - panic(ufmt.Sprintf("[POOL] pool_router.gno__FindBestPool() || unknown swap condition, zeroForOne: %t, amountSpecified: %d", zeroForOne, amountSpecified)) - } - - // check tick and return - var poolWithTick map[int32]string = make(map[int32]string) - - minTick := int32(887272) - maxTick := int32(-887272) - for _, singlePool := range firstSelectedPool { - // save tick with poolPath - pool := GetPoolFromPoolKey(singlePool) - poolTick := pool.slot0.tick - - poolWithTick[poolTick] = singlePool - - // find min - if poolTick < minTick { - minTick = poolTick - } - - // find max - if poolTick > maxTick { - maxTick = poolTick - } - } - - if zeroForOne == true { // if token0 is being sold to buy token1, then we want to find the pool with the largest tick (more token1 can be bought) - return ApiGetPool(poolWithTick[maxTick]) - - } else { // if token1 is being sold to buy token0, then we want to find the pool with the smallest tick (more token0 can be bought) - return ApiGetPool(poolWithTick[minTick]) - } -} - -func poolPathDivide(poolPath string) (string, string, uint16) { - poolPathSplit := strings.Split(poolPath, "_") - - if len(poolPathSplit) != 3 { - panic(ufmt.Sprintf("[POOL] pool_router.gno__poolPathDivide() || len(poolPathSplit) != 3, poolPath: %s", poolPath)) - } - - feeInt, err := strconv.Atoi(poolPathSplit[2]) - if err != nil { - panic(ufmt.Sprintf("[POOL] pool_router.gno__poolPathDivide() || cannot convert fee(%s) to uint16", poolPathSplit[2])) - } - - return poolPathSplit[0], poolPathSplit[1], uint16(feeInt) -} diff --git a/pool/swap_math.gno b/pool/swap_math.gno index e38f8f0f..5701a3fe 100644 --- a/pool/swap_math.gno +++ b/pool/swap_math.gno @@ -10,11 +10,10 @@ func swapMathComputeSwapStep( liquidity bigint, amountRemaining bigint, feePips uint32, -) (bigint, bigint, bigint, bigint) { +) (sqrtRatioNextX96, amountIn, amountOut, feeAmount bigint) { requireUnsigned(sqrtRatioCurrentX96, ufmt.Sprintf("[POOL] swap_math.gno__swapMathComputeSwapStep() || sqrtRatioCurrentX96(%s) >= 0", sqrtRatioCurrentX96)) requireUnsigned(sqrtRatioTargetX96, ufmt.Sprintf("[POOL] swap_math.gno__swapMathComputeSwapStep() || sqrtRatioTargetX96(%s) >= 0", sqrtRatioTargetX96)) requireUnsigned(liquidity, ufmt.Sprintf("[POOL] swap_math.gno__swapMathComputeSwapStep() || liquidity(%s) >= 0", liquidity)) - var sqrtRatioNextX96, amountIn, amountOut, feeAmount bigint zeroForOne := sqrtRatioCurrentX96 >= sqrtRatioTargetX96 exactIn := amountRemaining >= 0 @@ -94,7 +93,7 @@ func swapMathComputeSwapStep( requireUnsigned(amountOut, ufmt.Sprintf("[POOL] swap_math.gno__swapMathComputeSwapStep() || amountOut(%s) >= 0__#2", amountOut)) } - if !exactIn && amountOut > -amountRemaining { + if !exactIn && amountOut > amountRemaining { amountOut = -amountRemaining requireUnsigned(amountOut, ufmt.Sprintf("[POOL] swap_math.gno__swapMathComputeSwapStep() || amountOut(%s) >= 0__#3", amountOut)) } diff --git a/pool/utils.gno b/pool/utils.gno index 46e7a9f4..094cd1cd 100644 --- a/pool/utils.gno +++ b/pool/utils.gno @@ -3,15 +3,19 @@ package pool import ( "std" - "gno.land/p/demo/grc/grc20" + "gno.land/r/demo/users" + + "gno.land/p/demo/ufmt" ) -func balanceOf(token *grc20.AdminToken, addr std.Address) bigint { - balance, err := token.BalanceOf(addr) - if err != nil { - panic(err) - } - return bigint(balance) +func checkTicks(tickLower, tickUpper bigint) { + require(tickLower < tickUpper, ufmt.Sprintf("[POOL] pool.gno__checkTicks() || tickLower(%s) < tickUpper(%s)", tickLower, tickUpper)) + require(tickLower >= MIN_TICK, ufmt.Sprintf("[POOL] pool.gno__checkTicks() || tickLower(%s) >= MIN_TICK(%s)", tickLower, MIN_TICK)) + require(tickUpper <= MAX_TICK, ufmt.Sprintf("[POOL] pool.gno__checkTicks() || tickUpper(%s) <= MAX_TICK(%s)", tickUpper, MAX_TICK)) +} + +func a2u(addr std.Address) users.AddressOrName { + return users.AddressOrName(addr) } func requireUnsigned(x bigint, msg string) { @@ -39,3 +43,25 @@ func abs(x bigint) bigint { } return x } + +func removeDuplicateString(strSlice []string) []string { + // map to store unique keys + keys := make(map[string]bool) + returnSlice := []string{} + for _, item := range strSlice { + if _, value := keys[item]; !value { + keys[item] = true + returnSlice = append(returnSlice, item) + } + } + return returnSlice +} + +func remove(s []string, r string) []string { + for i, v := range s { + if v == r { + return append(s[:i], s[i+1:]...) + } + } + return s +} diff --git a/position/consts.gno b/position/consts.gno index 2e9f85a4..8d198a60 100644 --- a/position/consts.gno +++ b/position/consts.gno @@ -16,4 +16,14 @@ const ( Q128 bigint = 340282366920938463463374607431768211456 // 2 ** 128 MAX_UINT160 bigint = 1461501637330902918203684832716283019655932542975 + + // Tick Related + MIN_TICK int32 = -887272 + MAX_TICK int32 = 887272 + + MIN_SQRT_RATIO bigint = 4295128739 // same as TickMathGetSqrtRatioAtTick(MIN_TICK) + MAX_SQRT_RATIO bigint = 1461446703485210103287273052203988822378723970342 // same as TickMathGetSqrtRatioAtTick(MAX_TICK) + + MIN_PRICE bigint = 4295128740 // MIN_SQRT_RATIO + 1 + MAX_PRICE bigint = 1461446703485210103287273052203988822378723970341 // MAX_SQRT_RATIO - 1 ) diff --git a/position/math_logic_test.gno b/position/math_logic_test.gnoa similarity index 100% rename from position/math_logic_test.gno rename to position/math_logic_test.gnoa diff --git a/position/position_test.gno b/position/position_test.gno index 16dd5f25..acd823ed 100644 --- a/position/position_test.gno +++ b/position/position_test.gno @@ -1,15 +1,17 @@ package position import ( - "encoding/gjson" "std" "testing" - "gno.land/p/demo/testutils" + "encoding/gjson" - p "gno.land/r/pool" + "gno.land/p/demo/testutils" nft "gno.land/r/gnft" + + _ "gno.land/r/grc20_wrapper" + p "gno.land/r/pool" ) var ( @@ -24,8 +26,8 @@ var ( var ( // Common - pToken0 = "foo" - pToken1 = "bar" + fooPath = "gno.land/r/foo" + barPath = "gno.land/r/bar" pFee = uint16(500) test_tickLower = int32(9000) @@ -37,7 +39,7 @@ var ( func TestPoolInitCreatePool(t *testing.T) { std.TestSetOrigCaller(own) p.InitManual() - p.CreatePool("foo", "bar", pFee, 130621891405341611593710811006) + p.CreatePool(fooPath, barPath, pFee, 130621891405341611593710811006) // fee // 500 = 0.05% // USv3 default @@ -46,7 +48,7 @@ func TestPoolInitCreatePool(t *testing.T) { // sqrtPrice // 130621891405341611593710811006 // tick = 10000 - shouldPanic(t, func() { p.CreatePool("foo", "bar", 500, 130621891405341611593710811006) }) + shouldPanic(t, func() { p.CreatePool(fooPath, barPath, 500, 130621891405341611593710811006) }) } // 2. Mint LP and Get GNFT @@ -60,8 +62,8 @@ func TestMint(t *testing.T) { poolOldToken1Bal := Token1Bal(PoolAddr) tTokenId, tLiquidity, tAmount0, tAmount1 := Mint( - pToken0, - pToken1, + fooPath, + barPath, pFee, test_tickLower, test_tickUpper, @@ -75,6 +77,7 @@ func TestMint(t *testing.T) { isOwner(t, tTokenId, lp01) shouldEQ(t, tTokenId, 1) shouldEQ(t, getNextId(), 2) + shouldEQ(t, Token0Bal(PoolAddr), poolOldToken0Bal+tAmount0) shouldEQ(t, Token1Bal(PoolAddr), poolOldToken1Bal+tAmount1) } @@ -88,8 +91,8 @@ func TestMint(t *testing.T) { poolOldToken1Bal := Token1Bal(PoolAddr) tTokenId, tLiquidity, tAmount0, tAmount1 := Mint( - pToken0, - pToken1, + fooPath, + barPath, pFee, test_tickLower, test_tickUpper, @@ -115,8 +118,8 @@ func TestMint(t *testing.T) { poolOldToken1Bal := Token1Bal(PoolAddr) tTokenId, tLiquidity, tAmount0, tAmount1 := Mint( - pToken0, - pToken1, + fooPath, + barPath, pFee, int32(14000), int32(18000), @@ -144,8 +147,8 @@ func TestMint(t *testing.T) { poolOldToken1Bal := Token1Bal(PoolAddr) tTokenId, tLiquidity, tAmount0, tAmount1 := Mint( - pToken0, - pToken1, + fooPath, + barPath, pFee, int32(7000), int32(9000), @@ -210,7 +213,7 @@ func TestDecreaseLiquidity(t *testing.T) { // lp01 decreases liquidity at tid 1 position ( in range ) { std.TestSetOrigCaller(lp01) - pool := p.GetPool(pToken0, pToken1, pFee) + pool := p.GetPool(fooPath, barPath, pFee) tTargetLiquidity := bigint(1234) @@ -273,7 +276,7 @@ func TestCollect(t *testing.T) { // lp01 did decrease some liquidity => there are some to collect { std.TestSetOrigCaller(lp01) - pool := p.GetPool(pToken0, pToken1, pFee) + pool := p.GetPool(fooPath, barPath, pFee) poolOldLiquidity := pool.GetLiquidity() poolOldToken0Bal := Token0Bal(PoolAddr) @@ -308,7 +311,7 @@ func TestCollect(t *testing.T) { // lp01 collect all { std.TestSetOrigCaller(lp01) - pool := p.GetPool(pToken0, pToken1, pFee) + pool := p.GetPool(fooPath, barPath, pFee) poolOldLiquidity := pool.GetLiquidity() poolOldToken0Bal := Token0Bal(PoolAddr) @@ -342,7 +345,7 @@ func TestCollect(t *testing.T) { // lp02 didn't decrease any liquidity => nothing to collect { std.TestSetOrigCaller(lp02) - pool := p.GetPool(pToken0, pToken1, pFee) + pool := p.GetPool(fooPath, barPath, pFee) poolOldLiquidity := pool.GetLiquidity() poolOldToken0Bal := Token0Bal(PoolAddr) @@ -468,7 +471,7 @@ func TestApiGetPositionByUser(t *testing.T) { shouldEQ(t, len(jsonStr.Get("response.data").Array()), 3) shouldEQ(t, jsonStr.Get("response.data.0.token_id").Int(), 2) - shouldEQ(t, jsonStr.Get("response.data.0.pool_key").String(), "bar_foo_500") + shouldEQ(t, jsonStr.Get("response.data.0.pool_key").String(), "gno.land/r/bar:gno.land/r/foo:500") shouldEQ(t, jsonStr.Get("response.data.0.tick_lower").Int(), 9000) shouldEQ(t, jsonStr.Get("response.data.0.tick_upper").Int(), 11000) shouldEQ(t, jsonStr.Get("response.data.0.liquidity").Int(), 24874) @@ -482,7 +485,6 @@ func TestApiGetPositionByUser(t *testing.T) { shouldEQ(t, jsonStr.Get("response.data.2.tick_lower").Int(), 7000) shouldEQ(t, jsonStr.Get("response.data.2.tick_upper").Int(), 9000) shouldEQ(t, jsonStr.Get("response.data.2.liquidity").Int(), 6700) - } /* HELPER */ diff --git a/position/test_helper.gno b/position/test_helper.gno index 7ec463ef..bb7566b9 100644 --- a/position/test_helper.gno +++ b/position/test_helper.gno @@ -37,9 +37,9 @@ func tid(tokenId interface{}) grc721.TokenID { } func Token0Bal(addr std.Address) bigint { - return bigint(foo.BalanceOf(a2u(addr))) + return bigint(bar.BalanceOf(a2u(addr))) } func Token1Bal(addr std.Address) bigint { - return bigint(bar.BalanceOf(a2u(addr))) + return bigint(foo.BalanceOf(a2u(addr))) } diff --git a/position/util.gno b/position/util.gno index 289fcc01..56111cfa 100644 --- a/position/util.gno +++ b/position/util.gno @@ -25,7 +25,7 @@ func a2u(addr std.Address) users.AddressOrName { } func poolKeyDivide(poolKey string) (string, string, uint16) { - res := strings.Split(poolKey, "_") + res := strings.Split(poolKey, ":") if len(res) != 3 { panic(ufmt.Sprintf("[POSITION] util.gno__poolKeyDivide() || invalid poolKey(%s)", poolKey)) } diff --git a/router/_TEST_router_getter_api_test.gno b/router/_TEST_router_getter_api_test.gno new file mode 100644 index 00000000..eee4e65c --- /dev/null +++ b/router/_TEST_router_getter_api_test.gno @@ -0,0 +1,84 @@ +// EXTERNAL API +package router + +import ( + "encoding/gjson" + "std" + "testing" + + p "gno.land/r/pool" + pos "gno.land/r/position" + + "gno.land/p/demo/testutils" + + _ "gno.land/r/grc20_wrapper" +) + +var ( + gsa = testutils.TestAddress("gsa") // Gnoswap Admin + lp01 = testutils.TestAddress("lp01") // Liquidity Provider 01 + + // Common + fooPath = "gno.land/r/foo" + barPath = "gno.land/r/bar" + bazPath = "gno.land/r/baz" + quxPath = "gno.land/r/qux" + + test_fee100 = uint16(100) + + max_timeout = bigint(9999999999) +) + +func TestInitManual(t *testing.T) { + std.TestSetOrigCaller(gsa) + p.InitManual() +} + +func TestCreatePool(t *testing.T) { + std.TestSetOrigCaller(gsa) + + p.CreatePool(fooPath, barPath, test_fee100, 130621891405341611593710811006) // tick = 10_000, ratio = 2.7181459268252253 // 1 bar == 2.7 foo + p.CreatePool(barPath, bazPath, test_fee100, 130621891405341611593710811006) // tick = 10_000, ratio = 2.7181459268252253 // 1 bar == 2.7 baz + p.CreatePool(bazPath, quxPath, test_fee100, 130621891405341611593710811006) // tick = 10_000, ratio = 2.7181459268252253 // 1 baz == 2.7 qux +} + +func TestPositionMint(t *testing.T) { + std.TestSetOrigCaller(lp01) + + pos.Mint(fooPath, barPath, test_fee100, int32(9000), int32(11000), bigint(10000000), bigint(10000000), 0, 0, max_timeout) + pos.Mint(barPath, bazPath, test_fee100, int32(9000), int32(11000), bigint(10000000), bigint(10000000), 0, 0, max_timeout) + pos.Mint(bazPath, quxPath, test_fee100, int32(9000), int32(11000), bigint(10000000), bigint(10000000), 0, 0, max_timeout) +} + +func TestApiGetRatiosFromBase(t *testing.T) { + jsonStr := ApiGetRatiosFromBase() + jsonOutput := gjson.Parse(jsonStr) + + shouldEQ(t, len(jsonOutput.Get("response.data").Array()), 4) + shouldEQ(t, jsonOutput.Get("response.data.0").String(), "{\"gno.land/r/foo\":79228162514264337593543950336}") + shouldEQ(t, jsonOutput.Get("response.data.1").String(), "{\"gno.land/r/bar\":29147869410676662479573841822}") + shouldEQ(t, jsonOutput.Get("response.data.2").String(), "{\"gno.land/r/baz\":79228162514264337593543950333}") + shouldEQ(t, jsonOutput.Get("response.data.3").String(), "{\"gno.land/r/qux\":215353707227994575755767921538}") + /* + len(tokenPrice): 4 + + token: gno.land/r/foo + price: 79228162514264337593543950336 ~= 1 + + token: gno.land/r/bar + price: 29147869410676662479573841822 ~= 0.3678978344 + + token: gno.land/r/baz + price: 79228162514264337593543950333 ~= 1 + + token: gno.land/r/qux + price: 215353707227994575755767921538 ~= 2.7181459268 + */ +} + +/* HELPER */ +func shouldEQ(t *testing.T, got, expected interface{}) { + if got != expected { + t.Errorf("got %v, expected %v", got, expected) + } +} diff --git a/router/_TEST_router_multi_path_dry_test.gnoa b/router/_TEST_router_multi_path_dry_test.gnoa new file mode 100644 index 00000000..7d56925d --- /dev/null +++ b/router/_TEST_router_multi_path_dry_test.gnoa @@ -0,0 +1,102 @@ +package router + +import ( + "encoding/gjson" + "std" + "testing" + + "gno.land/p/demo/testutils" + + _ "gno.land/r/grc20_wrapper" + p "gno.land/r/pool" + pos "gno.land/r/position" +) + +var ( + gsa = testutils.TestAddress("gsa") // Gnoswap Admin + 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" // token1 + barPath = "gno.land/r/bar" // token2 + bazPath = "gno.land/r/baz" // token3 + quxPath = "gno.land/r/qux" // token4 + + test_fee100 = uint16(100) + test_fee500 = uint16(500) + + max_timeout = bigint(9999999999) +) + +func TestInitManual(t *testing.T) { + std.TestSetOrigCaller(gsa) + p.InitManual() + std.TestSkipHeights(1) +} + +func TestCreatePool(t *testing.T) { + std.TestSetOrigCaller(gsa) + + p.CreatePool(fooPath, barPath, test_fee100, 130621891405341611593710811006) // tick = 10_000, ratio = 2.7181459268252253 + p.CreatePool(barPath, quxPath, test_fee100, 130621891405341611593710811006) // tick = 10_000, ratio = 2.7181459268252253 + + p.CreatePool(fooPath, barPath, test_fee500, 101729702841318637793976746270) // tick = 5_000, ratio = 1.648680055931176 + p.CreatePool(barPath, quxPath, test_fee500, 101729702841318637793976746270) // tick = 5_000, ratio = 1.648680055931176 + // findSwapPaths("gno.land/r/foo", "gno.land/r/qux") // 2 paths + // foo_qux_100 +} + +func TestPositionMint(t *testing.T) { + std.TestSetOrigCaller(lp01) + pos.Mint(fooPath, barPath, test_fee100, int32(9000), int32(11000), bigint(10000000), bigint(10000000), 0, 0, max_timeout) + pos.Mint(barPath, quxPath, test_fee100, int32(9000), int32(11000), bigint(10000000), bigint(10000000), 0, 0, max_timeout) + + pos.Mint(fooPath, barPath, test_fee500, int32(4000), int32(6000), bigint(10000000), bigint(10000000), 0, 0, max_timeout) + pos.Mint(barPath, quxPath, test_fee500, int32(4000), int32(6000), bigint(10000000), bigint(10000000), 0, 0, max_timeout) +} + +func TestBestSwapDryExactIn(t *testing.T) { + std.TestSetOrigCaller(tr01) + + jsonOutput := BestSwapDry( + "gno.land/r/foo", // inputTokenPath + "gno.land/r/qux", // outputTokenPath + "EXACT_IN", // swapType + 123_456, // amountSpecified + 0, // sqrtPriceLimitX96 + ) + jsonStr := gjson.Parse(jsonOutput) + shouldEQ(t, jsonStr.Get("rpcRoutes.0.numPools").String(), "2") + shouldEQ(t, jsonStr.Get("rpcRoutes.0.swapPaths.0").String(), "gno.land/r/foo:gno.land/r/bar:100") + shouldEQ(t, jsonStr.Get("rpcRoutes.0.swapPaths.1").String(), "gno.land/r/bar:gno.land/r/qux:100") +} + +func TestBestSwapDryExactOut(t *testing.T) { + std.TestSetOrigCaller(tr01) + + jsonOutput := BestSwapDry( + "gno.land/r/foo", // inputTokenPath + "gno.land/r/qux", // outputTokenPath + "EXACT_OUT", // swapType + 123_456, // amountSpecified + 0, // sqrtPriceLimitX96 + ) + jsonStr := gjson.Parse(jsonOutput) + shouldEQ(t, jsonStr.Get("rpcRoutes.0.numPools").String(), "2") + shouldEQ(t, jsonStr.Get("rpcRoutes.0.swapPaths.0").String(), "gno.land/r/foo:gno.land/r/bar:100") + shouldEQ(t, jsonStr.Get("rpcRoutes.0.swapPaths.1").String(), "gno.land/r/bar:gno.land/r/qux:100") +} + +/* HELPER */ +func shouldEQ(t *testing.T, got, expected interface{}) { + if got != expected { + t.Errorf("got %v, expected %v", got, expected) + } +} diff --git a/router/_TEST_router_multi_path_swap_test.gnoa b/router/_TEST_router_multi_path_swap_test.gnoa new file mode 100644 index 00000000..0b486619 --- /dev/null +++ b/router/_TEST_router_multi_path_swap_test.gnoa @@ -0,0 +1,245 @@ +package router + +import ( + "encoding/gjson" + "std" + "testing" + + "gno.land/p/demo/testutils" + + _ "gno.land/r/grc20_wrapper" + p "gno.land/r/pool" + pos "gno.land/r/position" + + "gno.land/r/bar" + "gno.land/r/foo" + "gno.land/r/qux" +) + +var ( + gsa = testutils.TestAddress("gsa") // Gnoswap Admin + 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" // token1 + barPath = "gno.land/r/bar" // token2 + bazPath = "gno.land/r/baz" // token3 + quxPath = "gno.land/r/qux" // token4 + + test_fee100 = uint16(100) + test_fee500 = uint16(500) + + max_timeout = bigint(9999999999) +) + +// func init() { +// println(gsa, "// gsa") +// println(lp01, "// lp01") +// println(tr01, "// tr01") +// println(poolAddr, "// poolAddr") +// println(posAddr, "// posAddr") +// println(routerAddr, "// routerAddr") +// } + +func TestInitManual(t *testing.T) { + std.TestSetOrigCaller(gsa) + p.InitManual() + std.TestSkipHeights(1) +} + +func TestCreatePool(t *testing.T) { + std.TestSetOrigCaller(gsa) + + p.CreatePool(fooPath, barPath, test_fee100, 130621891405341611593710811006) // tick = 10_000, ratio = 2.7181459268252253 + p.CreatePool(barPath, quxPath, test_fee100, 130621891405341611593710811006) // tick = 10_000, ratio = 2.7181459268252253 + + p.CreatePool(fooPath, barPath, test_fee500, 101729702841318637793976746270) // tick = 5_000, ratio = 1.648680055931176 + p.CreatePool(barPath, quxPath, test_fee500, 101729702841318637793976746270) // tick = 5_000, ratio = 1.648680055931176 + // findSwapPaths("gno.land/r/foo", "gno.land/r/qux") // 2 paths + // foo_qux_100 + + jsonOutput := p.ApiGetPools() + jsonStr := gjson.Parse(jsonOutput) + shouldEQ(t, len(jsonStr.Get("response.data").Array()), 4) +} + +func TestPositionMint(t *testing.T) { + std.TestSetOrigCaller(lp01) + { + oldFooBalance := foo.BalanceOf(a2u(poolAddr)) + oldBarBalance := bar.BalanceOf(a2u(poolAddr)) + shouldEQ(t, oldFooBalance, 0) + shouldEQ(t, oldBarBalance, 0) + + tokenId, liquidity, amount0, amount1 := pos.Mint(fooPath, barPath, test_fee100, int32(9000), int32(11000), bigint(10000000), bigint(10000000), 0, 0, max_timeout) + shouldEQ(t, tokenId, 1) + shouldNEQ(t, liquidity, bigint(0)) + shouldNEQ(t, amount0, bigint(0)) + shouldNEQ(t, amount1, bigint(0)) + + newFooBalance := foo.BalanceOf(a2u(poolAddr)) + newBarBalance := bar.BalanceOf(a2u(poolAddr)) + shouldEQ(t, newBarBalance, uint64(amount0)) // 3678978 + shouldEQ(t, newFooBalance, uint64(amount1)) // 9999999 + } + + { + oldBarBalance := bar.BalanceOf(a2u(poolAddr)) + oldQuxBalance := qux.BalanceOf(a2u(poolAddr)) + shouldEQ(t, oldBarBalance, 3678978) + shouldEQ(t, oldQuxBalance, 0) + + tokenId, liquidity, amount0, amount1 := pos.Mint(barPath, quxPath, test_fee100, int32(9000), int32(11000), bigint(10000000), bigint(10000000), 0, 0, max_timeout) + shouldEQ(t, tokenId, 2) + shouldNEQ(t, liquidity, bigint(0)) + shouldNEQ(t, amount0, bigint(0)) + shouldNEQ(t, amount1, bigint(0)) + + newBarBalance := bar.BalanceOf(a2u(poolAddr)) + newQuxBalance := qux.BalanceOf(a2u(poolAddr)) + shouldEQ(t, newBarBalance, uint64(amount0)+3678978) // 7357956 + shouldEQ(t, newQuxBalance, uint64(amount1)) // 9999999 + } + + { + oldFooBalance := foo.BalanceOf(a2u(poolAddr)) + oldBarBalance := bar.BalanceOf(a2u(poolAddr)) + shouldEQ(t, oldFooBalance, 9999999) + shouldEQ(t, oldBarBalance, 7357956) + + tokenId, liquidity, amount0, amount1 := pos.Mint(fooPath, barPath, test_fee500, int32(4000), int32(6000), bigint(10000000), bigint(10000000), 0, 0, max_timeout) + shouldEQ(t, tokenId, 3) + shouldNEQ(t, liquidity, bigint(0)) + shouldNEQ(t, amount0, bigint(0)) + shouldNEQ(t, amount1, bigint(0)) + + newFooBalance := foo.BalanceOf(a2u(poolAddr)) + newBarBalance := bar.BalanceOf(a2u(poolAddr)) + shouldEQ(t, newBarBalance, uint64(amount0)+7357956) // 13423414 + shouldEQ(t, newFooBalance, uint64(amount1)+9999999) // 19999998 + } + + { + oldBarBalance := bar.BalanceOf(a2u(poolAddr)) + oldQuxBalance := qux.BalanceOf(a2u(poolAddr)) + shouldEQ(t, oldBarBalance, 13423414) + shouldEQ(t, oldQuxBalance, 9999999) + + tokenId, liquidity, amount0, amount1 := pos.Mint(barPath, quxPath, test_fee500, int32(4000), int32(6000), bigint(10000000), bigint(10000000), 0, 0, max_timeout) + shouldEQ(t, tokenId, 4) + shouldNEQ(t, liquidity, bigint(0)) + shouldNEQ(t, amount0, bigint(0)) + shouldNEQ(t, amount1, bigint(0)) + + newBarBalance := bar.BalanceOf(a2u(poolAddr)) + newQuxBalance := qux.BalanceOf(a2u(poolAddr)) + shouldEQ(t, newBarBalance, uint64(amount0)+13423414) // 19488872 + shouldEQ(t, newQuxBalance, uint64(amount1)+9999999) // 19999998 + } +} + +func TestBestSwapExactInMultiPath(t *testing.T) { + std.TestSetOrigCaller(tr01) + + userOldFooBalance := foo.BalanceOf(a2u(tr01)) + userOldQuxBalance := qux.BalanceOf(a2u(tr01)) + poolOldFooBalance := foo.BalanceOf(a2u(poolAddr)) + poolOldQuxBalance := qux.BalanceOf(a2u(poolAddr)) + + swapAmount := 123_456 + BestSwap( + fooPath, // inputToken + quxPath, // outputToken + "EXACT_IN", // swapType + bigint(swapAmount), // amountSpecified + 0, // sqrtPriceLimitX96 + ) + + userNewFooBalance := foo.BalanceOf(a2u(tr01)) + userNewQuxBalance := qux.BalanceOf(a2u(tr01)) + poolNewFooBalance := foo.BalanceOf(a2u(poolAddr)) + poolNewQuxBalance := qux.BalanceOf(a2u(poolAddr)) + + shouldGT(t, userOldFooBalance, userNewFooBalance) + shouldGT(t, userNewQuxBalance, userOldQuxBalance) + + shouldGT(t, poolNewFooBalance, poolOldFooBalance) + shouldGT(t, poolOldQuxBalance, poolNewQuxBalance) + + shouldLTE(t, userOldFooBalance-userNewFooBalance, swapAmount) +} + +func TestBestSwapExactOutMultiPath(t *testing.T) { + std.TestSetOrigCaller(tr01) + + userOldFooBalance := foo.BalanceOf(a2u(tr01)) + userOldQuxBalance := qux.BalanceOf(a2u(tr01)) + poolOldFooBalance := foo.BalanceOf(a2u(poolAddr)) + poolOldQuxBalance := qux.BalanceOf(a2u(poolAddr)) + + swapAmount := 987_654 + BestSwap( + fooPath, // inputToken + quxPath, // outputToken + "EXACT_OUT", // swapType + bigint(swapAmount), // amountSpecified + 0, // sqrtPriceLimitX96 + ) + + userNewFooBalance := foo.BalanceOf(a2u(tr01)) + userNewQuxBalance := qux.BalanceOf(a2u(tr01)) + poolNewFooBalance := foo.BalanceOf(a2u(poolAddr)) + poolNewQuxBalance := qux.BalanceOf(a2u(poolAddr)) + + shouldGT(t, userOldFooBalance, userNewFooBalance) + shouldGT(t, userNewQuxBalance, userOldQuxBalance) + + shouldGT(t, poolNewFooBalance, poolOldFooBalance) + shouldGT(t, poolOldQuxBalance, poolNewQuxBalance) + + shouldLTE(t, userNewQuxBalance-userOldQuxBalance, -swapAmount) +} + +/* HELPER */ +func shouldEQ(t *testing.T, got, expected interface{}) { + if got != expected { + t.Errorf("got %v, expected %v", got, expected) + } +} + +func shouldNEQ(t *testing.T, got, expected interface{}) { + if got == expected { + t.Errorf("got %v, didn't expected %v", got, expected) + } +} + +func shouldGT(t *testing.T, l, r interface{}) { + if !(l > r) { + t.Errorf("expected %v > %v", l, r) + } +} + +func shouldGTE(t *testing.T, l, r interface{}) { + if !(l >= r) { + t.Errorf("expected %v > %v", l, r) + } +} + +func shouldLT(t *testing.T, l, r interface{}) { + if !(l < r) { + t.Errorf("expected %v < %v", l, r) + } +} + +func shouldLTE(t *testing.T, l, r interface{}) { + if !(l <= r) { + t.Errorf("expected %v < %v", l, r) + } +} diff --git a/router/_TEST_router_single_path_dry_test.gnoa b/router/_TEST_router_single_path_dry_test.gnoa new file mode 100644 index 00000000..e4a25bee --- /dev/null +++ b/router/_TEST_router_single_path_dry_test.gnoa @@ -0,0 +1,103 @@ +package router + +import ( + "encoding/gjson" + "std" + "testing" + + "gno.land/p/demo/testutils" + + _ "gno.land/r/grc20_wrapper" + p "gno.land/r/pool" + pos "gno.land/r/position" +) + +var ( + gsa = testutils.TestAddress("gsa") // Gnoswap Admin + 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" // token1 + barPath = "gno.land/r/bar" // token2 + bazPath = "gno.land/r/baz" // token3 + quxPath = "gno.land/r/qux" // token4 + + test_fee100 = uint16(100) + test_fee500 = uint16(500) + + max_timeout = bigint(9999999999) +) + +// func init() { +// println(gsa, "// gsa") +// println(lp01, "// lp01") +// println(tr01, "// tr01") +// println(poolAddr, "// poolAddr") +// println(posAddr, "// posAddr") +// println(routerAddr, "// routerAddr") +// } + +func TestInitManual(t *testing.T) { + std.TestSetOrigCaller(gsa) + p.InitManual() + std.TestSkipHeights(1) +} + +func TestCreatePool(t *testing.T) { + std.TestSetOrigCaller(gsa) + + p.CreatePool(fooPath, quxPath, test_fee100, 130621891405341611593710811006) // tick = 10_000, ratio = 2.7181459268252253 + p.CreatePool(fooPath, quxPath, test_fee500, 101729702841318637793976746270) // tick = 5_000, ratio = 1.648680055931176 + // findSwapPaths("gno.land/r/foo", "gno.land/r/qux") // 2 paths + // foo_qux_100 +} + +func TestPositionMint(t *testing.T) { + std.TestSetOrigCaller(lp01) + pos.Mint(fooPath, quxPath, test_fee100, int32(9000), int32(11000), bigint(10000000), bigint(10000000), 0, 0, max_timeout) + pos.Mint(fooPath, quxPath, test_fee500, int32(4000), int32(6000), bigint(10000000), bigint(10000000), 0, 0, max_timeout) +} + +func TestBestSwapDryExactIn(t *testing.T) { + std.TestSetOrigCaller(tr01) + + jsonOutput := BestSwapDry( + "gno.land/r/foo", // inputTokenPath + "gno.land/r/qux", // outputTokenPath + "EXACT_IN", // swapType + 123_456, // amountSpecified + 0, // sqrtPriceLimitX96 + ) + jsonStr := gjson.Parse(jsonOutput) + shouldEQ(t, jsonStr.Get("totalRoutes").String(), "1") + shouldEQ(t, jsonStr.Get("rpcRoutes.0.swapPaths.0").String(), "gno.land/r/foo:gno.land/r/qux:100") +} + +func TestBestSwapDryExactOut(t *testing.T) { + std.TestSetOrigCaller(tr01) + + jsonOutput := BestSwapDry( + "gno.land/r/foo", // inputTokenPath + "gno.land/r/qux", // outputTokenPath + "EXACT_OUT", // swapType + 123_456, // amountSpecified + 0, // sqrtPriceLimitX96 + ) + jsonStr := gjson.Parse(jsonOutput) + shouldEQ(t, jsonStr.Get("totalRoutes").String(), "1") + shouldEQ(t, jsonStr.Get("rpcRoutes.0.swapPaths.0").String(), "gno.land/r/foo:gno.land/r/qux:100") +} + +/* HELPER */ +func shouldEQ(t *testing.T, got, expected interface{}) { + if got != expected { + t.Errorf("got %v, expected %v", got, expected) + } +} diff --git a/router/_TEST_router_single_path_swap_test.gnoa b/router/_TEST_router_single_path_swap_test.gnoa new file mode 100644 index 00000000..1510809a --- /dev/null +++ b/router/_TEST_router_single_path_swap_test.gnoa @@ -0,0 +1,207 @@ +package router + +import ( + "encoding/gjson" + "std" + "testing" + + "gno.land/p/demo/testutils" + + _ "gno.land/r/grc20_wrapper" + p "gno.land/r/pool" + pos "gno.land/r/position" + + "gno.land/r/foo" + "gno.land/r/qux" +) + +var ( + gsa = testutils.TestAddress("gsa") // Gnoswap Admin + 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" // token1 + barPath = "gno.land/r/bar" // token2 + bazPath = "gno.land/r/baz" // token3 + quxPath = "gno.land/r/qux" // token4 + + test_fee100 = uint16(100) + test_fee500 = uint16(500) + + max_timeout = bigint(9999999999) +) + +// func init() { +// println(gsa, "// gsa") +// println(lp01, "// lp01") +// println(tr01, "// tr01") +// println(poolAddr, "// poolAddr") +// println(posAddr, "// posAddr") +// println(routerAddr, "// routerAddr") +// } + +func TestInitManual(t *testing.T) { + std.TestSetOrigCaller(gsa) + p.InitManual() + std.TestSkipHeights(1) +} + +func TestCreatePool(t *testing.T) { + std.TestSetOrigCaller(gsa) + + p.CreatePool(fooPath, quxPath, test_fee100, 130621891405341611593710811006) // tick = 10_000, ratio = 2.7181459268252253 + p.CreatePool(fooPath, quxPath, test_fee500, 101729702841318637793976746270) // tick = 5_000, ratio = 1.648680055931176 + // findSwapPaths("gno.land/r/foo", "gno.land/r/qux") // 2 paths + // foo_qux_100 + + jsonOutput := p.ApiGetPools() + jsonStr := gjson.Parse(jsonOutput) + shouldEQ(t, len(jsonStr.Get("response.data").Array()), 2) +} + +func TestPositionMint(t *testing.T) { + std.TestSetOrigCaller(lp01) + { + oldT0Balance := foo.BalanceOf(a2u(poolAddr)) + oldT1Balance := qux.BalanceOf(a2u(poolAddr)) + shouldEQ(t, oldT0Balance, 0) + shouldEQ(t, oldT1Balance, 0) + + tokenId, liquidity, amount0, amount1 := pos.Mint(fooPath, quxPath, test_fee100, int32(9000), int32(11000), bigint(10000000), bigint(10000000), 0, 0, max_timeout) + + shouldEQ(t, tokenId, 1) + shouldNEQ(t, liquidity, bigint(0)) + shouldNEQ(t, amount0, bigint(0)) + shouldNEQ(t, amount1, bigint(0)) + + newT0Balance := foo.BalanceOf(a2u(poolAddr)) + newT1Balance := qux.BalanceOf(a2u(poolAddr)) + shouldEQ(t, newT0Balance, uint64(amount0)) // 3678978 + shouldEQ(t, newT1Balance, uint64(amount1)) // 9999999 + } + + { + oldT0Balance := foo.BalanceOf(a2u(poolAddr)) + oldT1Balance := qux.BalanceOf(a2u(poolAddr)) + shouldEQ(t, oldT0Balance, 3678978) + shouldEQ(t, oldT1Balance, 9999999) + + tokenId, liquidity, amount0, amount1 := pos.Mint(fooPath, quxPath, test_fee500, int32(4000), int32(6000), bigint(10000000), bigint(10000000), 0, 0, max_timeout) + + shouldEQ(t, tokenId, 2) + shouldNEQ(t, liquidity, bigint(0)) + shouldNEQ(t, amount0, bigint(0)) + shouldNEQ(t, amount1, bigint(0)) + + newT0Balance := foo.BalanceOf(a2u(poolAddr)) + newT1Balance := qux.BalanceOf(a2u(poolAddr)) + shouldEQ(t, newT0Balance, uint64(amount0)+3678978) + shouldEQ(t, newT1Balance, uint64(amount1)+9999999) + } +} + +func TestBestSwapExactInSinglePath(t *testing.T) { + std.TestSetOrigCaller(tr01) + + userOldFooBalance := foo.BalanceOf(a2u(tr01)) + userOldQuxBalance := qux.BalanceOf(a2u(tr01)) + poolOldFooBalance := foo.BalanceOf(a2u(poolAddr)) + poolOldQuxBalance := qux.BalanceOf(a2u(poolAddr)) + + swapAmount := 123_456 + BestSwap( + fooPath, // inputToken + quxPath, // outputToken + "EXACT_IN", // swapType + bigint(swapAmount), // amountSpecified + 0, // sqrtPriceLimitX96 + ) + + userNewFooBalance := foo.BalanceOf(a2u(tr01)) + userNewQuxBalance := qux.BalanceOf(a2u(tr01)) + poolNewFooBalance := foo.BalanceOf(a2u(poolAddr)) + poolNewQuxBalance := qux.BalanceOf(a2u(poolAddr)) + + shouldGT(t, userOldFooBalance, userNewFooBalance) + shouldGT(t, userNewQuxBalance, userOldQuxBalance) + + shouldGT(t, poolNewFooBalance, poolOldFooBalance) + shouldGT(t, poolOldQuxBalance, poolNewQuxBalance) + + shouldLTE(t, userOldFooBalance-userNewFooBalance, swapAmount) +} + +func TestBestSwapExactOutSinglePath(t *testing.T) { + std.TestSetOrigCaller(tr01) + + userOldFooBalance := foo.BalanceOf(a2u(tr01)) + userOldQuxBalance := qux.BalanceOf(a2u(tr01)) + poolOldFooBalance := foo.BalanceOf(a2u(poolAddr)) + poolOldQuxBalance := qux.BalanceOf(a2u(poolAddr)) + + swapAmount := 987_654 + BestSwap( + fooPath, // inputToken + quxPath, // outputToken + "EXACT_OUT", // swapType + bigint(swapAmount), // amountSpecified + 0, // sqrtPriceLimitX96 + ) + + userNewFooBalance := foo.BalanceOf(a2u(tr01)) + userNewQuxBalance := qux.BalanceOf(a2u(tr01)) + poolNewFooBalance := foo.BalanceOf(a2u(poolAddr)) + poolNewQuxBalance := qux.BalanceOf(a2u(poolAddr)) + + shouldGT(t, userOldFooBalance, userNewFooBalance) + shouldGT(t, userNewQuxBalance, userOldQuxBalance) + + shouldGT(t, poolNewFooBalance, poolOldFooBalance) + shouldGT(t, poolOldQuxBalance, poolNewQuxBalance) + + shouldLTE(t, userNewQuxBalance-userOldQuxBalance, -swapAmount) +} + +/* HELPER */ +func shouldEQ(t *testing.T, got, expected interface{}) { + if got != expected { + t.Errorf("got %v, expected %v", got, expected) + } +} + +func shouldNEQ(t *testing.T, got, expected interface{}) { + if got == expected { + t.Errorf("got %v, didn't expected %v", got, expected) + } +} + +func shouldGT(t *testing.T, l, r interface{}) { + if !(l > r) { + t.Errorf("expected %v > %v", l, r) + } +} + +func shouldGTE(t *testing.T, l, r interface{}) { + if !(l >= r) { + t.Errorf("expected %v > %v", l, r) + } +} + +func shouldLT(t *testing.T, l, r interface{}) { + if !(l < r) { + t.Errorf("expected %v < %v", l, r) + } +} + +func shouldLTE(t *testing.T, l, r interface{}) { + if !(l <= r) { + t.Errorf("expected %v < %v", l, r) + } +} diff --git a/router/consts.gno b/router/consts.gno index 6d894993..c1f0e57e 100644 --- a/router/consts.gno +++ b/router/consts.gno @@ -1,47 +1,13 @@ package router -import ( - "std" -) +import "std" const ( - zeroAddress = std.Address("") - - // some numbers - // calculated by https://mathiasbynens.be/demo/integer-range - MIN_INT8 bigint = -128 - MAX_INT8 bigint = 127 - MAX_UINT8 bigint = 255 - - MIN_INT16 bigint = -32768 - MAX_INT16 bigint = 32767 - MAX_UINT16 bigint = 65535 - - MIN_INT32 bigint = -2147483648 - MAX_INT32 bigint = 2147483647 - MAX_UINT32 bigint = 4294967295 + MIN_PRICE bigint = 4295128740 // MIN_SQRT_RATIO + 1 + MAX_PRICE bigint = 1461446703485210103287273052203988822378723970341 // MAX_SQRT_RATIO - 1 - MIN_INT64 bigint = -9223372036854775808 - MAX_INT64 bigint = 9223372036854775807 - MAX_UINT64 bigint = 18446744073709551615 - - MIN_INT128 bigint = -170141183460469231731687303715884105728 - MAX_INT128 bigint = 170141183460469231731687303715884105727 - MAX_UINT128 bigint = 340282366920938463463374607431768211455 - - MIN_INT256 bigint = -57896044618658097711785492504343953926634992332820282019728792003956564819968 - MAX_INT256 bigint = 57896044618658097711785492504343953926634992332820282019728792003956564819967 - MAX_UINT256 bigint = 115792089237316195423570985008687907853269984665640564039457584007913129639935 - - // ETC Q96 bigint = 79228162514264337593543950336 // 2 ** 96 - // Extra - MIN_INT24 bigint = -8388608 - MAX_INT24 bigint = 8388607 - MAX_UINT24 bigint = 16777215 - - MIN_INT96 bigint = -39614081257132168796771975168 - MAX_INT96 bigint = 39614081257132168796771975167 - MAX_UINT96 bigint = 79228162514264337593543950335 + ADDR_POOL std.Address = std.DerivePkgAddr("gno.land/r/pool") + ADDR_ROUTER std.Address = std.DerivePkgAddr("gno.land/r/router") ) diff --git a/router/find_path.gno b/router/find_path.gno new file mode 100644 index 00000000..2fd7fa45 --- /dev/null +++ b/router/find_path.gno @@ -0,0 +1,98 @@ +package router + +import ( + "strconv" + "strings" + + p "gno.land/r/pool" + + "gno.land/p/demo/ufmt" +) + +func findSwapPaths( + inputTokenPath string, + outputTokenPath string, +) (swapPaths SwapPaths) { + tokenPairs := TokenPairs{} + poolList := p.GetPoolList() + + for i, poolPath := range poolList { + token0Path, token1Path, pFee := poolPathWithFeeDivide(poolPath) + + { + k := token0Path + v := token1Path + ":" + strconv.Itoa(pFee) + + tokenPairs[k] = append(tokenPairs[k], v) + } + + { + k := token1Path + v := token0Path + ":" + strconv.Itoa(pFee) + + tokenPairs[k] = append(tokenPairs[k], v) + } + } + + swapPaths = getSwapPaths(tokenPairs, inputTokenPath, outputTokenPath) + return swapPaths +} + +func getSwapPaths( + tokenPairs TokenPairs, + inputTokenPath string, + outputTokenPath string, +) (swapPaths SwapPaths) { + swapPaths = make(SwapPaths, 0) + + // check if there is path that starts with input + require(len(tokenPairs[inputTokenPath]) != 0, ufmt.Sprintf("[ROUTER] find_path.gno__getSwapPaths() || len(tokenPairs[inputTokenPath]) == 0, inputTokenPath: %s", inputTokenPath)) + + // find direct path + for _, output := range tokenPairs[inputTokenPath] { + if strings.HasPrefix(output, outputTokenPath) { + outputPath, outputFee := singlePoolPathWithFeeDivide(output) + directPath := inputTokenPath + "," + outputFee + "," + outputPath + swapPaths[len(swapPaths)] = directPath // swapPaths = append(swapPaths, directPath) + + tokenPairs[inputTokenPath] = removeItemFromStringArray(tokenPairs[inputTokenPath], output) + } + } + + firstToken := "" + findPath(tokenPairs, inputTokenPath, outputTokenPath, "", 2, &swapPaths, &firstToken) + findPath(tokenPairs, inputTokenPath, outputTokenPath, "", 3, &swapPaths, &firstToken) + + return swapPaths +} + +func findPath( + tokenPairs TokenPairs, + currentTokenPath string, + outputTokenPath string, + currentPath string, + remainingHops int, + swapPaths *SwapPaths, + firstToken *string, +) { + if *firstToken == "" { + *firstToken = currentTokenPath + } + + if remainingHops == 0 { + if strings.HasPrefix(currentTokenPath, outputTokenPath) { + swapPaths[len(*swapPaths)] = (*firstToken + "," + currentPath) + } + return + } + + for _, next := range tokenPairs[currentTokenPath] { + nextPath, nextFee := singlePoolPathWithFeeDivide(next) + newPath := currentPath + if currentPath != "" { + newPath += "," + } + newPath += nextFee + "," + nextPath + findPath(tokenPairs, nextPath, outputTokenPath, newPath, remainingHops-1, swapPaths, firstToken) + } +} diff --git a/router/getter_api.gno b/router/getter_api.gno new file mode 100644 index 00000000..4c23ef67 --- /dev/null +++ b/router/getter_api.gno @@ -0,0 +1,177 @@ +// EXTERNAL API +package router + +import ( + "encoding/json" + "strings" + + p "gno.land/r/pool" + + "gno.land/p/demo/ufmt" +) + +const BASE_TOKEN = "gno.land/r/foo" + +type ApiQueryBase struct { + Height int64 `json:"height"` + Timestamp int64 `json:"timestamp"` +} + +type ResponseGetRatiosFromBase struct { + Stat ApiQueryBase `json:"stat"` + Response struct { + Data []map[string]bigint `json:"data"` + } `json:"response"` +} + +func ApiGetRatiosFromBase() string { + qb := ApiQueryBase{ + Height: GetHeight(), + Timestamp: GetTimestamp(), + } + + ratios := getRatiosFromBase() + r := ResponseGetRatiosFromBase{ + Stat: qb, + Response: struct { + Data []map[string]bigint `json:"data"` + }{ + Data: ratios, + }, + } + + rr, err := json.Marshal(r) + if err != nil { + panic(ufmt.Sprintf("[ROUTER] getter_api.gno()__ApiGetRatioFromBase || json.Marshal error with %v", err)) + } + + return string(rr) +} + +func getRatiosFromBase() []map[string]bigint { + tokenPrice := make(map[string]bigint, 0) + + // BASE + tokenPrice[BASE_TOKEN] = Q96 // ~= 1 + + // ELSE + tokenList := getTokenList() + for _, token := range tokenList { + if token != BASE_TOKEN { + swapPaths := findSwapPaths(token, BASE_TOKEN) + numSwapPaths := len(swapPaths) + + thisTokenPriceX96 := bigint(0) + if numSwapPaths < 1 { // NO CONNECTION TO BASE + tokenPrice[token] = 0 + } else { + for _, swapPath := range swapPaths { + numPools := strings.Count(swapPath, ",") / 2 + + switch numPools { + case 0: + thisTokenPriceX96 = 0 + case 1: + priceRatio := calculateTokenPrice(token, swapPath, numPools, 0, 1) + thisTokenPriceX96 += priceRatio + case 2: + priceRatio := calculateTokenPrice(token, swapPath, numPools, 0, 1) + thisTokenPriceX96 += priceRatio + case 3: + priceRatio := calculateTokenPrice(token, swapPath, numPools, 0, 1) + thisTokenPriceX96 += priceRatio + default: + thisTokenPriceX96 = 0 + } + } + avgPriceX96 := thisTokenPriceX96 / bigint(numSwapPaths) + tokenPrice[token] = avgPriceX96 + } + } + // TOKEN ENDS + } + + var tokenPrices []map[string]bigint + for token, price := range tokenPrice { + tokenPrices = append(tokenPrices, map[string]bigint{token: price}) + // DEBUG + // println("token:", token) + // println("price:", price) + // println() + } + + return tokenPrices +} + +func getTokenList() []string { + seen := make(map[string]bool) + uniqueTokenList := []string{} + poolList := p.GetPoolList() + + for _, poolPath := range poolList { + token0Path, token1Path, _ := poolPathWithFeeDivide(poolPath) + if _, exists := seen[token0Path]; !exists { + seen[token0Path] = true + uniqueTokenList = append(uniqueTokenList, token0Path) + } + if _, exists := seen[token1Path]; !exists { + seen[token1Path] = true + uniqueTokenList = append(uniqueTokenList, token1Path) + } + } + + return uniqueTokenList +} + +func makePoolPath(poolPath string, poolIndex int) string { + poolDatas := strings.Split(poolPath, ",") + // Calculate the indices for token paths and fee based on poolIndex. + baseIndex := poolIndex * 2 + if baseIndex+2 >= len(poolDatas) { + panic(ufmt.Sprintf("[ROUTER] getter_api.gno__makePoolPath() || index out of range for pool index: %d", poolIndex)) + } + + token0Path := poolDatas[baseIndex] + token1Path := poolDatas[baseIndex+2] + fee := poolDatas[baseIndex+1] + + // Ensure the tokens are in a consistent order. + if token0Path > token1Path { + token0Path, token1Path = token1Path, token0Path + } + + return token0Path + ":" + token1Path + ":" + fee +} + +func calculateTokenPrice(token, swapPath string, numPools, proceed int, currentPrice bigint) bigint { + currentPoolPathKey := makePoolPath(swapPath, proceed) + currentPool := p.GetPoolFromPoolKey(currentPoolPathKey) + + currentToken0 := currentPool.GetToken0Path() + currentToken1 := currentPool.GetToken1Path() + currentSqrtPriceX96 := currentPool.GetSlotSqrtPriceX96() + + if currentToken0 == token { + currentPrice *= (Q96 * Q96 / (currentSqrtPriceX96 * currentSqrtPriceX96 / Q96)) + token = currentToken1 + } else if currentToken1 == token { + currentPrice *= (currentSqrtPriceX96 * currentSqrtPriceX96 / Q96) + token = currentToken0 + } else { + panic("[ROUTER] getter_api.gno__calculateTokenPrice() || wrong condition") + } + + if proceed == numPools-1 { + return currentPrice / sqrt(Q96, proceed) + } + + return calculateTokenPrice(token, swapPath, numPools, proceed+1, currentPrice) +} + +func sqrt(x bigint, n int) bigint { + result := bigint(1) + for i := 0; i < n; i++ { + result *= x + } + return result +} diff --git a/router/gno.mod b/router/gno.mod new file mode 100644 index 00000000..3a953446 --- /dev/null +++ b/router/gno.mod @@ -0,0 +1 @@ +module gno.land/r/router \ No newline at end of file diff --git a/router/gno_helper.gno b/router/gno_helper.gno new file mode 100644 index 00000000..e3f24bde --- /dev/null +++ b/router/gno_helper.gno @@ -0,0 +1,14 @@ +package router + +import ( + "std" + "time" +) + +func GetHeight() int64 { + return std.GetHeight() +} + +func GetTimestamp() int64 { + return time.Now().Unix() +} diff --git a/router/quotation.gno b/router/quotation.gno new file mode 100644 index 00000000..f3b59101 --- /dev/null +++ b/router/quotation.gno @@ -0,0 +1,184 @@ +package router + +import ( + "sort" + "std" + "strings" + + "gno.land/p/demo/ufmt" +) + +func quoteForAllPath( + inputTokenPath string, + outputTokenPath string, + + amountSpecified bigint, + sqrtPriceLimitX96 bigint, +) (quoterTargets []QuoterTarget) { + swapPaths := findSwapPaths(inputTokenPath, outputTokenPath) + swapPcts := calculatePercentages(amountSpecified) // 5% - 100, 10% - 200, ...., 100% - 2000 + + for _, swapPath := range swapPaths { + for pct, pctAmount := range swapPcts { + quoterTarget := QuoterTarget{ + pct: pct, + pctAmount: pctAmount, + targetPath: swapPath, + resultRatioX96: bigint(0), // will update later + } + quoterTargets = append(quoterTargets, quoterTarget) + } + } + + // DrySwap to calculate + for i, quoterTarget := range quoterTargets { + numPools := strings.Count(quoterTarget.targetPath, ",") / 2 + require(numPools >= 1 && numPools <= 3, ufmt.Sprintf("[ROUTER] quotation.gno__quoteForAllPath() || numPools should 1 ~ 3, but found %d", numPools)) + + if numPools == 1 { + input, output, fee := getSwapData(quoterTarget.targetPath, 0) + + singleParams := SingleSwapParams{ + tokenIn: input, + tokenOut: output, + fee: fee, + amountSpecified: quoterTarget.pctAmount, + sqrtPriceLimitX96: sqrtPriceLimitX96, + } + + estimatedResult := singleSwapDry(singleParams) + if estimatedResult > 0 { + if amountSpecified > 0 { + resultRatioX96 := estimatedResult * Q96 / quoterTarget.pctAmount * Q96 / Q96 + quoterTarget.resultRatioX96 = resultRatioX96 + quoterTargets[i] = quoterTarget + } else { + resultRatioX96 := -quoterTarget.pctAmount * Q96 / estimatedResult * Q96 / Q96 + quoterTarget.resultRatioX96 = resultRatioX96 + quoterTargets[i] = quoterTarget + } + } else { + panic("[ROUTER] quotation.gno__quoteForAllPath() || SINGLE__estimateResult < 0") + } + } + + if numPools > 1 && numPools <= 3 { + input, output, fee := getSwapData(quoterTarget.targetPath, 0) + + swapParams := SwapParams{ + tokenIn: input, + tokenOut: output, + fee: fee, + + recipient: std.GetOrigCaller(), + amountSpecified: quoterTarget.pctAmount, + minAmountOut: 1, // r3v4_xx: sqrtPriceLimitX96 + } + + // if amountSpecified > 0, proceed forward + // if not, proceed with backward + if amountSpecified > 0 { + estimatedResult := multiSwapDry(swapParams, 0, numPools, quoterTarget.targetPath) // will iterate here to cover multi pools + if estimatedResult > 0 { + resultRatioX96 := estimatedResult * Q96 / quoterTarget.pctAmount + quoterTarget.resultRatioX96 = resultRatioX96 + quoterTargets[i] = quoterTarget + } else { + panic("[ROUTER] quotation.gno__quoteForAllPath() || MULTI__PositiveAmountSpecified__estimateResult < 0") + } + } else { + estimatedResult := multiSwapNegativeDry(swapParams, numPools-1, quoterTarget.targetPath) // will iterate here to cover multi pools + if estimatedResult > 0 { + resultRatioX96 := -quoterTarget.pctAmount * Q96 / estimatedResult * Q96 / Q96 + + quoterTarget.resultRatioX96 = resultRatioX96 + quoterTargets[i] = quoterTarget + } else { + panic("[ROUTER] quotation.gno__quoteForAllPath() || MULTI__NegativeAmountSpecified__estimateResult < 0") + } + } + + } + } + + // it contains outputRatio with 0 which is impossible path + // > remove path with 0 ratio + var finalTargets []QuoterTarget + for _, quote := range quoterTargets { + if quote.resultRatioX96 != 0 { + finalTargets = append(finalTargets, quote) + } + } + + sort.Sort(ByOutputRatioDesc(finalTargets)) + return finalTargets +} + +func calculatePercentages(amount bigint) map[int]bigint { // map[pct][value] + percentages := make(map[int]bigint) + + for i := 5; i <= 100; i += 5 { + percentage := (bigint(i) * amount) / 100 + percentages[i] = percentage + } + + return percentages +} + +func findBestPaths(quotes []QuoterTarget) (bestSwaps []QuoterTarget) { + bestSwaps = make([]QuoterTarget, 0) + + totalPct := 100 + for _, quote := range quotes { + quotePct := quote.pct + resultRatioX96 := quote.resultRatioX96 + + totalPct -= quotePct + + bestSwaps = append(bestSwaps, quote) + + if totalPct <= 0 { + break + } + } + + return bestSwaps +} + +func removeDuplication(bestSwaps []QuoterTarget) (finalSwaps []QuoterTarget) { + finalSwaps = make([]QuoterTarget, 0) // return + insertedPath := []string{} // tmp + pctToSwap := 0 + for _, bestSwap := range bestSwaps { + pctToSwap += bestSwap.pct + if !containsString(insertedPath, bestSwap.targetPath) { + insertedPath = append(insertedPath, bestSwap.targetPath) + finalSwaps = append(finalSwaps, bestSwap) + } else { + for i, finalSwap := range finalSwaps { + if finalSwap.targetPath == bestSwap.targetPath { + finalSwap.pctAmount += bestSwap.pctAmount // can be exceed + + if pctToSwap > 100 { + finalSwap.pct += (bestSwap.pct - (pctToSwap - 100)) + } else { + finalSwap.pct += bestSwap.pct + } + + finalSwaps[i] = finalSwap + } + } + } + } + + return finalSwaps +} + +func containsString(slice []string, target string) bool { + for _, s := range slice { + if s == target { + return true + } + } + return false +} diff --git a/router/router.gno b/router/router.gno new file mode 100644 index 00000000..185c24dc --- /dev/null +++ b/router/router.gno @@ -0,0 +1,94 @@ +package router + +import ( + "std" + "strings" + + "gno.land/p/demo/ufmt" +) + +func BestSwap( + inputToken string, + outputToken string, + swapType string, + amountSpecified bigint, + sqrtPriceLimitX96 bigint, +) { + switch swapType { + case "EXACT_IN": + amountSpecified = amountSpecified + case "EXACT_OUT": + amountSpecified = -amountSpecified + default: + panic("UNKNOWN TYPE") + } + + // get quotes + quotes := quoteForAllPath( // sorted by ratio DESC + inputToken, + outputToken, + amountSpecified, + sqrtPriceLimitX96, + ) + require(len(quotes) != 0, ufmt.Sprintf("[ROUTER] router.gno__BestSwap() || len(quotes) == 0, inputToken:%s, outputToken:%s", inputToken, outputToken)) + + bestSwaps := findBestPaths(quotes) + require(len(bestSwaps) != 0, "[ROUTER] router.gno__BestSwap() || len(bestSwaps) == 0") + + finalSwaps := removeDuplication(bestSwaps) + require(len(finalSwaps) != 0, "[ROUTER] router.gno__BestSwap() || len(finalSwaps) == 0") + + remainingAmount := amountSpecified + + for i, finalSwap := range finalSwaps { + numPools := strings.Count(finalSwap.targetPath, ",") / 2 + require(numPools >= 1 && numPools < 3, ufmt.Sprintf("[ROUTER] router.gno__BestSwap() || numPools should 1 ~ 3, but found %d", numPools)) + + // SINGLE + if numPools == 1 { + toSwap := bigint(0) + + // isFinal + if i == len(finalSwaps)-1 { // last swap routes + toSwap = remainingAmount + } else { + remainingAmount -= finalSwap.pctAmount + toSwap = finalSwap.pctAmount + } + + input, output, fee := getSwapData(finalSwap.targetPath, 0) + singleParams := SingleSwapParams{ + tokenIn: input, + tokenOut: output, + fee: fee, + amountSpecified: toSwap, + sqrtPriceLimitX96: sqrtPriceLimitX96, + } + amountOut := singleSwap(singleParams) + } + + // MULTI + if numPools >= 2 && numPools <= 3 { + toSwap := bigint(0) + + // isFinal + if i == len(finalSwaps)-1 { // last swap routes + toSwap = remainingAmount + } else { + remainingAmount -= finalSwap.pctAmount + toSwap = finalSwap.pctAmount + } + + input, output, fee := getSwapData(finalSwap.targetPath, 0) + swapParams := SwapParams{ + tokenIn: input, + tokenOut: output, + fee: fee, + recipient: std.GetOrigCaller(), + amountSpecified: toSwap, + minAmountOut: 1, + } + amountOut := multiSwap(swapParams, 0, numPools, finalSwap.targetPath) // iterate here + } + } +} diff --git a/router/router_register.gno b/router/router_register.gno new file mode 100644 index 00000000..f7650b6d --- /dev/null +++ b/router/router_register.gno @@ -0,0 +1,119 @@ +package router + +import ( + "std" + + "gno.land/r/demo/users" +) + +const APPROVED_CALLER = "g12l9splsyngcgefrwa52x5a7scc29e9v086m6p4" // gsa + +var registered = []GRC20Pair{} + +type GRC20Interface interface { + Transfer() func(to users.AddressOrName, amount uint64) + TransferFrom() func(from, to users.AddressOrName, amount uint64) + BalanceOf() func(owner users.AddressOrName) uint64 + Approve() func(spender users.AddressOrName, amount uint64) +} + +type GRC20Pair struct { + pkgPath string + igrc20 GRC20Interface +} + +func findGRC20(pkgPath string) (int, bool) { + for i, pair := range registered { + if pair.pkgPath == pkgPath { + return i, true + } + } + + return -1, false +} + +func appendGRC20Interface(pkgPath string, igrc20 GRC20Interface) { + registered = append(registered, GRC20Pair{pkgPath: pkgPath, igrc20: igrc20}) +} + +func removeGRC20Interface(pkgPath string) { + i, found := findGRC20(pkgPath) + if !found { + return + } + + registered = append(registered[:i], registered[i+1:]...) +} + +func RegisterGRC20Interface(pkgPath string, igrc20 GRC20Interface) { + // only admin can register + // r3v4_xx: below logic can't be used in test case + // r3v4_xx: however must be used in production + + // caller := std.GetOrigCaller() + // if caller != APPROVED_CALLER { + // panic("unauthorized address to register") + // } + + _, found := findGRC20(pkgPath) + if !found { + appendGRC20Interface(pkgPath, igrc20) + } +} + +func UnregisterGRC20Interface(pkgPath string) { + // do not allow realm to unregister + std.AssertOriginCall() + + // only admin can unregister + caller := std.GetOrigCaller() + require(caller == APPROVED_CALLER, "[ROUTER] router_register.gno__UnregisterGRC20Interface() || nauthorized address to unregister") + + _, found := findGRC20(pkgPath) + if found { + removeGRC20Interface(pkgPath) + } +} + +func transferByRegisterCall(pkgPath string, to std.Address, amount uint64) bool { + i, found := findGRC20(pkgPath) + if !found { + return false + } + + registered[i].igrc20.Transfer()(users.AddressOrName(to), amount) + + return true +} + +func transferFromByRegisterCall(pkgPath string, from, to std.Address, amount uint64) bool { + i, found := findGRC20(pkgPath) + if !found { + return false + } + + registered[i].igrc20.TransferFrom()(users.AddressOrName(from), users.AddressOrName(to), amount) + + return true +} + +func balanceOfByRegisterCall(pkgPath string, owner std.Address) uint64 { + i, found := findGRC20(pkgPath) + if !found { + return 0 + } + + balance := registered[i].igrc20.BalanceOf()(users.AddressOrName(owner)) + return balance +} + +func approveByRegisterCall(pkgPath string, spender std.Address, amount uint64) bool { + i, found := findGRC20(pkgPath) + if !found { + return false + } + + registered[i].igrc20.Approve()(users.AddressOrName(spender), amount) + + return true +} diff --git a/router/rpc_call.gno b/router/rpc_call.gno new file mode 100644 index 00000000..47378d1e --- /dev/null +++ b/router/rpc_call.gno @@ -0,0 +1,177 @@ +package router + +import ( + "encoding/json" + "std" + "strings" + + "gno.land/p/demo/ufmt" +) + +func BestSwapDry( + inputToken string, + outputToken string, + swapType string, + amountSpecified bigint, + sqrtPriceLimitX96 bigint, +) string { + + switch swapType { + case "EXACT_IN": + amountSpecified = amountSpecified + case "EXACT_OUT": + amountSpecified = -amountSpecified + default: + panic("UNKNOWN TYPE") + } + + // get quotes + quotes := quoteForAllPath( // sored by Ratio DESC + inputToken, + outputToken, + amountSpecified, + sqrtPriceLimitX96, + ) + require(len(quotes) > 0, ufmt.Sprintf("[ROUTER] rpc_call.gno__gno__BestSwapDry() || len(quotes) == 0, inputToken:%s, outputToken:%s", inputToken, outputToken)) + + bestSwaps := findBestPaths(quotes) + require(len(bestSwaps) > 0, "[ROUTER] rpc_call.gno__BestSwapDry() || len(bestSwaps) == 0") + + finalSwaps := removeDuplication(bestSwaps) + require(len(finalSwaps) > 0, "[ROUTER] rpc_call.gno__BestSwapDry() || len(finalSwaps) == 0") + + rpcReturnBestSwap := RpcReturnBestSwap{} + rpcReturnBestSwap.SwapType = swapType + rpcReturnBestSwap.TotalRoutes = len(finalSwaps) + + rpcRoutes := []RpcRoute{} + + remainingAmount := amountSpecified + outputEstimated := bigint(0) + for i, finalSwap := range finalSwaps { + numPools := strings.Count(finalSwap.targetPath, ",") / 2 + splitPaths := multiTargetPathToList(finalSwap.targetPath) + + rpcRoute := RpcRoute{ + SwapPct: finalSwap.pct, + NumPools: numPools, + SwapPaths: splitPaths, + } + rpcRoutes = append(rpcRoutes, rpcRoute) + + rpcReturnBestSwap.TotalPct += finalSwap.pct + + if rpcReturnBestSwap.TotalPct == 100 { + rpcReturnBestSwap.Possible = true + } + + // ESTIMATE Amount + // SINGLE + if numPools == 1 { + toSwap := bigint(0) + + // isFinal + if i == len(finalSwaps)-1 { // last swap routes + toSwap = remainingAmount + } else { + remainingAmount -= finalSwap.pctAmount + toSwap = finalSwap.pctAmount + } + + input, output, fee := getSwapData(finalSwap.targetPath, 0) + singleParams := SingleSwapParams{ + tokenIn: input, + tokenOut: output, + fee: fee, + amountSpecified: toSwap, + sqrtPriceLimitX96: sqrtPriceLimitX96, + } + amountOut := singleSwap(singleParams) + outputEstimated += amountOut + } + + // MULTI + if numPools > 1 && numPools <= 3 { + toSwap := bigint(0) + + // isFinal + if i == len(finalSwaps)-1 { // last swap routes + toSwap = remainingAmount + } else { + remainingAmount -= finalSwap.pctAmount + toSwap = finalSwap.pctAmount + } + + input, output, fee := getSwapData(finalSwap.targetPath, 0) + swapParams := SwapParams{ + tokenIn: input, + tokenOut: output, + fee: fee, + recipient: std.GetOrigCaller(), + amountSpecified: toSwap, + minAmountOut: 1, + } + amountOut := multiSwap(swapParams, 0, numPools, finalSwap.targetPath) // iterate here + outputEstimated += amountOut + } + } + rpcReturnBestSwap.RpcRoutes = rpcRoutes + + if rpcReturnBestSwap.Possible == true { + rpcReturnBestSwap.AmountResult = outputEstimated + + if rpcReturnBestSwap.SwapType == "EXACT_IN" { + rpcReturnBestSwap.AmountSpecified = amountSpecified + } else { + rpcReturnBestSwap.AmountSpecified = -amountSpecified + } + } + + rr, err := json.Marshal(rpcReturnBestSwap) + if err != nil { + panic(ufmt.Sprintf("[ROUTER] rpc_call.gno__BestSwapDry() || json.Marshal error with %v", err)) + } + + return string(rr) +} + +type RpcRoute struct { + SwapPct int `json:"swapPct"` + NumPools int `json:"numPools"` + SwapPaths []string `json:"swapPaths"` +} + +type RpcReturnBestSwap struct { + TotalRoutes int `json:"totalRoutes"` + TotalPct int `json:"totalPct"` + Possible bool `json:"possible"` + SwapType string `json:"swapType"` + AmountSpecified bigint `json:"amountSpecified"` + AmountResult bigint `json:"amountResult"` + RpcRoutes []RpcRoute `json:"rpcRoutes"` +} + +func multiTargetPathToList(path string) []string { + numPools := strings.Count(path, ",") / 2 + splitData := strings.Split(path, ",") + + fullPaths := []string{} + for i := 0; i < numPools; i++ { + j := 0 + if i == 0 { + j = i + } else { + j = i * 2 + } + + token0Path := splitData[j] // 0 > 2 > 4 + fee := splitData[j+1] // 1 > 3 > 5 + token1Path := splitData[j+2] // 2 > 4 > 6 + + fullPath := token0Path + ":" + token1Path + ":" + fee + + fullPaths = append(fullPaths, fullPath) + } + + return fullPaths +} diff --git a/router/swap_inner.gno b/router/swap_inner.gno new file mode 100644 index 00000000..12a58f94 --- /dev/null +++ b/router/swap_inner.gno @@ -0,0 +1,126 @@ +package router + +import ( + "std" + + p "gno.land/r/pool" +) + +func _swap( + amountSpecified bigint, + recipient std.Address, + sqrtPriceLimitX96 bigint, + data SwapCallbackData, +) (amountResult bigint) { + // prepare + zeroForOne := data.tokenIn < data.tokenOut + + if sqrtPriceLimitX96 == 0 { + if zeroForOne { + sqrtPriceLimitX96 = MIN_PRICE + } else { + sqrtPriceLimitX96 = MAX_PRICE + } + } + + // dry swap -> esteimate amount -> approve exact amount + approveAmount0, approveAmount1, _ := p.DrySwap( + data.tokenIn, + data.tokenOut, + data.fee, + + recipient, + zeroForOne, + amountSpecified, + sqrtPriceLimitX96, + ) + + // ROUTER approves POOL as spender + approveByRegisterCall(data.tokenIn, ADDR_POOL, abs(approveAmount0)) + approveByRegisterCall(data.tokenOut, ADDR_POOL, abs(approveAmount1)) + + amount0, amount1 := p.Swap( + data.tokenIn, + data.tokenOut, + data.fee, + + recipient, + zeroForOne, + amountSpecified, + sqrtPriceLimitX96, + + data.payer, + ) + + if amountSpecified > 0 { + if zeroForOne { // return amount, user recvs ~= pool sends + amountResult = -amount1 + } else { + amountResult = -amount0 + } + } else { + if zeroForOne { // return amount, user sends ~= pool recvs + amountResult = amount0 + } else { + amountResult = amount1 + } + } + + return amountResult +} + +func _swapDry( + amountSpecified bigint, + recipient std.Address, + sqrtPriceLimitX96 bigint, + data SwapCallbackData, +) (amountResult bigint) { + zeroForOne := data.tokenIn < data.tokenOut + + if sqrtPriceLimitX96 == 0 { + if zeroForOne { + sqrtPriceLimitX96 = MIN_PRICE + } else { + sqrtPriceLimitX96 = MAX_PRICE + } + } + + // check possible + amount0, amount1, ok := p.DrySwap( + data.tokenIn, + data.tokenOut, + data.fee, + + recipient, + zeroForOne, + amountSpecified, + sqrtPriceLimitX96, + ) + if !ok { + return 0 + } + + if amountSpecified > 0 { + if zeroForOne { // return amount, user recvs ~= pool sends + amountResult = -amount1 + } else { + amountResult = -amount0 + } + } else { // return amount, user sends ~= pool recvs + if zeroForOne { + amountResult = amount0 + } else { + amountResult = amount1 + } + } + + return amountResult +} + +func abs(x bigint) uint64 { + if x < 0 { + return uint64(-x) + } + + return uint64(x) +} diff --git a/router/swap_multi.gno b/router/swap_multi.gno new file mode 100644 index 00000000..ae276f36 --- /dev/null +++ b/router/swap_multi.gno @@ -0,0 +1,150 @@ +package router + +import ( + "std" +) + +type SwapParams struct { + tokenIn string + tokenOut string + fee uint16 + + recipient std.Address + amountSpecified bigint + minAmountOut bigint +} + +func multiSwap(params SwapParams, currentPoolIndex, numPool int, swapPath string) (amountOut bigint) { + payer := std.GetOrigCaller() // user + + for { + var recipient std.Address + currentPoolIndex++ + + if currentPoolIndex < numPool { + recipient = std.DerivePkgAddr("gno.land/r/router") + } else { + recipient = params.recipient // user ~= std.GetOrigCaller() + } + + params.amountSpecified = _swap( + params.amountSpecified, // amountSpecified + recipient, // recipient + 0, // sqrtPriceLimitX96 + SwapCallbackData{ + params.tokenIn, // tokenIn + params.tokenOut, // tokenOut + params.fee, // fee + payer, // payer + }, + ) + + if currentPoolIndex < numPool { + payer = std.DerivePkgAddr("gno.land/r/router") + + nextInput, nextOutput, nextFee := getSwapData(swapPath, currentPoolIndex) + params.tokenIn = nextInput + params.tokenOut = nextOutput + params.fee = nextFee + } else { + amountOut = params.amountSpecified + return amountOut + } + } + + if amountOut < params.minAmountOut { + panic("Too few receive") + } +} + +func multiSwapDry(params SwapParams, currentPoolIndex, numPool int, swapPath string) (amountOut bigint) { + payer := std.GetOrigCaller() // user + + for { + var recipient std.Address + currentPoolIndex++ + + if currentPoolIndex < numPool { + recipient = std.DerivePkgAddr("gno.land/r/router") + } else { + recipient = params.recipient // user ~= std.GetOrigCaller() + } + + params.amountSpecified = _swapDry( + params.amountSpecified, // amountSpecified + recipient, // recipient + 0, // sqrtPriceLimitX96 + SwapCallbackData{ + params.tokenIn, // tokenIn + params.tokenOut, // tokenOut + params.fee, // fee + payer, // payer + }, + ) + + if currentPoolIndex < numPool { + payer = std.DerivePkgAddr("gno.land/r/router") + + nextInput, nextOutput, nextFee := getSwapData(swapPath, currentPoolIndex) + params.tokenIn = nextInput + params.tokenOut = nextOutput + params.fee = nextFee + } else { + amountOut = params.amountSpecified + return amountOut + } + } + + if amountOut < params.minAmountOut { + panic("Too few receive") + } +} + +func multiSwapNegativeDry(params SwapParams, currentPoolIndex int, swapPath string) (amountOut bigint) { + payer := std.DerivePkgAddr("gno.land/r/router") + + params.tokenIn, params.tokenOut, params.fee = getSwapData(swapPath, currentPoolIndex) + + numPools := currentPoolIndex + for { + var recipient std.Address + currentPoolIndex-- + + if currentPoolIndex == numPools-1 { + recipient = params.recipient + } else { + recipient = std.DerivePkgAddr("gno.land/r/router") + } + + params.amountSpecified = _swapDry( + params.amountSpecified, // amountSpecified + recipient, // recipient + 0, // sqrtPriceLimitX96 + SwapCallbackData{ + params.tokenIn, // tokenIn + params.tokenOut, // tokenOut + params.fee, // fee + payer, // payer + }, + ) + + if currentPoolIndex != -1 { + nextInput, nextOutput, nextFee := getSwapData(swapPath, currentPoolIndex) + params.tokenIn = nextInput + params.tokenOut = nextOutput + params.fee = nextFee + + // input amounts derived in the last stage must be used again as the n-1th output amounts + // >> must be negative + params.amountSpecified = -params.amountSpecified + + } else { + amountOut = params.amountSpecified + return amountOut + } + } + + if amountOut < params.minAmountOut { + panic("Too few receive") + } +} diff --git a/router/swap_router.gno b/router/swap_router.gno deleted file mode 100644 index 7fb1912d..00000000 --- a/router/swap_router.gno +++ /dev/null @@ -1,239 +0,0 @@ -package router - -import ( - "std" - "math" - - "gno.land/p/demo/grc/grc20" - - pool "gno.land/p/demo/pool" // XXX -) - -var ( - token0 *grc20.AdminToken - token1 *grc20.AdminToken - DEFAULT_AMOUNT_IN_CACHED = MAX_UINT32 // 32 or 64 ? - amountCached = DEFAULT_AMOUNT_IN_CACHED -) - -type ExactInputSingleParams struct { - TokenIn std.Address - TokenOut std.Address - Fee bigint - Recipient std.Address - Deadline bigint - AmountIn bigint - AmountOutMinimum bigint - SqrtPriceLimit bigint - ZeroForOne bool -} - -type ExactInputParams struct { - Recipient std.Address - Deadline bigint - AmountIn bigint - AmountOutMinimum bigint - ZeroForOne bool -} - -type ExactOutputSingleParams struct { - ZeroForOne bool - TokenIn std.Address - TokenOut std.Address - Fee bigint - Recipient std.Address - Deadline bigint - AmountOut bigint - AmountInMaximum bigint - SqrtPriceLimit bigint -} - -type ExactOutputParams struct { - ZeroForOne bool - Recipient std.Address - Deadline bigint - AmountOut bigint - AmountInMaximum bigint -} - -func Init() { - // testtoken1.Init() - // testtoken1.Faucet() - // testtoken2.Init() - // testtoken2.Faucet() - - token0 = _.GetGRC20() - token1 = _.GetGRC20() - - routerAddress := std.GetOrigPkgAddr() -} - - -func getPool( - tokenA std.Address, - tokenB std.Address, - fee bigint, -) std.Address { - /// GetPoolAddresss - return pool.GetPoolAddress(tokenA, tokenB, fee) -} - - -func exactInputInternal( - amountIn bigint, - recipient std.Address, - sqrtPriceLimit bigint, - poolAddress std.Address -) (amountOut bigint) { - if (recipient == ZeroAddress) { - recipient = routerAddress - } - - // XXX - tokenIn, tokenOut std.Address, fee bigint = poolAddress.DecodeFirstPool() - var zeroForOne bool = tokenIn < tokenOut - - // XXX - var amount0, amount1 bigint = getPool(tokenIn, tokenOut, fee) - - if sqrtPriceLimit == 0 { - if zeeroForOne { - sqrtPriceLimit = TickMath.MIN_SQRT_RATIO + 1 - } else { - sqrtPriceLimit = TickMath.MAX_SQRT_RATIO - 1 - } - } - - poolAddress.swap( - recipient, - zeroForOne, - bigint(amountIn), - sqrtPriceLimit - ) - - if zeroForOne { - return bigint(-amount1) - } else { - return bigint(-amount0) - } -} - - -func exactInputSingle(params ExactInputSingleParams) (amountOut bigint) { - checkDeadline(params.Deadline) - - amountOut = exactInputInternal(e, - params.amountIn, - params.recipient, - params.sqrtPriceLimit, - ) - require(amountOut >= params.AmountOutMinimum, "Too little received") - - return amountOut -} - - -func exactInput(params ExactInputParams) (amountOut bigint) { - checkDeadline(params.Deadline) - - payer := GetOrigCaller() - - - // XX chaining // eth path - params.AmountIn = exactInputInternal( - params.ZeroForOne, - params.AmountIn, - params.Recipient, // for intermediate swaps, this contract custodies - 0, - ) - - // decide whether to continue or terminate - amountOut = params.AmountIn - - require(amountOut >= params.AmountOutMinimum, "Too little received") - - return amountOut -} - - -func exactOutputInternal( - amountOut bigint, - recipient std.Address, - sqrtPriceLimit bigint, -) (amountIn bigint) { - // allow swapping to the router address with address 0 - if recipient == zeroAddress { - recipient = routerAddress - } - - if sqrtPriceLimit == 0 { - if zeroForOne { - sqrtPriceLimit = MIN_SQRT_RATIO + 1 - } else { - sqrtPriceLimit = MAX_SQRT_RATIO - 1 - } - } - amount0Delta, amount1Delta := pool.Swap( - std.GetOrigCaller(), - recipient, - zeroForOne, - - int (amountOut), - sqrtPriceLimit, - ) - - var amountOutReceived bigint - if zeroForOne { - amountIn = bigint (amount0Delta) - amountOutReceived = bigint (-amount1Delta) - } else { - amountIn = bigint (amount1Delta) - amountOutReceived = bigint (-amount0Delta) - } - - if sqrtPriceLimit == 0 { - require(amountOutReceived == amountOut, "amountOutReceived must equal amountOut") - } - - return amountIn -} - -func exactOutputSingle(params ExactOutputSingleParams) (amountIn uint) { - checkDeadline(params.Deadline) - amountIn = exactOutputInternal( - params.ZeroForOne, - params.AmountOut, - params.Recipient, - params.SqrtPriceLimit, - ) - - require(amountIn <= params.AmountInMaximum, "Too much requested") - amountInCached = DEFAULT_AMOUNT_IN_CACHED - return amountIn -} - -func exactOutput(params ExactOutputParams) (amountIn uint) { - checkDeadline(params.Deadline) - amountIn = exactOutputInternal( - params.ZeroForOne, - params.AmountOut, - params.Recipient, - 0, - ) - - amountIn = amountInCached - require(amountIn <= params.AmountInMaximum, "Too much requested") - amountInCached = DEFAULT_AMOUNT_IN_CACHED - return amountIn -} - - -// helpers -func require(ok bool, msg string) { - if !ok { - panic(msg) - } -} - -func checkDeadline(deadlint uint64) { - require(BlockTimestamp() <= deadline, "Expired") -} \ No newline at end of file diff --git a/router/swap_single.gno b/router/swap_single.gno new file mode 100644 index 00000000..31799241 --- /dev/null +++ b/router/swap_single.gno @@ -0,0 +1,43 @@ +package router + +import ( + "std" +) + +type SingleSwapParams struct { + tokenIn string + tokenOut string + fee uint16 + amountSpecified bigint + sqrtPriceLimitX96 bigint +} + +func singleSwap(params SingleSwapParams) (amountOut bigint) { + amountOut = _swap( + params.amountSpecified, + std.GetOrigCaller(), // if single swap => user will recieve + params.sqrtPriceLimitX96, + SwapCallbackData{ + params.tokenIn, + params.tokenOut, + params.fee, + std.PrevRealm().Addr(), // payer ==> msg.sender, + }, + ) + return amountOut +} + +func singleSwapDry(params SingleSwapParams) (amountOut bigint) { + amountOut = _swapDry( + params.amountSpecified, + std.GetOrigCaller(), // if single swap => user will recieve + params.sqrtPriceLimitX96, + SwapCallbackData{ + params.tokenIn, + params.tokenOut, + params.fee, + std.PrevRealm().Addr(), // payer ==> msg.sender, + }, + ) + return amountOut +} diff --git a/router/type.gno b/router/type.gno new file mode 100644 index 00000000..3fae9d05 --- /dev/null +++ b/router/type.gno @@ -0,0 +1,27 @@ +package router + +import "std" + +type SwapCallbackData struct { + tokenIn string + tokenOut string + fee uint16 + + payer std.Address +} + +type SwapPaths map[int]string +type TokenPairs map[string][]string + +type QuoterTarget struct { + pct int + pctAmount bigint + targetPath string + resultRatioX96 bigint +} + +type ByOutputRatioDesc []QuoterTarget + +func (a ByOutputRatioDesc) Len() int { return len(a) } +func (a ByOutputRatioDesc) Swap(i, j int) { a[i], a[j] = a[j], a[i] } +func (a ByOutputRatioDesc) Less(i, j int) bool { return a[i].resultRatioX96 > a[j].resultRatioX96 } diff --git a/router/util.gno b/router/util.gno new file mode 100644 index 00000000..a1a38434 --- /dev/null +++ b/router/util.gno @@ -0,0 +1,75 @@ +package router + +import ( + "std" + "strconv" + "strings" + + "gno.land/p/demo/ufmt" + "gno.land/r/demo/users" +) + +func removeItemFromStringArray(s []string, r string) []string { + for i, v := range s { + if v == r { + return append(s[:i], s[i+1:]...) + } + } + return s +} + +func poolPathWithFeeDivide(poolPath string) (string, string, int) { + poolPathSplit := strings.Split(poolPath, ":") + require(len(poolPathSplit) == 3, ufmt.Sprintf("[ROUTER] util.gno__poolPathWithFeeDivide() || len(poolPathSplit) != 3, poolPath: %s", poolPath)) + + feeInt, err := strconv.Atoi(poolPathSplit[2]) + if err != nil { + panic(ufmt.Sprintf("[ROUTER] util.gno__poolPathWithFeeDivide() || cannot convert fee(%s) to int", poolPathSplit[2])) + } + + return poolPathSplit[0], poolPathSplit[1], feeInt +} + +func singlePoolPathWithFeeDivide(poolPath string) (string, string) { + singlePoolPathSplit := strings.Split(poolPath, ":") + require(len(singlePoolPathSplit) == 2, ufmt.Sprintf("[ROUTER] util.gno__singlePoolPathWithFeeDivide || len(singlePoolPathSplit) != 2, poolPath: %s", poolPath)) + + return singlePoolPathSplit[0], singlePoolPathSplit[1] +} + +func getSwapData(path string, poolIdx int) (string, string, uint16) { + // inputToken, outputToken, fee + datas := strings.Split(path, ",") + + switch poolIdx { + case 0: + fee, _ := strconv.Atoi(datas[1]) + return datas[0], datas[2], uint16(fee) + case 1: + fee, _ := strconv.Atoi(datas[3]) + return datas[2], datas[4], uint16(fee) + case 2: + fee, _ := strconv.Atoi(datas[5]) + return datas[4], datas[6], uint16(fee) + default: + return "", "", 0 + } +} + +func sortItems(tokenAPath, tokenBPath, fee string) string { + if tokenAPath < tokenBPath { + return tokenAPath + ":" + tokenBPath + ":" + fee + } else { + return tokenBPath + ":" + tokenAPath + ":" + fee + } +} + +func a2u(addr std.Address) users.AddressOrName { + return users.AddressOrName(addr) +} + +func require(cond bool, msg string) { + if !cond { + panic(msg) + } +} diff --git a/staker/getter_api.gno b/staker/getter_api.gno index 738e10c5..b3c32f94 100644 --- a/staker/getter_api.gno +++ b/staker/getter_api.gno @@ -33,12 +33,12 @@ func ApiGetRewardByAddress(address std.Address) string { if deposit.owner == address { poolPath := deposit.targetPoolPath - // get internal gnos reward - internalGNOS := rewardMathComputeInternalRewardAmount(tokenId, deposit) + // get internal gns reward + internalGNS := rewardMathComputeInternalRewardAmount(tokenId, deposit) rewardList = append(rewardList, ApiRewardByAddress{ Type: "Internal", - Token: "GNOS", - Reward: internalGNOS, + Token: "GNS", + Reward: internalGNS, }) // find all external reward list for this pool diff --git a/staker/reward_math.gno b/staker/reward_math.gno index f6f9ae92..0e8e2488 100644 --- a/staker/reward_math.gno +++ b/staker/reward_math.gno @@ -1,24 +1,24 @@ package staker import ( - s "gno.land/r/position" - "gno.land/p/demo/ufmt" + + pos "gno.land/r/position" ) func rewardMathComputeInternalRewardAmount(tokenId uint64, deposit Deposit) uint64 { - // r3v4_xxx: calculate amount of `GNOS` to be minted by every block + // r3v4_xxx: calculate amount of `GNS` to be minted by every block // 1. get block creation time (5s for now) - // 2. calculate amount of `GNOS` to be minted by every block (10_000 for now) - // 3. above `GNOS amount`` is supposed to be minted to a separate wallet specified by onbloc - // 4. this logic is supposed to be included in GNOS token contract, not staker contract + // 2. calculate amount of `GNS` to be minted by every block (10_000 for now) + // 3. above `GNS amount`` is supposed to be minted to a separate wallet specified by onbloc + // 4. this logic is supposed to be included in GNS token contract, not staker contract blockRewardInit := 10_000 - // because of `~ 10 days` staking duration, certain block GNOS amount won't be distribute 100% as reward) - blockRewardLeft := blockRewardInit / 10 // gnos.BalanceOf(INTERNAL_REWARD_ACCOUNT) - blockRewardInit + // because of `~ 10 days` staking duration, certain block GNS amount won't be distribute 100% as reward) + blockRewardLeft := blockRewardInit / 10 // gns.BalanceOf(INTERNAL_REWARD_ACCOUNT) - blockRewardInit blockReward := blockRewardInit + blockRewardLeft // get pool tier and ratio - poolPath := s.PositionGetPositionPoolKey(tokenId) + poolPath := pos.PositionGetPositionPoolKey(tokenId) _, poolRatioX96 := getPoolTierAndRatio(poolPath) // get pool reward per block @@ -66,7 +66,7 @@ func rewardMathComputeExternalRewardAmount(tokenId uint64, deposit Deposit, ince // 1 second reward == total reward amount / reward duration monthlyReward = incentive.rewardAmount / uint64(incentiveDuration) * uint64(TIMESTAMP_30DAYS) default: - panic(ufmt.Sprintf("[STAKER] reward_math.gno || incentiveDuration(%s) at least 30 days", incentiveDuration)) + panic(ufmt.Sprintf("[STAKER] reward_math.gno || incentiveDuration(%d) at least 30 days", incentiveDuration)) } // calculate reward amount per block @@ -112,7 +112,7 @@ func getPoolTotalStakedLiquidity(poolPath string) bigint { // get all staked liquidity for tokenId, deposit := range deposits { // key is tokenId // used in this range loop only if deposit.targetPoolPath == poolPath { - tokenLiquidity := s.PositionGetPositionLiquidity(tokenId) + tokenLiquidity := pos.PositionGetPositionLiquidity(tokenId) poolStakedLiquidity += tokenLiquidity } } @@ -124,7 +124,7 @@ func getMyLiquidityRatio(poolPath string, tokenId uint64) bigint { poolStakedLiquidity := getPoolTotalStakedLiquidity(poolPath) // my(current tokenId) liquidity - myLiquidity := s.PositionGetPositionLiquidity(tokenId) + myLiquidity := pos.PositionGetPositionLiquidity(tokenId) // my liquidity ratio liqRatioX96 := (myLiquidity * Q96 / poolStakedLiquidity * Q96) // 2 times Q96 because of Q96 / Q96 diff --git a/staker/staker.gno b/staker/staker.gno index 2841964e..b92228d3 100644 --- a/staker/staker.gno +++ b/staker/staker.gno @@ -6,7 +6,7 @@ import ( "gno.land/p/demo/ufmt" gnft "gno.land/r/gnft" // GNFT, Gnoswap NFT - gnos "gno.land/r/gnos" // GNOS, INTERNAL Reward Token + gns "gno.land/r/gns" // GNS, INTERNAL Reward Token obl "gno.land/r/obl" // OBL, EXTERNAL Reward Token s "gno.land/r/position" @@ -30,8 +30,8 @@ func init() { poolTiers["BAR/FOO_500"] = 1 // DEV // tier 2 - poolTiers["GNOS/USDT_500"] = 2 - poolTiers["ATOM/GNOS_500"] = 2 + poolTiers["GNS/USDT_500"] = 2 + poolTiers["ATOM/GNS_500"] = 2 // tier 3 poolTiers["ATOM/GNOT_500"] = 3 @@ -142,10 +142,10 @@ func UnstakeToken( } // default `Internal` reward - internalGNOS := rewardMathComputeInternalRewardAmount(tokenId, deposit) + internalGNS := rewardMathComputeInternalRewardAmount(tokenId, deposit) // transfer it - gnos.TransferFrom(a2u(INTERNAL_REWARD_ACCOUNT), a2u(deposit.owner), uint64(internalGNOS)) + gns.TransferFrom(a2u(INTERNAL_REWARD_ACCOUNT), a2u(deposit.owner), uint64(internalGNS)) // unstaked status delete(deposits, tokenId) diff --git a/staker/staker_test.gno b/staker/staker_test.gno index e48fd908..71a95847 100644 --- a/staker/staker_test.gno +++ b/staker/staker_test.gno @@ -1,19 +1,20 @@ package staker import ( - "encoding/gjson" "std" "testing" + "encoding/gjson" + "gno.land/p/demo/testutils" - g "gno.land/r/gov" p "gno.land/r/pool" - s "gno.land/r/position" + pos "gno.land/r/position" gnft "gno.land/r/gnft" // GNFT, Gnoswap NFT - gnos "gno.land/r/gnos" // GNOS, INTERNAL Reward Token - obl "gno.land/r/obl" // OBL, EXTERNAL Reward Token + gns "gno.land/r/gns" // GNS, Gnoswap Share + + _ "gno.land/r/grc20_wrapper" ) var ( @@ -25,45 +26,42 @@ var ( ira = testutils.TestAddress("ira") // Internal Reward Account ) +var ( + fooPath = "gno.land/r/foo" + barPath = "gno.land/r/bar" +) + func init() { // init pool tiers // tier 1 - poolTiers["BAR/FOO_500"] = 1 // DEV + poolTiers["gno.land/r/bar:gno.land/r/foo:500"] = 1 // DEV // tier 2 - poolTiers["GNOS/USDT_500"] = 2 - poolTiers["ATOM/GNOS_500"] = 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 - - // debug - print addr - // println(pc01, "// pc01") - // println(ci01, "// ci01") - // println(lp01, "// lp01") - // println(lp02, "// lp02") - // println(ira, "// internal_reward_account") - // println(p.GetOrigPkgAddr(), "// pool") - // println(s.GetOrigPkgAddr(), "// position") - // println(GetOrigPkgAddr(), "// staker") } func TestPoolInitCreatePool(t *testing.T) { std.TestSetOrigCaller(pc01) + p.InitManual() std.TestSkipHeights(1) - p.CreatePool("foo", "bar", 500, 130621891405341611593710811006) + + p.CreatePool(fooPath, barPath, 500, 130621891405341611593710811006) std.TestSkipHeights(1) } func TestPositionMint(t *testing.T) { { std.TestSetOrigCaller(lp01) - tPosTokenId, tPosLiquidity, tPosAmount0, tPosAmount1 := s.Mint( - std.Address("foo"), // token0 - std.Address("bar"), // token1 + tPosTokenId, tPosLiquidity, tPosAmount0, tPosAmount1 := pos.Mint( + fooPath, // token0 + barPath, // token1 uint16(500), // fee int32(9000), // tickLower int32(11000), // tickUpper @@ -86,9 +84,9 @@ func TestPositionMint(t *testing.T) { { std.TestSetOrigCaller(lp02) - tPosTokenId, tPosLiquidity, tPosAmount0, tPosAmount1 := s.Mint( - std.Address("foo"), // token0 - std.Address("bar"), // token1 + tPosTokenId, tPosLiquidity, tPosAmount0, tPosAmount1 := pos.Mint( + fooPath, // token0 + barPath, // token1 uint16(500), // fee int32(9100), // tickLower int32(12000), // tickUpper @@ -114,16 +112,16 @@ func TestCreateExternalIncentive(t *testing.T) { std.TestSetOrigCaller(ci01) CreateExternalIncentive( - "bar_foo_500", // targetPoolPath - "OBL", // rewardToken - 10_000_000_000, // rewardAmount - GetTimestamp(), // startTimestamp + "gno.land/r/bar:gno.land/r/foo:500", // targetPoolPath + "OBL", // rewardToken + 10_000_000_000, // rewardAmount + GetTimestamp(), // startTimestamp GetTimestamp()+TIMESTAMP_30DAYS+TIMESTAMP_30DAYS, // endTimestamp ) std.TestSkipHeights(5) shouldPanic(t, func() { - CreateExternalIncentive("bar_foo_500", "OBL", 10_000_000_000, GetTimestamp(), GetTimestamp()+TIMESTAMP_30DAYS+TIMESTAMP_30DAYS) + CreateExternalIncentive("gno.land/r/bar:gno.land/r/foo:500", "OBL", 10_000_000_000, GetTimestamp(), GetTimestamp()+TIMESTAMP_30DAYS+TIMESTAMP_30DAYS) }) } @@ -153,8 +151,8 @@ func TestApiGetRewardsByAddress(t *testing.T) { gra := ApiGetRewardByAddress(lp01) jsonStr := gjson.Parse(gra) shouldEQ(t, jsonStr.Get("response.data.0.type").String(), "Internal") - shouldEQ(t, jsonStr.Get("response.data.0.token").String(), "GNOS") - shouldEQ(t, jsonStr.Get("response.data.0.reward").Int(), 252) + shouldEQ(t, jsonStr.Get("response.data.0.token").String(), "GNS") + shouldEQ(t, jsonStr.Get("response.data.0.reward").Int(), 126) shouldEQ(t, jsonStr.Get("response.data.1.type").String(), "External") shouldEQ(t, jsonStr.Get("response.data.1.token").String(), "OBL") shouldEQ(t, jsonStr.Get("response.data.1.reward").Int(), 486) @@ -165,8 +163,8 @@ func TestApiGetRewardsByAddress(t *testing.T) { gra := ApiGetRewardByAddress(lp02) jsonStr := gjson.Parse(gra) shouldEQ(t, jsonStr.Get("response.data.0.type").String(), "Internal") - shouldEQ(t, jsonStr.Get("response.data.0.token").String(), "GNOS") - shouldEQ(t, jsonStr.Get("response.data.0.reward").Int(), 1397) + shouldEQ(t, jsonStr.Get("response.data.0.token").String(), "GNS") + shouldEQ(t, jsonStr.Get("response.data.0.reward").Int(), 698) shouldEQ(t, jsonStr.Get("response.data.1.type").String(), "External") shouldEQ(t, jsonStr.Get("response.data.1.token").String(), "OBL") shouldEQ(t, jsonStr.Get("response.data.1.reward").Int(), 2696) @@ -182,8 +180,8 @@ func TestUnstakeToken(t *testing.T) { shouldEQ(t, gnft.OwnerOf(tid(1)), lp01) // lp01 // check reward - shouldEQ(t, gnos.BalanceOf(a2u(lp01)), 252) // internal - shouldEQ(t, obl.BalanceOf(a2u(lp01)), 486) // external + shouldEQ(t, gns.BalanceOf(a2u(lp01)), 252) // internal + shouldEQ(t, obl.BalanceOf(a2u(lp01)), 486) // external } { @@ -194,8 +192,8 @@ func TestUnstakeToken(t *testing.T) { shouldEQ(t, gnft.OwnerOf(tid(2)), lp02) // lp02 // check reward - shouldEQ(t, gnos.BalanceOf(a2u(lp02)), 1650) // internal - shouldEQ(t, obl.BalanceOf(a2u(lp02)), 3182) // external + shouldEQ(t, gns.BalanceOf(a2u(lp02)), 1650) // internal + shouldEQ(t, obl.BalanceOf(a2u(lp02)), 3182) // external } } diff --git a/staker/utils.gno b/staker/utils.gno index ff06ca93..b66cc74a 100644 --- a/staker/utils.gno +++ b/staker/utils.gno @@ -12,18 +12,21 @@ import ( ) func poolKeyDivide(poolKey string) string { - res := strings.Split(poolKey, "_") + + res := strings.Split(poolKey, ":") if len(res) != 3 { - panic(ufmt.Sprintf("[STAKER] staker.gno__poolKeyDivide() || invalid poolKey(%s)", poolKey)) + panic(ufmt.Sprintf("[STAKER] utils.gno__poolKeyDivide() || invalid poolKey(%s)", poolKey)) } pToken0, pToken1, fee := res[0], res[1], res[2] if pToken0 < pToken1 { - return ufmt.Sprintf("%s/%s_%s", strings.ToUpper(pToken0), strings.ToUpper(pToken1), fee) + zz := ufmt.Sprintf("%s:%s:%s", pToken0, pToken1, fee) + return ufmt.Sprintf("%s:%s:%s", pToken0, pToken1, fee) } - return ufmt.Sprintf("%s/%s_%s", strings.ToUpper(pToken1), strings.ToUpper(pToken0), fee) + zz := ufmt.Sprintf("%s:%s:%s", pToken1, pToken0, fee) + return ufmt.Sprintf("%s:%s:%s", pToken1, pToken0, fee) } func require(condition bool, message string) {