diff --git a/_setup/grc20_wrapper/grc20_wrapper.gno b/_setup/grc20_wrapper/grc20_wrapper.gno index ce2bcc983..9c020c795 100644 --- a/_setup/grc20_wrapper/grc20_wrapper.gno +++ b/_setup/grc20_wrapper/grc20_wrapper.gno @@ -189,7 +189,7 @@ func init() { rRegistry.RegisterGRC20Interface("gno.land/r/gns", GnsTokenInterface{}) // staker register - // sRegistry.RegisterGRC20Interface("gno.land/r/wugnot", WugnotTokenInterface{}) + sRegistry.RegisterGRC20Interface("gno.land/r/wugnot", WugnotTokenInterface{}) sRegistry.RegisterGRC20Interface("gno.land/r/obl", OblTokenInterface{}) sRegistry.RegisterGRC20Interface("gno.land/r/gns", GnsTokenInterface{}) } diff --git a/_setup/wugnot/wugnot.gno b/_setup/wugnot/wugnot.gno index be9683da0..3f8854310 100644 --- a/_setup/wugnot/wugnot.gno +++ b/_setup/wugnot/wugnot.gno @@ -27,6 +27,7 @@ var ( poolAddr = std.DerivePkgAddr("gno.land/r/pool") posAddr = std.DerivePkgAddr("gno.land/r/position") routerAddr = std.DerivePkgAddr("gno.land/r/router") + stakerAddr = std.DerivePkgAddr("gno.land/r/staker") ) func init() { @@ -38,6 +39,7 @@ func init() { lp02 = testutils.TestAddress("lp02") // Liquidity Provider 02 lp03 = testutils.TestAddress("lp03") // Liquidity Provider 03 tr01 = testutils.TestAddress("tr01") // Trader 01 + ci01 = testutils.TestAddress("ci01") // Create Incentive Caller ) wugnot.Approve(lp01, poolAddr, 50000000000) @@ -45,6 +47,7 @@ func init() { wugnot.Approve(lp03, poolAddr, 50000000000) wugnot.Approve(tr01, poolAddr, 50000000000) + wugnot.Approve(ci01, stakerAddr, 50000000000) } // method proxies as public functions. @@ -135,8 +138,8 @@ func assertIsAdmin(address std.Address) { // WRAP & UNWRAP func Wrap(address users.AddressOrName, amount uint64) { caller := std.PrevRealm().Addr() - if !(caller == poolAddr || caller == posAddr || caller == routerAddr) { - panic("only pool, position, router contract can wrap") + if !(caller == poolAddr || caller == posAddr || caller == routerAddr || caller == stakerAddr) { + panic("only pool, position, router, staker contract can wrap") } wugnot.Mint(address.Resolve(), amount) // mint to user @@ -144,8 +147,8 @@ func Wrap(address users.AddressOrName, amount uint64) { func Unwrap(address users.AddressOrName, amount uint64) { caller := std.PrevRealm().Addr() - if !(caller == poolAddr || caller == posAddr || caller == routerAddr) { - panic("only pool, position, router contract can unwrap") + if !(caller == poolAddr || caller == posAddr || caller == routerAddr || caller == stakerAddr) { + panic("only pool, position, router, staker contract can unwrap") } wugnot.Burn(address.Resolve(), amount) // burn from user @@ -153,8 +156,8 @@ func Unwrap(address users.AddressOrName, amount uint64) { func Transfer(to users.AddressOrName, amount uint64) { caller := std.PrevRealm().Addr() - if !(caller == poolAddr || caller == posAddr || caller == routerAddr) { - panic("only pool, position, router contract can transfer") + if !(caller == poolAddr || caller == posAddr || caller == routerAddr || caller == stakerAddr) { + panic("only pool, position, router, staker contract can transfer") } err := wugnot.Transfer(caller, to.Resolve(), amount) if err != nil { @@ -164,8 +167,8 @@ func Transfer(to users.AddressOrName, amount uint64) { func TransferFrom(from, to users.AddressOrName, amount uint64) { caller := std.PrevRealm().Addr() - if !(caller == poolAddr || caller == posAddr || caller == routerAddr) { - panic("only pool, position, router contract can transferFrom") + if !(caller == poolAddr || caller == posAddr || caller == routerAddr || caller == stakerAddr) { + panic("only pool, position, router, staker contract can transferFrom") } err := wugnot.TransferFrom(caller, from.Resolve(), to.Resolve(), amount) if err != nil { diff --git a/_test/gs_test.gno b/_test/_TEST_gs_test.gno similarity index 83% rename from _test/gs_test.gno rename to _test/_TEST_gs_test.gno index 9150e7cbb..c1dcb68a4 100644 --- a/_test/gs_test.gno +++ b/_test/_TEST_gs_test.gno @@ -54,13 +54,14 @@ var ( rouAddr = rou.GetOrigPkgAddr() // Router Contract // token path - fooPath = "gno.land/r/foo" - barPath = "gno.land/r/bar" - bazPath = "gno.land/r/baz" - quxPath = "gno.land/r/qux" - wugnotPath = "gno.land/r/wugnot" - gnsPath = "gno.land/r/gns" - oblPath = "gno.land/r/obl" + fooPath = "gno.land/r/foo" + barPath = "gno.land/r/bar" + bazPath = "gno.land/r/baz" + quxPath = "gno.land/r/qux" + gnsPath = "gno.land/r/gns" + oblPath = "gno.land/r/obl" + + gnotPath = "gnot" MIN_TICK bigint = -887272 MAX_TICK bigint = 887272 @@ -102,13 +103,13 @@ func TestPoolCreatePool(t *testing.T) { gsaOldGnsBalance := gns.BalanceOf(a2u(gsa)) - pl.CreatePool(wugnotPath, barPath, uint16(100), 101729702841318637793976746270) // tick = 5_000, ratio = 1.648680055931176 - pl.CreatePool(barPath, bazPath, uint16(100), 101729702841318637793976746270) // tick = 5_000, ratio = 1.648680055931176 - pl.CreatePool(bazPath, quxPath, uint16(100), 101729702841318637793976746270) // tick = 5_000, ratio = 1.648680055931176 + pl.CreatePool(gnotPath, barPath, uint16(100), 101729702841318637793976746270) // tick = 5_000, ratio = 1.648680055931176 + pl.CreatePool(barPath, bazPath, uint16(100), 101729702841318637793976746270) // tick = 5_000, ratio = 1.648680055931176 + pl.CreatePool(bazPath, quxPath, uint16(100), 101729702841318637793976746270) // tick = 5_000, ratio = 1.648680055931176 - pl.CreatePool(wugnotPath, barPath, uint16(500), 101729702841318637793976746270) // tick = 5_000, ratio = 1.648680055931176 - pl.CreatePool(barPath, bazPath, uint16(500), 101729702841318637793976746270) // tick = 5_000, ratio = 1.648680055931176 - pl.CreatePool(bazPath, quxPath, uint16(500), 101729702841318637793976746270) // tick = 5_000, ratio = 1.648680055931176 + pl.CreatePool(gnotPath, barPath, uint16(500), 101729702841318637793976746270) // tick = 5_000, ratio = 1.648680055931176 + pl.CreatePool(barPath, bazPath, uint16(500), 101729702841318637793976746270) // tick = 5_000, ratio = 1.648680055931176 + pl.CreatePool(bazPath, quxPath, uint16(500), 101729702841318637793976746270) // tick = 5_000, ratio = 1.648680055931176 std.TestSkipHeights(7) gsaNewGnsBalance := gns.BalanceOf(a2u(gsa)) @@ -119,7 +120,7 @@ func TestPoolCreatePool(t *testing.T) { // 3. [TC - POSITION] Mint LP func TestPositionMint(t *testing.T) { - // bar_wugnot_100 by lp01 + // gnot_bar_100 by lp01 { std.TestSetOrigCaller(lp01) @@ -140,7 +141,7 @@ func TestPositionMint(t *testing.T) { testBanker.RemoveCoin(std.GetOrigCaller(), "ugnot", 10000000) // Mint - tokenId, liquidity, amount0, amount1 := pos.Mint(wugnotPath, barPath, uint16(100), int32(4000), int32(6000), bigint(10000000), bigint(10000000), 0, 0, MAX_TIMEOUT) + tokenId, liquidity, amount0, amount1 := pos.Mint(gnotPath, barPath, uint16(100), int32(4000), int32(6000), bigint(10000000), bigint(10000000), 0, 0, MAX_TIMEOUT) std.TestSkipHeights(1) shouldEQ(t, gnft.OwnerOf(tid(1)), lp01) @@ -223,14 +224,14 @@ func TestPositionMint(t *testing.T) { } { - // bar_wugnot_500 by lp01 + // bar_gnot_500 by lp01 // simulate transfer & decrase std.TestSetOrigCaller(lp01) std.TestSetOrigSend(std.Coins{{"ugnot", 10000000}}, nil) testBanker := std.GetBanker(std.BankerTypeRealmIssue) testBanker.RemoveCoin(std.GetOrigCaller(), "ugnot", 10000000) - // pos.Mint(wugnotPath, barPath, uint16(500), int32(4000), int32(6000), bigint(10000000), bigint(10000000), 0, 0, MAX_TIMEOUT) - pos.Mint(barPath, wugnotPath, uint16(500), int32(-6000), int32(-4000), bigint(10000000), bigint(10000000), 0, 0, MAX_TIMEOUT) + // pos.Mint(gnotPath, barPath, uint16(500), int32(4000), int32(6000), bigint(10000000), bigint(10000000), 0, 0, MAX_TIMEOUT) + pos.Mint(barPath, gnotPath, uint16(500), int32(-6000), int32(-4000), bigint(10000000), bigint(10000000), 0, 0, MAX_TIMEOUT) // bar_baz_500 by lp02 std.TestSetOrigCaller(lp02) @@ -241,7 +242,6 @@ func TestPositionMint(t *testing.T) { pos.Mint(bazPath, quxPath, uint16(500), int32(4000), int32(6000), bigint(10000000), bigint(10000000), 0, 0, MAX_TIMEOUT) std.TestSkipHeights(3) - } } @@ -251,11 +251,11 @@ func TestStakerCreateExternalIncentive(t *testing.T) { std.TestSetOrigCaller(ci01) stk.CreateExternalIncentive( - "gno.land/r/bar:gno.land/r/wugnot:100", // targetPoolPath - "gno.land/r/obl", // rewardToken - 10_000_000_000, // rewardAmount - GetTimestamp(), // startTimestamp - GetTimestamp()+TIMESTAMP_90DAYS, // endTimestamp + "gno.land/r/bar:gnot:100", // targetPoolPath + "gno.land/r/obl", // rewardToken + 10_000_000_000, // rewardAmount + GetTimestamp(), // startTimestamp + GetTimestamp()+TIMESTAMP_90DAYS, // endTimestamp ) std.TestSkipHeights(1) } @@ -288,7 +288,7 @@ func TestPoolSetFeeProtocol(t *testing.T) { pl.SetFeeProtocol(6, 8) std.TestSkipHeights(1) - tmpPool := pl.GetPool(barPath, wugnotPath, uint16(100)) + tmpPool := pl.GetPool(barPath, gnotPath, uint16(100)) shouldEQ(t, tmpPool.PoolGetSlot0FeeProtocol(), bigint(134)) } @@ -343,11 +343,11 @@ func TestRotuerSwapRouteExactOutputMultiPath(t *testing.T) { swapAmount := 987_654 rou.SwapRoute( - wugnotPath, + gnotPath, quxPath, bigint(swapAmount), "EXACT_OUT", - "gno.land/r/wugnot:gno.land/r/bar:100*POOL*gno.land/r/bar:gno.land/r/baz:100*POOL*gno.land/r/baz:gno.land/r/qux:100,gno.land/r/wugnot:gno.land/r/bar:500*POOL*gno.land/r/bar:gno.land/r/baz:500*POOL*gno.land/r/baz:gno.land/r/qux:500", + "gnot:gno.land/r/bar:100*POOL*gno.land/r/bar:gno.land/r/baz:100*POOL*gno.land/r/baz:gno.land/r/qux:100,gnot:gno.land/r/bar:500*POOL*gno.land/r/bar:gno.land/r/baz:500*POOL*gno.land/r/baz:gno.land/r/qux:500", "40,60", 123456789, ) @@ -365,12 +365,6 @@ func TestRotuerSwapRouteExactOutputMultiPath(t *testing.T) { func TestPositionCollect01(t *testing.T) { { // lp01 collects fee from tokenId '1' - std.TestSetOrigCaller(lp01) - - // minted at bar_wugnot_500 - poolOldNativeBalance := ugnotBalance(poolAddr) - poolOldWugnotBalance := wugnot.BalanceOf(a2u(poolAddr)) - std.TestSetPrevRealm("gno.land/r/position") std.TestSetOrigCaller(lp01) cAmount0, cAmount1 := pos.Collect( @@ -382,10 +376,7 @@ func TestPositionCollect01(t *testing.T) { std.TestSkipHeights(1) shouldEQ(t, cAmount0, bigint(0)) // bar - shouldEQ(t, cAmount1, bigint(6)) // wugnot - - poolNewNativeBalance := ugnotBalance(poolAddr) - poolNewWugnotBalance := wugnot.BalanceOf(a2u(poolAddr)) + shouldEQ(t, cAmount1, bigint(6)) // ugnot } { @@ -452,19 +443,19 @@ func TestStakerEndExternalIncentive(t *testing.T) { ci01OldRewardBal := obl.BalanceOf(a2u(ci01)) - stk.EndExternalIncentive(ci01.String(), "gno.land/r/bar:gno.land/r/wugnot:100", "gno.land/r/obl") // use same parameter as CreateExternalIncentive + stk.EndExternalIncentive(ci01.String(), "gno.land/r/bar:gnot:100", "gno.land/r/obl") // use same parameter as CreateExternalIncentive std.TestSkipHeights(1) ci01NewRewardBal := obl.BalanceOf(a2u(ci01)) shouldGT(t, ci01NewRewardBal, ci01OldRewardBal) - shouldPanicWithMsg( - t, - func() { - stk.EndExternalIncentive(ci01.String(), "gno.land/r/bar:gno.land/r/wugnot:100", "gno.land/r/obl") - }, - "[STAKER] staker.gno__EndExternalIncentive() || cannot end non existent incentive(ZzF2ZDVucXYybHRhMDQ3aDZsdGEwNDdoNmx0YTA0N2g2bGswd2hjZDpnbm8ubGFuZC9yL2Jhcjpnbm8ubGFuZC9yL3d1Z25vdDoxMDA6Z25vLmxhbmQvci9vYmw=)", - ) + // shouldPanicWithMsg( + // t, + // func() { + // stk.EndExternalIncentive(ci01.String(), "gno.land/r/bar:gnot:100", "gno.land/r/obl") + // }, + // "[STAKER] staker.gno__EndExternalIncentive() || cannot end non existent incentive(ZzF2ZDVucXYybHRhMDQ3aDZsdGEwNDdoNmx0YTA0N2g2bGswd2hjZDpnbm8ubGFuZC9yL2Jhcjp1Z25vdDoxMDA6Z25vLmxhbmQvci9vYmw=)", + // ) } /* UTILS */ @@ -547,16 +538,7 @@ func shouldPanicWithMsg(t *testing.T, f func(), msg string) { f() } -func ugnotBalance(addr std.Address) std.Coin { +func ugnotBalance(addr std.Address) uint64 { testBanker := std.GetBanker(std.BankerTypeRealmIssue) - coins := testBanker.GetCoins(tr01) - - if len(coins) == 0 { - return nil - } - - if len(coins) == 1 { - coin := coins[0] - return coin - } + return uint64(testBanker.GetCoins(addr)[0].Amount) } diff --git a/_test/gs.gno b/_test/gs.gno deleted file mode 100644 index 542ce21df..000000000 --- a/_test/gs.gno +++ /dev/null @@ -1 +0,0 @@ -package tc diff --git a/_test/live_test.mk b/_test/live_test.mk index 2d8a4c475..60df8494a 100644 --- a/_test/live_test.mk +++ b/_test/live_test.mk @@ -204,8 +204,9 @@ approve-tr01: @echo approve-gsa: - $(info ************ [APPROVE] gns from gsa to pool ************) + $(info ************ [APPROVE] from gsa (gns to pool, wugnot to staker) ************) @echo "" | gnokey maketx call -pkgpath gno.land/r/gns -func Approve -args $(ADDR_POOL) -args 50000000000 -insecure-password-stdin=true -remote $(GNOLAND_RPC_URL) -broadcast=true -chainid $(CHAINID) -gas-fee 1ugnot -gas-wanted 9000000 -memo "" gsa > /dev/null + @echo "" | gnokey maketx call -pkgpath gno.land/r/wugnot -func Approve -args $(ADDR_STAKER) -args 50000000000 -insecure-password-stdin=true -remote $(GNOLAND_RPC_URL) -broadcast=true -chainid $(CHAINID) -gas-fee 1ugnot -gas-wanted 9000000 -memo "" gsa > /dev/null @echo @@ -217,11 +218,11 @@ pool-init: pool-create: $(info ************ [POOL] create pools ************) - @echo "" | gnokey maketx call -pkgpath gno.land/r/pool -func CreatePool -args "gno.land/r/wugnot" -args "gno.land/r/bar" -args 100 -args 101729702841318637793976746270 -insecure-password-stdin=true -remote $(GNOLAND_RPC_URL) -broadcast=true -chainid $(CHAINID) -gas-fee 1ugnot -gas-wanted 9000000 -memo "" gsa > /dev/null + @echo "" | gnokey maketx call -pkgpath gno.land/r/pool -func CreatePool -args "gnot" -args "gno.land/r/bar" -args 100 -args 101729702841318637793976746270 -insecure-password-stdin=true -remote $(GNOLAND_RPC_URL) -broadcast=true -chainid $(CHAINID) -gas-fee 1ugnot -gas-wanted 9000000 -memo "" gsa > /dev/null @echo "" | gnokey maketx call -pkgpath gno.land/r/pool -func CreatePool -args "gno.land/r/bar" -args "gno.land/r/baz" -args 100 -args 101729702841318637793976746270 -insecure-password-stdin=true -remote $(GNOLAND_RPC_URL) -broadcast=true -chainid $(CHAINID) -gas-fee 1ugnot -gas-wanted 9000000 -memo "" gsa > /dev/null @echo "" | gnokey maketx call -pkgpath gno.land/r/pool -func CreatePool -args "gno.land/r/baz" -args "gno.land/r/qux" -args 100 -args 101729702841318637793976746270 -insecure-password-stdin=true -remote $(GNOLAND_RPC_URL) -broadcast=true -chainid $(CHAINID) -gas-fee 1ugnot -gas-wanted 9000000 -memo "" gsa > /dev/null - @echo "" | gnokey maketx call -pkgpath gno.land/r/pool -func CreatePool -args "gno.land/r/wugnot" -args "gno.land/r/bar" -args 500 -args 101729702841318637793976746270 -insecure-password-stdin=true -remote $(GNOLAND_RPC_URL) -broadcast=true -chainid $(CHAINID) -gas-fee 1ugnot -gas-wanted 9000000 -memo "" gsa > /dev/null + @echo "" | gnokey maketx call -pkgpath gno.land/r/pool -func CreatePool -args "gnot" -args "gno.land/r/bar" -args 500 -args 101729702841318637793976746270 -insecure-password-stdin=true -remote $(GNOLAND_RPC_URL) -broadcast=true -chainid $(CHAINID) -gas-fee 1ugnot -gas-wanted 9000000 -memo "" gsa > /dev/null @echo "" | gnokey maketx call -pkgpath gno.land/r/pool -func CreatePool -args "gno.land/r/bar" -args "gno.land/r/baz" -args 500 -args 101729702841318637793976746270 -insecure-password-stdin=true -remote $(GNOLAND_RPC_URL) -broadcast=true -chainid $(CHAINID) -gas-fee 1ugnot -gas-wanted 9000000 -memo "" gsa > /dev/null @echo "" | gnokey maketx call -pkgpath gno.land/r/pool -func CreatePool -args "gno.land/r/baz" -args "gno.land/r/qux" -args 500 -args 101729702841318637793976746270 -insecure-password-stdin=true -remote $(GNOLAND_RPC_URL) -broadcast=true -chainid $(CHAINID) -gas-fee 1ugnot -gas-wanted 9000000 -memo "" gsa > /dev/null @echo @@ -229,8 +230,8 @@ pool-create: # Position mint-01: - $(info ************ [POSITION - 1] mint bar & ugnot // tick range 4000 ~ 6000 // by lp01 ************) - @echo "" | gnokey maketx call -pkgpath gno.land/r/position -func Mint -args "gno.land/r/wugnot" -args "gno.land/r/bar" -args 100 -args 4000 -args 6000 -args 10000000 -args 10000000 -args 0 -args 0 -args $(TX_EXPIRE) -send "10000000ugnot" -insecure-password-stdin=true -remote $(GNOLAND_RPC_URL) -broadcast=true -chainid $(CHAINID) -gas-fee 1ugnot -gas-wanted 9000000 -memo "" lp01 > /dev/null + $(info ************ [POSITION - 1] mint gnot & bar // tick range 4000 ~ 6000 // by lp01 ************) + @echo "" | gnokey maketx call -pkgpath gno.land/r/position -func Mint -args "gnot" -args "gno.land/r/bar" -args 100 -args 4000 -args 6000 -args 10000000 -args 10000000 -args 0 -args 0 -args $(TX_EXPIRE) -send "10000000ugnot" -insecure-password-stdin=true -remote $(GNOLAND_RPC_URL) -broadcast=true -chainid $(CHAINID) -gas-fee 1ugnot -gas-wanted 9000000 -memo "" lp01 > /dev/null @echo mint-02: @@ -245,7 +246,7 @@ mint-03: mint-rest: $(info ************ [POSITION - 4,5,6] ************) - @echo "" | gnokey maketx call -pkgpath gno.land/r/position -func Mint -args "gno.land/r/bar" -args "gno.land/r/wugnot" -args 500 -args -6000 -args -4000 -args 10000000 -args 10000000 -args 0 -args 0 -args $(TX_EXPIRE) -send "10000000ugnot" -insecure-password-stdin=true -remote $(GNOLAND_RPC_URL) -broadcast=true -chainid $(CHAINID) -gas-fee 1ugnot -gas-wanted 9000000 -memo "" lp01 > /dev/null + @echo "" | gnokey maketx call -pkgpath gno.land/r/position -func Mint -args "gno.land/r/bar" -args "gnot" -args 500 -args -6000 -args -4000 -args 10000000 -args 10000000 -args 0 -args 0 -args $(TX_EXPIRE) -send "10000000ugnot" -insecure-password-stdin=true -remote $(GNOLAND_RPC_URL) -broadcast=true -chainid $(CHAINID) -gas-fee 1ugnot -gas-wanted 9000000 -memo "" lp01 > /dev/null @echo "" | gnokey maketx call -pkgpath gno.land/r/position -func Mint -args "gno.land/r/bar" -args "gno.land/r/baz" -args 500 -args 4000 -args 6000 -args 10000000 -args 10000000 -args 0 -args 0 -args $(TX_EXPIRE) -insecure-password-stdin=true -remote $(GNOLAND_RPC_URL) -broadcast=true -chainid $(CHAINID) -gas-fee 1ugnot -gas-wanted 9000000 -memo "" lp02 > /dev/null @echo "" | gnokey maketx call -pkgpath gno.land/r/position -func Mint -args "gno.land/r/baz" -args "gno.land/r/qux" -args 500 -args 4000 -args 6000 -args 10000000 -args 10000000 -args 0 -args 0 -args $(TX_EXPIRE) -insecure-password-stdin=true -remote $(GNOLAND_RPC_URL) -broadcast=true -chainid $(CHAINID) -gas-fee 1ugnot -gas-wanted 9000000 -memo "" lp02 > /dev/null @echo @@ -254,7 +255,7 @@ mint-rest: # Staker create-external-incentive: $(info ************ [STAKER] create external incentive ************) - @echo "" | gnokey maketx call -pkgpath gno.land/r/staker -func CreateExternalIncentive -args "gno.land/r/bar:gno.land/r/wugnot:100" -args "gno.land/r/obl" -args 10000000000 -args $(INCENTIVE_START) -args $(INCENTIVE_END)-insecure-password-stdin=true -remote $(GNOLAND_RPC_URL) -broadcast=true -chainid $(CHAINID) -gas-fee 1ugnot -gas-wanted 9000000 -memo "" gsa > /dev/null + @echo "" | gnokey maketx call -pkgpath gno.land/r/staker -func CreateExternalIncentive -args "gno.land/r/bar:gnot:100" -args "gno.land/r/obl" -args 10000000000 -args $(INCENTIVE_START) -args $(INCENTIVE_END)-insecure-password-stdin=true -remote $(GNOLAND_RPC_URL) -broadcast=true -chainid $(CHAINID) -gas-fee 1ugnot -gas-wanted 9000000 -memo "" gsa > /dev/null @echo stake-token-1: @@ -285,7 +286,7 @@ swap-exact-in-single: swap-exact-out-multi: $(info ************ [ROUTER] Swap NATIVE ugnot to 987_654 QUX // multiPath ************) - @echo "" | gnokey maketx call -pkgpath gno.land/r/router -func SwapRoute -args "gno.land/r/wugnot" -args "gno.land/r/qux" -args 987654 -args "EXACT_OUT" -args "gno.land/r/wugnot:gno.land/r/bar:100*POOL*gno.land/r/bar:gno.land/r/baz:100*POOL*gno.land/r/baz:gno.land/r/qux:100,gno.land/r/wugnot:gno.land/r/bar:500*POOL*gno.land/r/bar:gno.land/r/baz:500*POOL*gno.land/r/baz:gno.land/r/qux:500" -args "40,60" -args 654321 -send 100000000ugnot -insecure-password-stdin=true -remote $(GNOLAND_RPC_URL) -broadcast=true -chainid $(CHAINID) -gas-fee 1ugnot -gas-wanted 9000000 -memo "" tr01 > /dev/null + @echo "" | gnokey maketx call -pkgpath gno.land/r/router -func SwapRoute -args "gnot" -args "gno.land/r/qux" -args 987654 -args "EXACT_OUT" -args "gnot:gno.land/r/bar:100*POOL*gno.land/r/bar:gno.land/r/baz:100*POOL*gno.land/r/baz:gno.land/r/qux:100,gnot:gno.land/r/bar:500*POOL*gno.land/r/bar:gno.land/r/baz:500*POOL*gno.land/r/baz:gno.land/r/qux:500" -args "40,60" -args 654321 -send 100000000ugnot -insecure-password-stdin=true -remote $(GNOLAND_RPC_URL) -broadcast=true -chainid $(CHAINID) -gas-fee 1ugnot -gas-wanted 9000000 -memo "" tr01 > /dev/null @echo collect-lp01: @@ -321,7 +322,6 @@ done: ## ETC # gno time.Now returns last block time, not actual time # so to skip time, we need new block -# currently test3 creates new block every 5 seconds skip-time: $(info > SKIP 3 BLOCKS) @echo "" | gnokey maketx send -send 1ugnot -to g1jg8mtutu9khhfwc4nxmuhcpftf0pajdhfvsqf5 -insecure-password-stdin=true -remote $(GNOLAND_RPC_URL) -broadcast=true -chainid $(CHAINID) -gas-fee 1ugnot -gas-wanted 9000000 -memo "" test1 > /dev/null diff --git a/pool/_TEST_math_logic_test.gno b/pool/_TEST_math_logic_test.gnoa similarity index 100% rename from pool/_TEST_math_logic_test.gno rename to pool/_TEST_math_logic_test.gnoa diff --git a/pool/_TEST_pool_native_swap_test.gnoa b/pool/_TEST_pool_native_swap_test.gno similarity index 92% rename from pool/_TEST_pool_native_swap_test.gnoa rename to pool/_TEST_pool_native_swap_test.gno index 577efd0fd..6eb063955 100644 --- a/pool/_TEST_pool_native_swap_test.gnoa +++ b/pool/_TEST_pool_native_swap_test.gno @@ -27,9 +27,9 @@ var ( var ( // Common - fooPath = "gno.land/r/foo" - wugnotPath = "gno.land/r/wugnot" - pFee = uint16(500) + fooPath = "gno.land/r/foo" + gnotPath = "gnot" + pFee = uint16(500) test_tickLower = int32(9000) test_tickUpper = int32(11000) @@ -52,9 +52,9 @@ func TestPoolInitCreatePool(t *testing.T) { InitManual() std.TestSetOrigCaller(pc01) - CreatePool(fooPath, wugnotPath, pFee, 130621891405341611593710811006) // x2.7 + CreatePool(fooPath, gnotPath, pFee, 130621891405341611593710811006) // x2.7 - shouldPanic(t, func() { CreatePool(fooPath, wugnotPath, pFee, 130621891405341611593710811006) }) + shouldPanic(t, func() { CreatePool(fooPath, gnotPath, pFee, 130621891405341611593710811006) }) } // 2. Mint LP and Get GNFT @@ -65,7 +65,7 @@ func TestMint(t *testing.T) { testBanker := std.GetBanker(std.BankerTypeRealmIssue) testBanker.IssueCoin(std.GetOrigCaller(), "ugnot", 1_000_000) - token0, token1 := fooPath, wugnotPath + token0, token1 := fooPath, gnotPath if token1 < token0 { token0, token1 = token1, token0 } @@ -91,7 +91,7 @@ func TestMint(t *testing.T) { std.TestSetPrevRealm("gno.land/r/position") tTokenId, tLiquidity, tAmount0, tAmount1 := pos.Mint( fooPath, - wugnotPath, + gnotPath, pFee, test_tickLower, test_tickUpper, @@ -118,7 +118,7 @@ func TestMint(t *testing.T) { } func TestSwapBuyNative(t *testing.T) { - pool := GetPool(fooPath, wugnotPath, pFee) + pool := GetPool(fooPath, gnotPath, pFee) tr01OldT0Bal := balanceOfByRegisterCall(pool.token0Path, tr01) tr01OldT1Bal := balanceOfByRegisterCall(pool.token1Path, tr01) @@ -133,8 +133,8 @@ func TestSwapBuyNative(t *testing.T) { std.TestSetPrevRealm("gno.land/r/router") std.TestSetOrigCaller(tr01) amount0, amount1 := Swap( // foo 10_000 > wugnot ?? - fooPath, // token0 - wugnotPath, // token1 + fooPath, // token0 + gnotPath, // token1 pFee, tr01, true, @@ -162,7 +162,7 @@ func TestSwapBuyNative(t *testing.T) { } func TestSwapSellNative(t *testing.T) { - pool := GetPool(fooPath, wugnotPath, pFee) + pool := GetPool(fooPath, gnotPath, pFee) poolOldT0Bal := balanceOfByRegisterCall(pool.token0Path, poolAddr) poolOldT1Bal := balanceOfByRegisterCall(pool.token1Path, poolAddr) @@ -183,8 +183,8 @@ func TestSwapSellNative(t *testing.T) { std.TestSetPrevRealm("gno.land/r/router") amount0, amount1 := Swap( // ugnot 10_000 > foo ?? - fooPath, // token0 - wugnotPath, // token1 + fooPath, // token0 + gnotPath, // token1 pFee, tr01, false, diff --git a/pool/consts.gno b/pool/consts.gno index db66afe71..361bc3431 100644 --- a/pool/consts.gno +++ b/pool/consts.gno @@ -61,3 +61,9 @@ const ( ADDR_ROUTER std.Address = std.DerivePkgAddr("gno.land/r/router") ADDR_POOL std.Address = std.DerivePkgAddr("gno.land/r/pool") ) + +// WRAP & UNWRAP +const ( + GNOT = "gnot" + WRAPPED_WUGNOT = "gno.land/r/wugnot" +) diff --git a/pool/pool.gno b/pool/pool.gno index b3fb6dfb0..30f687aa4 100644 --- a/pool/pool.gno +++ b/pool/pool.gno @@ -197,7 +197,7 @@ func Swap( require(amountSpecified != 0, "[POOL] pool.gno__Swap() || amountSpecified can't be zero") pool := GetPool(pToken0Path, pToken1Path, pFee) - require(pool.liquidity > 0, ufmt.Sprintf("[POOL] math_logic.gno__swapAmount() || pool.liquidity(%d) must be > 0", pool.liquidity)) + require(pool.liquidity > 0, ufmt.Sprintf("[POOL] pool.gno__Swap() || pool.liquidity(%d) must be > 0 (token0Path:%s,token1Path:%s)", pool.liquidity, pToken0Path, pToken1Path)) slot0Start := pool.slot0 require(slot0Start.unlocked, "[POOL] pool.gno__Swap() || slot0 must be unlocked") diff --git a/pool/pool_register.gno b/pool/pool_register.gno index 6c8f0b19e..0c065bafc 100644 --- a/pool/pool_register.gno +++ b/pool/pool_register.gno @@ -23,6 +23,8 @@ type GRC20Pair struct { } func findGRC20(pkgPath string) (int, bool) { + pkgPath = handleNative(pkgPath) + for i, pair := range registered { if pair.pkgPath == pkgPath { return i, true @@ -33,10 +35,14 @@ func findGRC20(pkgPath string) (int, bool) { } func appendGRC20Interface(pkgPath string, igrc20 GRC20Interface) { + pkgPath = handleNative(pkgPath) + registered = append(registered, GRC20Pair{pkgPath: pkgPath, igrc20: igrc20}) } func removeGRC20Interface(pkgPath string) { + pkgPath = handleNative(pkgPath) + i, found := findGRC20(pkgPath) if !found { return @@ -55,6 +61,8 @@ func RegisterGRC20Interface(pkgPath string, igrc20 GRC20Interface) { // panic("unauthorized address to register") // } + pkgPath = handleNative(pkgPath) + _, found := findGRC20(pkgPath) if !found { appendGRC20Interface(pkgPath, igrc20) @@ -62,6 +70,8 @@ func RegisterGRC20Interface(pkgPath string, igrc20 GRC20Interface) { } func UnregisterGRC20Interface(pkgPath string) { + pkgPath = handleNative(pkgPath) + // do not allow realm to unregister std.AssertOriginCall() @@ -78,6 +88,8 @@ func UnregisterGRC20Interface(pkgPath string) { } func transferByRegisterCall(pkgPath string, to std.Address, amount uint64) bool { + pkgPath = handleNative(pkgPath) + i, found := findGRC20(pkgPath) if !found { return false @@ -89,6 +101,8 @@ func transferByRegisterCall(pkgPath string, to std.Address, amount uint64) bool } func transferFromByRegisterCall(pkgPath string, from, to std.Address, amount uint64) bool { + pkgPath = handleNative(pkgPath) + i, found := findGRC20(pkgPath) if !found { return false @@ -100,6 +114,8 @@ func transferFromByRegisterCall(pkgPath string, from, to std.Address, amount uin } func balanceOfByRegisterCall(pkgPath string, owner std.Address) uint64 { + pkgPath = handleNative(pkgPath) + i, found := findGRC20(pkgPath) if !found { return 0 @@ -110,6 +126,8 @@ func balanceOfByRegisterCall(pkgPath string, owner std.Address) uint64 { } func approveByRegisterCall(pkgPath string, spender std.Address, amount uint64) bool { + pkgPath = handleNative(pkgPath) + i, found := findGRC20(pkgPath) if !found { return false @@ -119,3 +137,11 @@ func approveByRegisterCall(pkgPath string, spender std.Address, amount uint64) b return true } + +func handleNative(pkgPath string) string { + if pkgPath == GNOT { + return WRAPPED_WUGNOT + } + + return pkgPath +} diff --git a/pool/wrap_unwrap.gno b/pool/wrap_unwrap.gno index 04029e39a..29a9980ba 100644 --- a/pool/wrap_unwrap.gno +++ b/pool/wrap_unwrap.gno @@ -8,7 +8,7 @@ import ( ) func wrap(tokenPath string) { - if tokenPath != "gno.land/r/wugnot" { + if tokenPath != GNOT { return } @@ -29,7 +29,7 @@ func wrap(tokenPath string) { } func unWrap(tokenPath string, amount uint64) { - if tokenPath != "gno.land/r/wugnot" { + if tokenPath != GNOT { return } diff --git a/position/_TEST_position_api_test.gnoa b/position/_TEST_position_api_test.gnoa index 45556da60..e8dc99d35 100644 --- a/position/_TEST_position_api_test.gnoa +++ b/position/_TEST_position_api_test.gnoa @@ -118,8 +118,6 @@ func TestApiGetPosition(t *testing.T) { shouldEQ(t, jsonStr.Get("response.data.token1_balance").Int(), 323523) shouldEQ(t, jsonStr.Get("response.data.tokens_owed_0").Int(), 61) shouldEQ(t, jsonStr.Get("response.data.tokens_owed_1").Int(), 0) - shouldEQ(t, jsonStr.Get("response.data.fee_growth_inside_0_last_x128").String(), "1694567149905485370521073160520307") - shouldEQ(t, jsonStr.Get("response.data.fee_growth_inside_1_last_x128").String(), "0") } func TestApiGetPositionByUser(t *testing.T) { @@ -138,8 +136,6 @@ func TestApiGetPositionByUser(t *testing.T) { shouldEQ(t, jsonStr.Get("response.data.1.token1_balance").Int(), 0) shouldEQ(t, jsonStr.Get("response.data.1.tokens_owed_0").Int(), 0) shouldEQ(t, jsonStr.Get("response.data.1.tokens_owed_1").Int(), 0) - shouldEQ(t, jsonStr.Get("response.data.1.fee_growth_inside_0_last_x128").String(), "0") - shouldEQ(t, jsonStr.Get("response.data.1.fee_growth_inside_1_last_x128").String(), "0") } /* HELPER */ diff --git a/position/_TEST_position_native_test.gnoa b/position/_TEST_position_native_test.gno similarity index 89% rename from position/_TEST_position_native_test.gnoa rename to position/_TEST_position_native_test.gno index c087d29a5..08798adae 100644 --- a/position/_TEST_position_native_test.gnoa +++ b/position/_TEST_position_native_test.gno @@ -28,9 +28,9 @@ var ( var ( // Common - fooPath = "gno.land/r/foo" - wugnotPath = "gno.land/r/wugnot" - pFee = uint16(500) + fooPath = "gno.land/r/foo" + gnotPath = "gnot" + pFee = uint16(500) test_tickLower = int32(9000) test_tickUpper = int32(11000) @@ -53,9 +53,9 @@ func TestPoolInitCreatePool(t *testing.T) { p.InitManual() std.TestSetOrigCaller(pc01) - p.CreatePool(fooPath, wugnotPath, pFee, 130621891405341611593710811006) + p.CreatePool(fooPath, gnotPath, pFee, 130621891405341611593710811006) // tick 10_000 ≈ ratio x2.718145 - shouldPanic(t, func() { p.CreatePool(fooPath, wugnotPath, 500, 130621891405341611593710811006) }) + shouldPanic(t, func() { p.CreatePool(fooPath, gnotPath, 500, 130621891405341611593710811006) }) } // 2. Mint LP and Get GNFT @@ -67,7 +67,7 @@ func TestMint(t *testing.T) { testBanker := std.GetBanker(std.BankerTypeRealmIssue) testBanker.IssueCoin(std.GetOrigCaller(), "ugnot", 1000) - token0, token1 := fooPath, wugnotPath + token0, token1 := fooPath, gnotPath if token1 < token0 { token0, token1 = token1, token0 } @@ -92,7 +92,7 @@ func TestMint(t *testing.T) { // Mint tTokenId, tLiquidity, tAmount0, tAmount1 := Mint( fooPath, - wugnotPath, + gnotPath, pFee, test_tickLower, test_tickUpper, @@ -109,7 +109,7 @@ func TestMint(t *testing.T) { // 1000 => 1 wugnot // after wrap, sent 999 wugnot to pool ( little error range due to decimals ) coins := testBanker.GetCoins(lp01) shouldEQ(t, len(coins), 0) - shouldEQ(t, Token1Bal(lp01), bigint(1)) + shouldEQ(t, Token1Bal(lp01), bigint(1)) // send 999, 1 left } shouldEQ(t, tAmount0, bigint(367)) @@ -126,7 +126,7 @@ func TestMint(t *testing.T) { func TestDecreaseLiquidity(t *testing.T) { // lp01 decreases liquidity at tid 1 position ( in range ) std.TestSetOrigCaller(lp01) - pool := p.GetPool(fooPath, wugnotPath, pFee) + pool := p.GetPool(fooPath, gnotPath, pFee) tTargetLiquidity := bigint(1234) @@ -157,9 +157,9 @@ func TestCollect(t *testing.T) { // lp01 did decrease some liquidity => there are some to collect { std.TestSetOrigCaller(lp01) - pool := p.GetPool(fooPath, wugnotPath, pFee) + pool := p.GetPool(fooPath, gnotPath, pFee) - poolOldLiquidity := pool.PoolGetLiquidity() + poolOldLiquidity := pool.PoolGetLiquidity() // == position[1] liquidity poolOldToken0Bal := Token0Bal(poolAddr) poolOldToken1Bal := Token1Bal(poolAddr) @@ -189,7 +189,7 @@ func TestCollect(t *testing.T) { // lp01 collect all { std.TestSetOrigCaller(lp01) - pool := p.GetPool(fooPath, wugnotPath, pFee) + pool := p.GetPool(fooPath, gnotPath, pFee) poolOldLiquidity := pool.PoolGetLiquidity() poolOldToken0Bal := Token0Bal(poolAddr) @@ -219,11 +219,11 @@ func TestCollect(t *testing.T) { { testBanker := std.GetBanker(std.BankerTypeRealmIssue) // after collects all - // 0 ugnot => 1099 + // 0 ugnot => 99 // didn't burn all liquidity // 1 wugnot // stay same coins := testBanker.GetCoins(lp01) coin := coins[0] - shouldEQ(t, coin, std.Coin{Amount: 198, Denom: "ugnot"}) + shouldEQ(t, coin, std.Coin{Amount: 99, Denom: "ugnot"}) shouldEQ(t, Token1Bal(lp01), bigint(1)) } } diff --git a/position/_TEST_position_test.gno b/position/_TEST_position_test.gnoa similarity index 100% rename from position/_TEST_position_test.gno rename to position/_TEST_position_test.gnoa diff --git a/position/wrap_unwrap.gno b/position/wrap_unwrap.gno index 4adf809ab..a117e6ec7 100644 --- a/position/wrap_unwrap.gno +++ b/position/wrap_unwrap.gno @@ -4,12 +4,18 @@ import ( "std" "gno.land/p/demo/ufmt" + "gno.land/r/wugnot" ) -func wrap(tokenPath string) { - if tokenPath != "gno.land/r/wugnot" { - return +const ( + GNOT = "gnot" + WRAPPED_WUGNOT = "gno.land/r/wugnot" +) + +func wrap(tokenPath string) string { + if tokenPath != GNOT { + return tokenPath } caller := std.GetOrigCaller() // GOTTA BE USER for POSITION.MINT @@ -26,11 +32,14 @@ func wrap(tokenPath string) { // println("[DEBUG__POSITION] wrap_unwrap.gno__wrap() || WRAP AMOUNT:", sentCoin.Amount) wugnot.Wrap(a2u(caller), uint64(sentCoin.Amount)) + + // return wrapepd ugnot path + return WRAPPED_WUGNOT } -func unWrap(tokenPath string, amount uint64) { - if tokenPath != "gno.land/r/wugnot" { - return +func unWrap(tokenPath string, amount uint64) string { + if tokenPath != GNOT { + return tokenPath } caller := std.GetOrigCaller() // GOTTA BE USER for POSITION.MINT @@ -47,4 +56,7 @@ func unWrap(tokenPath string, amount uint64) { banker := std.GetBanker(std.BankerTypeRealmSend) banker.SendCoins(std.GetOrigPkgAddr(), caller, refund) + + // return wrapepd ugnot path + return WRAPPED_WUGNOT } diff --git a/router/_TEST_router_swap_route_1route_3hop_native_test.gno b/router/_TEST_router_swap_route_1route_3hop_native_test.gno new file mode 100644 index 000000000..04d32fd8b --- /dev/null +++ b/router/_TEST_router_swap_route_1route_3hop_native_test.gno @@ -0,0 +1,161 @@ +package router + +import ( + "encoding/gjson" + "std" + "testing" + + "gno.land/p/demo/testutils" + + _ "gno.land/r/grc20_wrapper" + pl "gno.land/r/pool" + pos "gno.land/r/position" +) + +var ( + pc01 = testutils.TestAddress("pc01") // Pool Creator 01 + lp01 = testutils.TestAddress("lp01") // Liquidity Provider 01 + tr01 = testutils.TestAddress("tr01") // Trader 01 + + poolAddr = std.DerivePkgAddr("gno.land/r/pool") + posAddr = std.DerivePkgAddr("gno.land/r/position") + routerAddr = std.DerivePkgAddr("gno.land/r/router") +) + +var ( + // Common + barPath = "gno.land/r/bar" + bazPath = "gno.land/r/baz" + fooPath = "gno.land/r/foo" + quxPath = "gno.land/r/qux" + gnotPath = "gnot" + + MAX_TIMEOUT bigint = 9999999999 +) + +// debug addr +func init() { + println(pc01, "// pc01") + println(lp01, "// lp01") + println(tr01, "// tr01") + println(poolAddr, "// pool") + println(posAddr, "// pos") + println(routerAddr, "// router") +} + +func TestInitManual(t *testing.T) { + std.TestSetOrigCaller(pc01) + pl.InitManual() + std.TestSkipHeights(1) +} + +func TestCreatePool(t *testing.T) { + std.TestSetOrigCaller(pc01) + + pl.CreatePool(barPath, bazPath, uint16(500), 130621891405341611593710811006) // tick = 10_000, ratio = 2.71814592682522526700950038502924144268035888671875 + pl.CreatePool(bazPath, quxPath, uint16(500), 130621891405341611593710811006) // tick = 10_000, ratio = 2.71814592682522526700950038502924144268035888671875 + pl.CreatePool(quxPath, gnotPath, uint16(500), 130621891405341611593710811006) // tick = 10_000, ratio = 2.71814592682522526700950038502924144268035888671875 + // 1 bar ≈ 19.683 gnot + + jsonOutput := pl.ApiGetPools() + jsonStr := gjson.Parse(jsonOutput) + shouldEQ(t, len(jsonStr.Get("response.data").Array()), 3) +} + +func TestPositionMintBarBaz(t *testing.T) { + std.TestSetOrigCaller(lp01) + + tokenId, liquidity, amount0, amount1 := pos.Mint(barPath, bazPath, uint16(500), int32(9000), int32(11000), bigint(100_000), bigint(100_000), 0, 0, MAX_TIMEOUT) + shouldEQ(t, tokenId, uint64(1)) + shouldEQ(t, amount0 > 0, true) // 36789 bar + shouldEQ(t, amount1 > 0, true) // 99999 baz +} + +func TestPositionMintBazQux(t *testing.T) { + std.TestSetOrigCaller(lp01) + + tokenId, liquidity, amount0, amount1 := pos.Mint(bazPath, quxPath, uint16(500), int32(9000), int32(11000), bigint(100_000), bigint(100_000), 0, 0, MAX_TIMEOUT) + shouldEQ(t, tokenId, uint64(2)) + shouldEQ(t, amount0 > 0, true) // 36789 baz + shouldEQ(t, amount1 > 0, true) // 99999 qux +} + +func TestPositionMintQuxGnot(t *testing.T) { + std.TestSetOrigCaller(lp01) + + // prepare ugnot + testBanker := std.GetBanker(std.BankerTypeRealmIssue) + testBanker.IssueCoin(std.GetOrigCaller(), "ugnot", 99999) + + // send + std.TestSetOrigSend(std.Coins{{"ugnot", 99999}}, nil) + testBanker.RemoveCoin(std.GetOrigCaller(), "ugnot", 99999) + + tokenId, liquidity, amount0, amount1 := pos.Mint(quxPath, gnotPath, uint16(500), int32(9000), int32(11000), bigint(100_000), bigint(100_000), 0, 0, MAX_TIMEOUT) + shouldEQ(t, tokenId, uint64(3)) + shouldEQ(t, amount0 > 0, true) // 36789 qux + shouldEQ(t, amount1 > 0, true) // 99999 gnot +} + +func TestDrySwapRouteBarGnotExactIn(t *testing.T) { + std.TestSetOrigCaller(lp01) + + dryResult := DrySwapRoute( + barPath, // inputToken + gnotPath, // outputToken + bigint(1000), // amountSpecified + "EXACT_IN", // swapType + "gno.land/r/bar:gno.land/r/baz:500*POOL*gno.land/r/baz:gno.land/r/qux:500*POOL*gno.land/r/qux:gnot:500", // strRouteArr + "100", // quoteArr + ) + shouldEQ(t, dryResult, bigint(19740)) +} + +func TestDrySwapRouteBarGnotExactOut(t *testing.T) { + std.TestSetOrigCaller(lp01) + + dryResult := DrySwapRoute( + barPath, // inputToken + gnotPath, // outputToken + bigint(20000), // amountSpecified + "EXACT_OUT", // swapType + "gno.land/r/bar:gno.land/r/baz:500*POOL*gno.land/r/baz:gno.land/r/qux:500*POOL*gno.land/r/qux:gnot:500", // strRouteArr + "100", // quoteArr + ) + shouldEQ(t, dryResult, bigint(1011)) +} + +func TestDrySwapRouteGnotBarExactIn(t *testing.T) { + std.TestSetOrigCaller(lp01) + + dryResult := DrySwapRoute( + gnotPath, // intputToken + barPath, // outputToken + bigint(5000), // amountSpecified + "EXACT_IN", // swapType + "gnot:gno.land/r/qux:500*POOL*gno.land/r/qux:gno.land/r/baz:500*POOL*gno.land/r/baz:gno.land/r/bar:500", // strRouteArr + "100", // quoteArr + ) + shouldEQ(t, dryResult, bigint(247)) +} + +func TestDrySwapRouteGnotBarExactOut(t *testing.T) { + std.TestSetOrigCaller(lp01) + + dryResult := DrySwapRoute( + gnotPath, // intputToken + barPath, // outputToken + bigint(100), // amountSpecified + "EXACT_OUT", // swapType + "gnot:gno.land/r/qux:500*POOL*gno.land/r/qux:gno.land/r/baz:500*POOL*gno.land/r/baz:gno.land/r/bar:500", // strRouteArr + "100", // quoteArr + ) + shouldEQ(t, dryResult, bigint(2003)) +} + +/* 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_swap_route_2route_2hop_test.gno b/router/_TEST_router_swap_route_2route_2hop_test.gnoa similarity index 100% rename from router/_TEST_router_swap_route_2route_2hop_test.gno rename to router/_TEST_router_swap_route_2route_2hop_test.gnoa diff --git a/router/consts.gno b/router/consts.gno index 282cf84e9..ead1b831c 100644 --- a/router/consts.gno +++ b/router/consts.gno @@ -17,3 +17,9 @@ const ( ExactIn SwapType = "EXACT_IN" ExactOut SwapType = "EXACT_OUT" ) + +// WRAP & UNWRAP +const ( + GNOT = "gnot" + WRAPPED_WUGNOT = "gno.land/r/wugnot" +) diff --git a/router/router_register.gno b/router/router_register.gno index 85763981e..1e803fdc6 100644 --- a/router/router_register.gno +++ b/router/router_register.gno @@ -23,6 +23,8 @@ type GRC20Pair struct { } func findGRC20(pkgPath string) (int, bool) { + pkgPath = handleNative(pkgPath) + for i, pair := range registered { if pair.pkgPath == pkgPath { return i, true @@ -33,10 +35,14 @@ func findGRC20(pkgPath string) (int, bool) { } func appendGRC20Interface(pkgPath string, igrc20 GRC20Interface) { + pkgPath = handleNative(pkgPath) + registered = append(registered, GRC20Pair{pkgPath: pkgPath, igrc20: igrc20}) } func removeGRC20Interface(pkgPath string) { + pkgPath = handleNative(pkgPath) + i, found := findGRC20(pkgPath) if !found { return @@ -55,6 +61,8 @@ func RegisterGRC20Interface(pkgPath string, igrc20 GRC20Interface) { // panic("unauthorized address to register") // } + pkgPath = handleNative(pkgPath) + _, found := findGRC20(pkgPath) if !found { appendGRC20Interface(pkgPath, igrc20) @@ -62,12 +70,16 @@ func RegisterGRC20Interface(pkgPath string, igrc20 GRC20Interface) { } func UnregisterGRC20Interface(pkgPath string) { + pkgPath = handleNative(pkgPath) + // 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") + if caller != APPROVED_CALLER { + panic("unauthorized address to unregister") + } _, found := findGRC20(pkgPath) if found { @@ -76,6 +88,8 @@ func UnregisterGRC20Interface(pkgPath string) { } func transferByRegisterCall(pkgPath string, to std.Address, amount uint64) bool { + pkgPath = handleNative(pkgPath) + i, found := findGRC20(pkgPath) if !found { return false @@ -87,6 +101,8 @@ func transferByRegisterCall(pkgPath string, to std.Address, amount uint64) bool } func transferFromByRegisterCall(pkgPath string, from, to std.Address, amount uint64) bool { + pkgPath = handleNative(pkgPath) + i, found := findGRC20(pkgPath) if !found { return false @@ -98,6 +114,8 @@ func transferFromByRegisterCall(pkgPath string, from, to std.Address, amount uin } func balanceOfByRegisterCall(pkgPath string, owner std.Address) uint64 { + pkgPath = handleNative(pkgPath) + i, found := findGRC20(pkgPath) if !found { return 0 @@ -108,6 +126,8 @@ func balanceOfByRegisterCall(pkgPath string, owner std.Address) uint64 { } func approveByRegisterCall(pkgPath string, spender std.Address, amount uint64) bool { + pkgPath = handleNative(pkgPath) + i, found := findGRC20(pkgPath) if !found { return false @@ -117,3 +137,11 @@ func approveByRegisterCall(pkgPath string, spender std.Address, amount uint64) b return true } + +func handleNative(pkgPath string) string { + if pkgPath == GNOT { + return WRAPPED_WUGNOT + } + + return pkgPath +} diff --git a/staker/_TEST_staker_one_external_native_test.gnoa b/staker/_TEST_staker_one_external_native_test.gnoa new file mode 100644 index 000000000..cde902742 --- /dev/null +++ b/staker/_TEST_staker_one_external_native_test.gnoa @@ -0,0 +1,283 @@ +package staker + +import ( + "std" + "testing" + + "encoding/gjson" + + "gno.land/p/demo/testutils" + + g "gno.land/r/gov" + p "gno.land/r/pool" + pos "gno.land/r/position" + + gnft "gno.land/r/gnft" // GNFT, Gnoswap NFT + gns "gno.land/r/gns" // GNS, Gnoswap Share + + // WUGNOT, Wrapped UGNOT + _ "gno.land/r/grc20_wrapper" +) + +var ( + pc01 = testutils.TestAddress("pc01") // Pool Creator + ci01 = testutils.TestAddress("ci01") // Create Incentive Caller + lp01 = testutils.TestAddress("lp01") // Liquidity Provider 01 + lp02 = testutils.TestAddress("lp02") // Liquidity Provider 02 + + ira = testutils.TestAddress("ira") // Internal Reward Account + + stakerAddr = std.DerivePkgAddr("gno.land/r/staker") +) + +var ( + fooPath = "gno.land/r/foo" + barPath = "gno.land/r/bar" + gnotPath = "gnotPath" +) + +func init() { + // init pool tiers + // tier 1 + poolTiers["gno.land/r/bar:gno.land/r/foo:500"] = 1 // DEV + + // tier 2 + poolTiers["GNS/USDT_500"] = 2 + poolTiers["ATOM/GNS_500"] = 2 + + // tier 3 + poolTiers["ATOM/GNOT_500"] = 3 + poolTiers["ATOM/USDT_500"] = 3 + poolTiers["ATOM/WETH_500"] = 3 + + // // debug addr + // println(pc01, "// pc01") + // println(ci01, "// ci01") + // println(lp01, "// lp01") + // println(lp02, "// lp02") + // println(stakerAddr, "// staker") +} + +func TestPoolInitCreatePool(t *testing.T) { + std.TestSetOrigCaller(pc01) + + p.InitManual() + std.TestSkipHeights(1) + + p.CreatePool(barPath, fooPath, 500, 130621891405341611593710811006) + std.TestSkipHeights(1) +} + +func TestPositionMint(t *testing.T) { + { + std.TestSetOrigCaller(lp01) + tPosTokenId, tPosLiquidity, tPosAmount0, tPosAmount1 := pos.Mint( + barPath, // token0 + fooPath, // token1 + uint16(500), // fee + int32(9000), // tickLower + int32(11000), // tickUpper + bigint(1000), // amount0Desired + bigint(1000), // amount1Desired + bigint(1), // amount0Min + bigint(1), // amount1Min + bigint(2345678901), // deadline + ) + std.TestSkipHeights(1) + + shouldEQ(t, tPosTokenId, 1) + shouldEQ(t, gnft.OwnerOf(tid(tPosTokenId)), GetOrigCaller()) // lp01 + + // approve nft to staker + std.TestSetPrevAddr(lp01) + gnft.Approve(a2u(GetOrigPkgAddr()), tid(tPosTokenId)) + std.TestSkipHeights(1) + } + + { + std.TestSetOrigCaller(lp02) + tPosTokenId, tPosLiquidity, tPosAmount0, tPosAmount1 := pos.Mint( + barPath, // token10 + fooPath, // token1 + uint16(500), // fee + int32(9100), // tickLower + int32(12000), // tickUpper + bigint(5000), // amount0Desired + bigint(5000), // amount1Desired + bigint(1), // amount0Min + bigint(1), // amount1Min + bigint(2345678901), // deadline + ) + std.TestSkipHeights(1) + + shouldEQ(t, tPosTokenId, 2) + shouldEQ(t, gnft.OwnerOf(tid(tPosTokenId)), GetOrigCaller()) // lp02 + + // approve nft to staker + std.TestSetPrevAddr(lp02) + gnft.Approve(a2u(GetOrigPkgAddr()), tid(tPosTokenId)) + std.TestSkipHeights(1) + } +} + +func TestCreateExternalIncentive(t *testing.T) { + std.TestSetOrigCaller(ci01) + + // give ci01 enough ugnot to create incentive + testBanker := std.GetBanker(std.BankerTypeRealmIssue) + testBanker.IssueCoin(std.GetOrigCaller(), "ugnot", 10_000_000_000) + + // send + std.TestSetOrigSend(std.Coins{{"ugnot", 10_000_000_000}}, nil) + testBanker.RemoveCoin(std.GetOrigCaller(), "ugnot", 10_000_000_000) + testBanker.IssueCoin(stakerAddr, "ugnot", 10_000_000_000) + + CreateExternalIncentive( + "gno.land/r/bar:gno.land/r/foo:500", // targetPoolPath + GNOT, // rewardToken + 10_000_000_000, // rewardAmount + GetTimestamp(), // startTimestamp + GetTimestamp()+TIMESTAMP_90DAYS, // endTimestamp + ) + std.TestSkipHeights(1) +} + +func TestStakeToken(t *testing.T) { + { + std.TestSetOrigCaller(lp01) + StakeToken(1) // GNFT tokenId + std.TestSkipHeights(2) + + shouldEQ(t, gnft.OwnerOf(tid(1)), GetOrigPkgAddr()) // staker + shouldEQ(t, len(deposits), 1) + } + + { + std.TestSetOrigCaller(lp02) + StakeToken(2) // GNFT tokenId + std.TestSkipHeights(2) + + shouldEQ(t, gnft.OwnerOf(tid(2)), GetOrigPkgAddr()) // staker + shouldEQ(t, len(deposits), 2) + } +} + +func TestApiGetRewardsByAddress(t *testing.T) { + { + // lp01 reward check + 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(), "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(), "gnot") + shouldEQ(t, jsonStr.Get("response.data.1.reward").Int(), 324) + } + + { + // lp02 reward check + 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(), "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(), "gnot") + shouldEQ(t, jsonStr.Get("response.data.1.reward").Int(), 1797) + } +} + +func TestUnstakeToken(t *testing.T) { + { + std.TestSetOrigCaller(lp01) + UnstakeToken(1) // GNFT tokenId + std.TestSkipHeights(1) + + shouldEQ(t, gnft.OwnerOf(tid(1)), lp01) + + // check reward + shouldEQ(t, gns.BalanceOf(a2u(lp01)), 126) // internal + shouldEQ(t, ugnotBalance(lp01), 324) // external + } + + { + std.TestSetOrigCaller(lp02) + UnstakeToken(2) // GNFT tokenId + std.TestSkipHeights(1) + + shouldEQ(t, gnft.OwnerOf(tid(2)), lp02) + + // check reward + shouldEQ(t, gns.BalanceOf(a2u(lp02)), 825) // internal + shouldEQ(t, ugnotBalance(lp02), 2121) // external + } +} + +func TestEndExternalIncentive(t *testing.T) { + std.TestSetOrigCaller(ci01) + std.TestSkipHeights(9999999) + EndExternalIncentive(GetOrigCaller().String(), "gno.land/r/bar:gno.land/r/foo:500", GNOT) // use same parameter as CreateExternalIncentive() + std.TestSkipHeights(1) + + shouldEQ(t, len(incentives), 0) + shouldEQ(t, len(poolIncentives["gno.land/r/bar:gno.land/r/foo:500"]), 0) +} + +// GOV +func TestSubmitProposalParameterStakingReward(t *testing.T) { + // Init GOV Contract + g.Init() + + id := SubmitProposalParameterStakingReward( + "staking reward change", // title + "change staking rewards", // summary + "", // metadata + 0, // initialDeposit + + 10, // newStakingReward1 + 8, // newStakingReward2 + 6, // newStakingReward3 + 4, // newStakingReward4 + ) + shouldEQ(t, id, uint64(1)) +} + +/* HELPERS */ +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, expected %v", got, expected) + } +} + +func shouldGT(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 shouldPanic(t *testing.T, f func()) { + defer func() { + if r := recover(); r == nil { + t.Errorf("expected panic") + } + }() + f() +} + +func ugnotBalance(addr std.Address) uint64 { + testBanker := std.GetBanker(std.BankerTypeRealmIssue) + return uint64(testBanker.GetCoins(addr)[0].Amount) +} diff --git a/staker/consts.gno b/staker/consts.gno index 40252a6eb..e3766f395 100644 --- a/staker/consts.gno +++ b/staker/consts.gno @@ -15,3 +15,9 @@ const ( TIMESTAMP_180DAYS uint64 = 15552000 TIMESTAMP_360DAYS uint64 = 31104000 ) + +// WRAP & UNWRAP +const ( + GNOT = "gnot" + WRAPPED_WUGNOT = "gno.land/r/wugnot" +) diff --git a/staker/staker.gno b/staker/staker.gno index 0ae39af76..fafbc6e53 100644 --- a/staker/staker.gno +++ b/staker/staker.gno @@ -26,7 +26,7 @@ var ( func init() { // init pool tiers // tier 1 - poolTiers["gno.land/r/bar:gno.land/r/wugnot:100"] = 1 // DEV + poolTiers["gno.land/r/bar:gnot:100"] = 1 // DEV // tier 2 poolTiers["GNS/USDT_500"] = 2 @@ -52,6 +52,9 @@ func CreateExternalIncentive( panic(ufmt.Sprintf("[STAKER] staker.gno__CreateExternalIncentive() || externalDuration(%d) must be 90, 180, 360 days)", externalDuration)) } + // handle native coin(gnot) + wrap(rewardToken, rewardAmount) + fromBalanceBefore := balanceOfByRegisterCall(rewardToken, GetOrigCaller()) require(fromBalanceBefore >= rewardAmount, ufmt.Sprintf("[STAKER] staker.gno__CreateExternalIncentive() || not enough rewardAmount(%d) to create incentive(%d)", fromBalanceBefore, rewardAmount)) @@ -138,6 +141,7 @@ func UnstakeToken( // r3v4_xxx: handle native coin(gnot) reward transferByRegisterCall(incentive.rewardToken, deposit.owner, uint64(externalReward)) + unWrap(incentive.rewardToken, uint64(externalReward)) incentive.rewardAmount -= externalReward incentives[incentiveId] = incentive @@ -170,6 +174,7 @@ func EndExternalIncentive(refundee, targetPoolPath, rewardToken string) { require(poolExternalReward >= refund, ufmt.Sprintf("[STAKER] staker.gno__EndExternalIncentive() || not enough poolExternalReward(%d) to refund(%d)", poolExternalReward, refund)) transferByRegisterCall(incentive.rewardToken, incentive.refundee, uint64(refund)) + unWrap(incentive.rewardToken, uint64(refund)) delete(incentives, incentiveId) for i, v := range poolIncentives[targetPoolPath] { diff --git a/staker/staker_register.gno b/staker/staker_register.gno index e7cb46a91..1d629a96f 100644 --- a/staker/staker_register.gno +++ b/staker/staker_register.gno @@ -23,6 +23,8 @@ type GRC20Pair struct { } func findGRC20(pkgPath string) (int, bool) { + pkgPath = handleNative(pkgPath) + for i, pair := range registered { if pair.pkgPath == pkgPath { return i, true @@ -33,10 +35,14 @@ func findGRC20(pkgPath string) (int, bool) { } func appendGRC20Interface(pkgPath string, igrc20 GRC20Interface) { + pkgPath = handleNative(pkgPath) + registered = append(registered, GRC20Pair{pkgPath: pkgPath, igrc20: igrc20}) } func removeGRC20Interface(pkgPath string) { + pkgPath = handleNative(pkgPath) + i, found := findGRC20(pkgPath) if !found { return @@ -55,6 +61,8 @@ func RegisterGRC20Interface(pkgPath string, igrc20 GRC20Interface) { // panic("unauthorized address to register") // } + pkgPath = handleNative(pkgPath) + _, found := findGRC20(pkgPath) if !found { appendGRC20Interface(pkgPath, igrc20) @@ -62,6 +70,8 @@ func RegisterGRC20Interface(pkgPath string, igrc20 GRC20Interface) { } func UnregisterGRC20Interface(pkgPath string) { + pkgPath = handleNative(pkgPath) + // do not allow realm to unregister std.AssertOriginCall() @@ -78,6 +88,8 @@ func UnregisterGRC20Interface(pkgPath string) { } func transferByRegisterCall(pkgPath string, to std.Address, amount uint64) bool { + pkgPath = handleNative(pkgPath) + i, found := findGRC20(pkgPath) if !found { return false @@ -89,6 +101,8 @@ func transferByRegisterCall(pkgPath string, to std.Address, amount uint64) bool } func transferFromByRegisterCall(pkgPath string, from, to std.Address, amount uint64) bool { + pkgPath = handleNative(pkgPath) + i, found := findGRC20(pkgPath) if !found { return false @@ -100,6 +114,8 @@ func transferFromByRegisterCall(pkgPath string, from, to std.Address, amount uin } func balanceOfByRegisterCall(pkgPath string, owner std.Address) uint64 { + pkgPath = handleNative(pkgPath) + i, found := findGRC20(pkgPath) if !found { return 0 @@ -110,6 +126,8 @@ func balanceOfByRegisterCall(pkgPath string, owner std.Address) uint64 { } func approveByRegisterCall(pkgPath string, spender std.Address, amount uint64) bool { + pkgPath = handleNative(pkgPath) + i, found := findGRC20(pkgPath) if !found { return false @@ -119,3 +137,11 @@ func approveByRegisterCall(pkgPath string, spender std.Address, amount uint64) b return true } + +func handleNative(pkgPath string) string { + if pkgPath == GNOT { + return WRAPPED_WUGNOT + } + + return pkgPath +} diff --git a/staker/wrap_unwrap.gno b/staker/wrap_unwrap.gno new file mode 100644 index 000000000..7bd74bee6 --- /dev/null +++ b/staker/wrap_unwrap.gno @@ -0,0 +1,54 @@ +package staker + +import ( + "std" + + "gno.land/p/demo/ufmt" + "gno.land/r/wugnot" +) + +func wrap(tokenPath string, amount uint64) { + if tokenPath != GNOT { + return + } + + caller := std.GetOrigCaller() + sentCoins := std.GetOrigSend() + + if len(sentCoins) != 1 { + panic(ufmt.Sprintf("[STAKER] wrap_unwrap.gno__wrap() || NEED TO SEND ONLY ONE COIN, BUT %d SENT", len(sentCoins))) + } + sentCoin := sentCoins[0] + + if sentCoin.Denom != "ugnot" { + panic(ufmt.Sprintf("[STAKER] wrap_unwrap.gno__wrap() || SEND ONLY UGNOT, BUT %s SENT", sentCoin.Denom)) + } + + if sentCoin.Amount != amount { + panic(ufmt.Sprintf("[STAKER] wrap_unwrap.gno__wrap() || COIN AMOUNT(%d) != REWARD AMOUNT(%d)", sentCoin.Amount, amount)) + } + + // println("[DEBUG__STAKER] wrap_unwrap.gno__wrap() || WRAP AMOUNT:", sentCoin.Amount) + wugnot.Wrap(a2u(caller), uint64(sentCoin.Amount)) +} + +func unWrap(tokenPath string, amount uint64) { + if tokenPath != GNOT { + return + } + + caller := std.GetOrigCaller() + + refund := std.Coins{ + std.Coin{ + Denom: "ugnot", + Amount: int64(amount), + }, + } + + // println("[DEBUG__STAKER] wrap_unwrap.gno__unWrap() || UNWRAP AMOUNT:", amount) + wugnot.Unwrap(a2u(caller), amount) + + banker := std.GetBanker(std.BankerTypeRealmSend) + banker.SendCoins(std.GetOrigPkgAddr(), caller, refund) +}