diff --git a/pool/_TEST_INIT_register_tokens_test.gno b/pool/_TEST_INIT_register_tokens_test.gno index 791697e8..87a7b1c2 100644 --- a/pool/_TEST_INIT_register_tokens_test.gno +++ b/pool/_TEST_INIT_register_tokens_test.gno @@ -1,6 +1,11 @@ package pool import ( + "std" + "testing" + + "gno.land/p/demo/testutils" + "gno.land/r/demo/foo" "gno.land/r/demo/bar" @@ -15,6 +20,8 @@ import ( "gno.land/r/demo/gns" + "gno.land/r/demo/consts" + "gno.land/r/demo/users" ) @@ -127,6 +134,8 @@ func (GNSToken) Approve() func(spender users.AddressOrName, amount uint64) { } func init() { + std.TestSetOrigCaller(consts.GNOSWAP_ADMIN) + RegisterGRC20Interface("gno.land/r/demo/bar", BarToken{}) RegisterGRC20Interface("gno.land/r/demo/foo", FooToken{}) RegisterGRC20Interface("gno.land/r/demo/baz", BazToken{}) @@ -135,3 +144,35 @@ func init() { RegisterGRC20Interface("gno.land/r/demo/obl", OBLToken{}) RegisterGRC20Interface("gno.land/r/demo/gns", GNSToken{}) } + +func TestGetRegisteredTokens(t *testing.T) { + shouldEQ(t, len(GetRegisteredTokens()), 7) +} + +func TestRegisterGRC20Interface(t *testing.T) { + shouldPanic(t, + func() { + RegisterGRC20Interface("gno.land/r/demo/bar", BarToken{}) + }, + ) +} + +func TestUnregisterGRC20Interface(t *testing.T) { + dummy := testutils.TestAddress("dummy") + std.TestSetOrigCaller(dummy) + + shouldPanic(t, + func() { + UnregisterGRC20Interface("gno.land/r/demo/bar") + }, + ) + + shouldEQ(t, len(GetRegisteredTokens()), 7) + + std.TestSetOrigCaller(consts.GNOSWAP_ADMIN) + UnregisterGRC20Interface("gno.land/r/demo/bar") + shouldEQ(t, len(GetRegisteredTokens()), 6) + + // re-register to avoid panic in other tests + RegisterGRC20Interface("gno.land/r/demo/bar", BarToken{}) +} diff --git a/pool/pool_register.gno b/pool/pool_register.gno index d157a6eb..813f3552 100644 --- a/pool/pool_register.gno +++ b/pool/pool_register.gno @@ -9,10 +9,6 @@ import ( "gno.land/r/demo/consts" ) -var registered = []GRC20Pair{} - -var locked bool // mutex flag - type GRC20Interface interface { Transfer() func(to users.AddressOrName, amount uint64) TransferFrom() func(from, to users.AddressOrName, amount uint64) @@ -20,88 +16,61 @@ type GRC20Interface interface { Approve() func(spender users.AddressOrName, amount uint64) } -type GRC20Pair struct { - pkgPath string - igrc20 GRC20Interface -} - -func findGRC20(pkgPath string) (int, bool) { - pkgPath = handleNative(pkgPath) - - for i, pair := range registered { - if pair.pkgPath == pkgPath { - return i, true - } - } - - return -1, false -} - -func appendGRC20Interface(pkgPath string, igrc20 GRC20Interface) { - pkgPath = handleNative(pkgPath) - - registered = append(registered, GRC20Pair{pkgPath: pkgPath, igrc20: igrc20}) -} - -func removeGRC20Interface(pkgPath string) { - pkgPath = handleNative(pkgPath) +var ( + registered = make(map[string]GRC20Interface) + locked = false // mutex +) - i, found := findGRC20(pkgPath) - if !found { - return +func GetRegisteredTokens() []string { + tokens := make([]string, 0, len(registered)) + for k := range registered { + tokens = append(tokens, k) } - - registered = append(registered[:i], registered[i+1:]...) + return tokens } func RegisterGRC20Interface(pkgPath string, igrc20 GRC20Interface) { - // only admin can register - // r3v4_xxx: below logic can't be used in test case - // r3v4_xxx: however must be used in production - - // caller := std.GetOrigCaller() - // if caller != consts.GNOSWAP_ADMIN { - // panic("unauthorized address to register") - // } + caller := std.GetOrigCaller() + if caller != consts.GNOSWAP_ADMIN { + panic(ufmt.Sprintf("[POOL] pool_register.gno__RegisterGRC20Interface() || unauthorized address(%s) to register", caller.String())) + } pkgPath = handleNative(pkgPath) - _, found := findGRC20(pkgPath) - if !found { - appendGRC20Interface(pkgPath, igrc20) + _, found := registered[pkgPath] + if found { + panic(ufmt.Sprintf("[POOL] pool_register.gno__RegisterGRC20Interface() || pkgPath(%s) already registered", pkgPath)) } + + registered[pkgPath] = igrc20 } func UnregisterGRC20Interface(pkgPath string) { - pkgPath = handleNative(pkgPath) - - // do not allow realm to unregister - std.AssertOriginCall() - // only admin can unregister caller := std.GetOrigCaller() if caller != consts.GNOSWAP_ADMIN { panic(ufmt.Sprintf("[POOL] pool_register.gno__UnregisterGRC20Interface() || unauthorized address(%s) to unregister", caller.String())) } - _, found := findGRC20(pkgPath) + pkgPath = handleNative(pkgPath) + + _, found := registered[pkgPath] if found { - removeGRC20Interface(pkgPath) + delete(registered, pkgPath) } } func transferByRegisterCall(pkgPath string, to std.Address, amount uint64) bool { pkgPath = handleNative(pkgPath) - i, found := findGRC20(pkgPath) + _, found := registered[pkgPath] if !found { panic(ufmt.Sprintf("[POOL] pool_register.gno__transferByRegisterCall() || pkgPath(%s) not found", pkgPath)) - // return false } if !locked { locked = true - registered[i].igrc20.Transfer()(users.AddressOrName(to), amount) + registered[pkgPath].Transfer()(users.AddressOrName(to), amount) defer func() { locked = false @@ -115,15 +84,14 @@ 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) + _, found := registered[pkgPath] if !found { panic(ufmt.Sprintf("[POOL] pool_register.gno__transferFromByRegisterCall() || pkgPath(%s) not found", pkgPath)) - // return false } if !locked { locked = true - registered[i].igrc20.TransferFrom()(users.AddressOrName(from), users.AddressOrName(to), amount) + registered[pkgPath].TransferFrom()(users.AddressOrName(from), users.AddressOrName(to), amount) defer func() { locked = false @@ -137,26 +105,24 @@ 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) + _, found := registered[pkgPath] if !found { panic(ufmt.Sprintf("[POOL] pool_register.gno__balanceOfByRegisterCall() || pkgPath(%s) not found", pkgPath)) - // return 0 } - balance := registered[i].igrc20.BalanceOf()(users.AddressOrName(owner)) + balance := registered[pkgPath].BalanceOf()(users.AddressOrName(owner)) return balance } func approveByRegisterCall(pkgPath string, spender std.Address, amount uint64) bool { pkgPath = handleNative(pkgPath) - i, found := findGRC20(pkgPath) + _, found := registered[pkgPath] if !found { panic(ufmt.Sprintf("[POOL] pool_register.gno__approveByRegisterCall() || pkgPath(%s) not found", pkgPath)) - // return false } - registered[i].igrc20.Approve()(users.AddressOrName(spender), amount) + registered[pkgPath].Approve()(users.AddressOrName(spender), amount) return true } diff --git a/position/_TEST_INIT_register_tokens_test.gno b/position/_TEST_INIT_register_tokens_test.gno index 48ff455e..a5e471c1 100644 --- a/position/_TEST_INIT_register_tokens_test.gno +++ b/position/_TEST_INIT_register_tokens_test.gno @@ -1,6 +1,8 @@ package position import ( + "std" + "gno.land/r/demo/foo" "gno.land/r/demo/bar" @@ -15,6 +17,7 @@ import ( "gno.land/r/demo/gns" + "gno.land/r/demo/consts" "gno.land/r/demo/users" pl "gno.land/r/demo/pool" // only for position `testcase`` to call grc20 by register @@ -129,6 +132,8 @@ func (GNSToken) Approve() func(spender users.AddressOrName, amount uint64) { } func init() { + std.TestSetOrigCaller(consts.GNOSWAP_ADMIN) + pl.RegisterGRC20Interface("gno.land/r/demo/bar", BarToken{}) pl.RegisterGRC20Interface("gno.land/r/demo/foo", FooToken{}) pl.RegisterGRC20Interface("gno.land/r/demo/baz", BazToken{}) diff --git a/position/_TEST_position_test.gno b/position/_TEST_position_test.gnoa similarity index 82% rename from position/_TEST_position_test.gno rename to position/_TEST_position_test.gnoa index d5046e88..b516c1cd 100644 --- a/position/_TEST_position_test.gno +++ b/position/_TEST_position_test.gnoa @@ -6,6 +6,7 @@ import ( "testing" "gno.land/p/demo/common" + "gno.land/r/demo/consts" pl "gno.land/r/demo/pool" @@ -95,6 +96,30 @@ func TestCollectFeeBeforeSwap(t *testing.T) { shouldEQ(t, poolPath, "gno.land/r/demo/bar:gno.land/r/demo/foo:500") } +func TestSwap(t *testing.T) { + std.TestSetPrevRealm(consts.ROUTER_PATH) + std.TestSetOrigCaller(test1) + amount0, amount1 := pl.Swap( + barPath, + fooPath, + fee500, + test1, + true, + bigint(1_234_567), + consts.MIN_PRICE, + test1, + ) +} + +func TestCollectFeeAfterSwap(t *testing.T) { + tokenId, fee0, fee1, poolPath := CollectFee(1) + shouldEQ(t, tokenId, uint64(1)) + shouldNEQ(t, fee0, bigint(0)) // this is input token + shouldEQ(t, fee1, bigint(0)) // this it output token + shouldEQ(t, poolPath, "gno.land/r/demo/bar:gno.land/r/demo/foo:500") + +} + func TestBurnUpperPosition(t *testing.T) { std.TestSetOrigCaller(test1) diff --git a/position/_TEST_position_test_two_position_used_single_swap_test.gno b/position/_TEST_position_test_two_position_used_single_swap_test.gno new file mode 100644 index 00000000..eae86d8b --- /dev/null +++ b/position/_TEST_position_test_two_position_used_single_swap_test.gno @@ -0,0 +1,116 @@ +package position + +import ( + "std" + "testing" + + "gno.land/p/demo/common" + "gno.land/r/demo/consts" + + pl "gno.land/r/demo/pool" +) + +// 1. Init & Create Pool +func TestPoolInitCreatePool(t *testing.T) { + std.TestSetOrigCaller(test1) + pl.InitManual() + + std.TestSetOrigCaller(test1) + pl.CreatePool(barPath, fooPath, fee500, common.TickMathGetSqrtRatioAtTick(10000)) // x2.71814592682522526700950038502924144268035888671875 + + // sqrtPrice + shouldPanic(t, func() { pl.CreatePool(fooPath, barPath, fee500, common.TickMathGetSqrtRatioAtTick(10000)) }) +} + +func TestMintPosition01WideInRange(t *testing.T) { + std.TestSetOrigCaller(test1) + + tokenId, liquidity, amount0, amount1 := Mint( + barPath, + fooPath, + fee500, + 8000, + 12000, + bigint(50000000), + bigint(50000000), + bigint(0), + bigint(0), + max_timeout, + ) + shouldEQ(t, tokenId, 1) + shouldEQ(t, getNextId(), 2) + shouldEQ(t, liquidity, bigint(318704394)) + shouldEQ(t, amount0, bigint(18394891)) + shouldEQ(t, amount1, bigint(50000000)) +} + +func TestMintPositionTightInRange(t *testing.T) { + std.TestSetOrigCaller(test1) + + tokenId, liquidity, amount0, amount1 := Mint( + barPath, + fooPath, + fee500, + 9500, + 10500, + bigint(50000000), + bigint(50000000), + bigint(0), + bigint(0), + max_timeout, + ) + shouldEQ(t, tokenId, 2) + shouldEQ(t, getNextId(), 3) + shouldEQ(t, liquidity, bigint(1228379123)) + shouldEQ(t, amount0, bigint(18394891)) + shouldEQ(t, amount1, bigint(50000000)) +} + +func TestCollectFeeBeforeSwapPos1(t *testing.T) { + tokenId, fee0, fee1, poolPath := CollectFee(1) + shouldEQ(t, tokenId, uint64(1)) + shouldEQ(t, fee0, bigint(0)) + shouldEQ(t, fee1, bigint(0)) + shouldEQ(t, poolPath, "gno.land/r/demo/bar:gno.land/r/demo/foo:500") +} + +func TestCollectFeeBeforeSwapPos2(t *testing.T) { + tokenId, fee0, fee1, poolPath := CollectFee(2) + shouldEQ(t, tokenId, uint64(2)) + shouldEQ(t, fee0, bigint(0)) + shouldEQ(t, fee1, bigint(0)) + shouldEQ(t, poolPath, "gno.land/r/demo/bar:gno.land/r/demo/foo:500") +} + +func TestSwap(t *testing.T) { + std.TestSetPrevRealm(consts.ROUTER_PATH) + std.TestSetOrigCaller(test1) + amount0, amount1 := pl.Swap( + barPath, + fooPath, + fee500, + test1, + true, + bigint(1_234_567), + consts.MIN_PRICE, + test1, + ) +} + +// IF POSITION 2 DOESN'T EXIST, POSITION 1 WILL EARN '617' as fee +func TestCollectFeeAfterSwapPos1(t *testing.T) { + tokenId, fee0, fee1, poolPath := CollectFee(1) + shouldEQ(t, tokenId, uint64(1)) + // shouldEQ(t, fee0, bigint(617)) + shouldEQ(t, fee0, bigint(127)) + shouldEQ(t, fee1, bigint(0)) + shouldEQ(t, poolPath, "gno.land/r/demo/bar:gno.land/r/demo/foo:500") +} + +func TestCollectFeeAfterSwapPos2(t *testing.T) { + tokenId, fee0, fee1, poolPath := CollectFee(2) + shouldEQ(t, tokenId, uint64(2)) + shouldEQ(t, fee0, bigint(490)) + shouldEQ(t, fee1, bigint(0)) + shouldEQ(t, poolPath, "gno.land/r/demo/bar:gno.land/r/demo/foo:500") +} diff --git a/position/position.gno b/position/position.gno index 0940db3c..a271910b 100644 --- a/position/position.gno +++ b/position/position.gno @@ -247,7 +247,7 @@ func CollectFee(tokenId uint64) (uint64, bigint, bigint, string) { // tokenId, t position := positions[tokenId] token0, token1, fee := poolKeyDivide(position.poolKey) - burnedAmount0, burnedAmount1 := pl.Burn( + pl.Burn( token0, token1, fee, @@ -256,9 +256,8 @@ func CollectFee(tokenId uint64) (uint64, bigint, bigint, string) { // tokenId, t 0, // burn '0' liquidity to collect fee ) - tokensOwed0, tokensOwed1 := position.tokensOwed0, position.tokensOwed1 - // r3v4_xxx: DOES THIS NEED FOR COLLECT FEE + // tokensOwed0, tokensOwed1 := position.tokensOwed0, position.tokensOwed1 // positionKey := positionKeyCompute(GetOrigPkgAddr(), position.tickLower, position.tickUpper) // pool := pl.GetPoolFromPoolPath(position.poolKey) // feeGrowthInside0LastX128, feeGrowthInside1LastX128 := pool.PoolGetPositionFeeGrowthInside0LastX128(positionKey), pool.PoolGetPositionFeeGrowthInside1LastX128(positionKey) @@ -276,13 +275,13 @@ func CollectFee(tokenId uint64) (uint64, bigint, bigint, string) { // tokenId, t GetOrigCaller(), position.tickLower, position.tickUpper, - burnedAmount0, - burnedAmount1, + consts.MAX_UINT64, // r3v4_xxx: current grc20 handles amount by `unit64` + consts.MAX_UINT64, // r3v4_xxx: current grc20 handles amount by `unit64` ) // r3v4_xxx: DOES THIS NEED FOR COLLECT FEE - position.tokensOwed0, position.tokensOwed1 = tokensOwed0-amount0, tokensOwed1-amount1 - positions[tokenId] = position + // position.tokensOwed0, position.tokensOwed1 = tokensOwed0-amount0, tokensOwed1-amount1 + // positions[tokenId] = position return tokenId, amount0, amount1, position.poolKey } diff --git a/router/_TEST_INIT_register_tokens_test.gno b/router/_TEST_INIT_register_tokens_test.gno index a444627c..715304b1 100644 --- a/router/_TEST_INIT_register_tokens_test.gno +++ b/router/_TEST_INIT_register_tokens_test.gno @@ -1,6 +1,8 @@ package router import ( + "std" + "gno.land/r/demo/foo" "gno.land/r/demo/bar" @@ -9,19 +11,16 @@ import ( "gno.land/r/demo/qux" - "gno.land/r/demo/fred" - - "gno.land/r/demo/thud" - "gno.land/r/demo/wugnot" "gno.land/r/demo/obl" "gno.land/r/demo/gns" + "gno.land/r/demo/consts" "gno.land/r/demo/users" - "gno.land/r/demo/pool" // only for position `testcase`` to call grc20 by register + pl "gno.land/r/demo/pool" // only for position `testcase`` to call grc20 by register ) type FooToken struct{} @@ -84,36 +83,6 @@ func (QuxToken) Approve() func(spender users.AddressOrName, amount uint64) { return qux.Approve } -type FredToken struct{} - -func (FredToken) Transfer() func(to users.AddressOrName, amount uint64) { - return fred.Transfer -} -func (FredToken) TransferFrom() func(from, to users.AddressOrName, amount uint64) { - return fred.TransferFrom -} -func (FredToken) BalanceOf() func(owner users.AddressOrName) uint64 { - return fred.BalanceOf -} -func (FredToken) Approve() func(spender users.AddressOrName, amount uint64) { - return fred.Approve -} - -type ThudToken struct{} - -func (ThudToken) Transfer() func(to users.AddressOrName, amount uint64) { - return thud.Transfer -} -func (ThudToken) TransferFrom() func(from, to users.AddressOrName, amount uint64) { - return thud.TransferFrom -} -func (ThudToken) BalanceOf() func(owner users.AddressOrName) uint64 { - return thud.BalanceOf -} -func (ThudToken) Approve() func(spender users.AddressOrName, amount uint64) { - return thud.Approve -} - type WugnotToken struct{} func (WugnotToken) Transfer() func(to users.AddressOrName, amount uint64) { @@ -163,24 +132,22 @@ func (GNSToken) Approve() func(spender users.AddressOrName, amount uint64) { } func init() { + std.TestSetOrigCaller(consts.GNOSWAP_ADMIN) + // POOL - pool.RegisterGRC20Interface("gno.land/r/demo/bar", BarToken{}) - pool.RegisterGRC20Interface("gno.land/r/demo/foo", FooToken{}) - pool.RegisterGRC20Interface("gno.land/r/demo/baz", BazToken{}) - pool.RegisterGRC20Interface("gno.land/r/demo/qux", QuxToken{}) - pool.RegisterGRC20Interface("gno.land/r/demo/fred", FredToken{}) - pool.RegisterGRC20Interface("gno.land/r/demo/thud", ThudToken{}) - pool.RegisterGRC20Interface("gno.land/r/demo/wugnot", WugnotToken{}) - pool.RegisterGRC20Interface("gno.land/r/demo/obl", OBLToken{}) - pool.RegisterGRC20Interface("gno.land/r/demo/gns", GNSToken{}) + pl.RegisterGRC20Interface("gno.land/r/demo/bar", BarToken{}) + pl.RegisterGRC20Interface("gno.land/r/demo/foo", FooToken{}) + pl.RegisterGRC20Interface("gno.land/r/demo/baz", BazToken{}) + pl.RegisterGRC20Interface("gno.land/r/demo/qux", QuxToken{}) + pl.RegisterGRC20Interface("gno.land/r/demo/wugnot", WugnotToken{}) + pl.RegisterGRC20Interface("gno.land/r/demo/obl", OBLToken{}) + pl.RegisterGRC20Interface("gno.land/r/demo/gns", GNSToken{}) // ROUTER RegisterGRC20Interface("gno.land/r/demo/bar", BarToken{}) RegisterGRC20Interface("gno.land/r/demo/foo", FooToken{}) RegisterGRC20Interface("gno.land/r/demo/baz", BazToken{}) RegisterGRC20Interface("gno.land/r/demo/qux", QuxToken{}) - RegisterGRC20Interface("gno.land/r/demo/fred", FredToken{}) - RegisterGRC20Interface("gno.land/r/demo/thud", ThudToken{}) RegisterGRC20Interface("gno.land/r/demo/wugnot", WugnotToken{}) RegisterGRC20Interface("gno.land/r/demo/obl", OBLToken{}) RegisterGRC20Interface("gno.land/r/demo/gns", GNSToken{}) diff --git a/router/router_register.gno b/router/router_register.gno index b3db724c..8f448677 100644 --- a/router/router_register.gno +++ b/router/router_register.gno @@ -3,11 +3,11 @@ package router import ( "std" - "gno.land/r/demo/consts" + "gno.land/p/demo/ufmt" "gno.land/r/demo/users" -) -var registered = []GRC20Pair{} + "gno.land/r/demo/consts" +) type GRC20Interface interface { Transfer() func(to users.AddressOrName, amount uint64) @@ -16,105 +16,121 @@ type GRC20Interface interface { 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}) -} +var ( + registered = make(map[string]GRC20Interface) + locked = false // mutex +) -func removeGRC20Interface(pkgPath string) { - i, found := findGRC20(pkgPath) - if !found { - return +func GetRegisteredTokens() []string { + tokens := make([]string, 0, len(registered)) + for k := range registered { + tokens = append(tokens, k) } - - registered = append(registered[:i], registered[i+1:]...) + return tokens } func RegisterGRC20Interface(pkgPath string, igrc20 GRC20Interface) { - // only admin can register - // r3v4_xxx: below logic can't be used in test case - // r3v4_xxx: however must be used in production + caller := std.GetOrigCaller() + if caller != consts.GNOSWAP_ADMIN { + panic(ufmt.Sprintf("[ROUTER] router_register.gno__RegisterGRC20Interface() || unauthorized address(%s) to register", caller.String())) + } - // caller := std.GetOrigCaller() - // if caller != consts.GNOSWAP_ADMIN { - // panic("unauthorized address to register") - // } + pkgPath = handleNative(pkgPath) - _, found := findGRC20(pkgPath) - if !found { - appendGRC20Interface(pkgPath, igrc20) + _, found := registered[pkgPath] + if found { + panic(ufmt.Sprintf("[ROUTER] router_register.gno__RegisterGRC20Interface() || pkgPath(%s) already registered", pkgPath)) } + + registered[pkgPath] = igrc20 } func UnregisterGRC20Interface(pkgPath string) { - // do not allow realm to unregister - std.AssertOriginCall() - // only admin can unregister caller := std.GetOrigCaller() if caller != consts.GNOSWAP_ADMIN { - panic("unauthorized address to unregister") + panic(ufmt.Sprintf("[ROUTER] router_register.gno__UnregisterGRC20Interface() || unauthorized address(%s) to unregister", caller.String())) } - _, found := findGRC20(pkgPath) + pkgPath = handleNative(pkgPath) + + _, found := registered[pkgPath] if found { - removeGRC20Interface(pkgPath) + delete(registered, pkgPath) } } func transferByRegisterCall(pkgPath string, to std.Address, amount uint64) bool { - i, found := findGRC20(pkgPath) + pkgPath = handleNative(pkgPath) + + _, found := registered[pkgPath] if !found { - return false + panic(ufmt.Sprintf("[ROUTER] router_register.gno__transferByRegisterCall() || pkgPath(%s) not found", pkgPath)) } - registered[i].igrc20.Transfer()(users.AddressOrName(to), amount) + if !locked { + locked = true + registered[pkgPath].Transfer()(users.AddressOrName(to), amount) + defer func() { + locked = false + }() + } else { + panic("[POOl] router_register.gno__transferByRegisterCall() || expected locked to be false") + } return true } func transferFromByRegisterCall(pkgPath string, from, to std.Address, amount uint64) bool { - i, found := findGRC20(pkgPath) + pkgPath = handleNative(pkgPath) + + _, found := registered[pkgPath] if !found { - return false + panic(ufmt.Sprintf("[ROUTER] router_register.gno__transferFromByRegisterCall() || pkgPath(%s) not found", pkgPath)) } - registered[i].igrc20.TransferFrom()(users.AddressOrName(from), users.AddressOrName(to), amount) + if !locked { + locked = true + registered[pkgPath].TransferFrom()(users.AddressOrName(from), users.AddressOrName(to), amount) + defer func() { + locked = false + }() + } else { + panic("[POOl] router_register.gno__transferFromByRegisterCall() || expected locked to be false") + } return true } func balanceOfByRegisterCall(pkgPath string, owner std.Address) uint64 { - i, found := findGRC20(pkgPath) + pkgPath = handleNative(pkgPath) + + _, found := registered[pkgPath] if !found { - return 0 + panic(ufmt.Sprintf("[ROUTER] router_register.gno__balanceOfByRegisterCall() || pkgPath(%s) not found", pkgPath)) } - balance := registered[i].igrc20.BalanceOf()(users.AddressOrName(owner)) + balance := registered[pkgPath].BalanceOf()(users.AddressOrName(owner)) return balance } func approveByRegisterCall(pkgPath string, spender std.Address, amount uint64) bool { - i, found := findGRC20(pkgPath) + pkgPath = handleNative(pkgPath) + + _, found := registered[pkgPath] if !found { - return false + panic(ufmt.Sprintf("[ROUTER] router_register.gno__approveByRegisterCall() || pkgPath(%s) not found", pkgPath)) } - registered[i].igrc20.Approve()(users.AddressOrName(spender), amount) + registered[pkgPath].Approve()(users.AddressOrName(spender), amount) return true } + +func handleNative(pkgPath string) string { + if pkgPath == consts.GNOT { + return consts.WRAPPED_WUGNOT + } + + return pkgPath +} diff --git a/staker/_TEST_INIT_register_tokens_test.gno b/staker/_TEST_INIT_register_tokens_test.gno index bc887c8a..c743ad42 100644 --- a/staker/_TEST_INIT_register_tokens_test.gno +++ b/staker/_TEST_INIT_register_tokens_test.gno @@ -1,6 +1,8 @@ package staker import ( + "std" + "gno.land/r/demo/foo" "gno.land/r/demo/bar" @@ -15,9 +17,10 @@ import ( "gno.land/r/demo/gns" + "gno.land/r/demo/consts" "gno.land/r/demo/users" - "gno.land/r/demo/pool" // only for position `testcase`` to call grc20 by register + pl "gno.land/r/demo/pool" // only for position `testcase`` to call grc20 by register ) type FooToken struct{} @@ -129,14 +132,16 @@ func (GNSToken) Approve() func(spender users.AddressOrName, amount uint64) { } func init() { + std.TestSetOrigCaller(consts.GNOSWAP_ADMIN) + // POOL - pool.RegisterGRC20Interface("gno.land/r/demo/bar", BarToken{}) - pool.RegisterGRC20Interface("gno.land/r/demo/foo", FooToken{}) - pool.RegisterGRC20Interface("gno.land/r/demo/baz", BazToken{}) - pool.RegisterGRC20Interface("gno.land/r/demo/qux", QuxToken{}) - pool.RegisterGRC20Interface("gno.land/r/demo/wugnot", WugnotToken{}) - pool.RegisterGRC20Interface("gno.land/r/demo/obl", OBLToken{}) - pool.RegisterGRC20Interface("gno.land/r/demo/gns", GNSToken{}) + pl.RegisterGRC20Interface("gno.land/r/demo/bar", BarToken{}) + pl.RegisterGRC20Interface("gno.land/r/demo/foo", FooToken{}) + pl.RegisterGRC20Interface("gno.land/r/demo/baz", BazToken{}) + pl.RegisterGRC20Interface("gno.land/r/demo/qux", QuxToken{}) + pl.RegisterGRC20Interface("gno.land/r/demo/wugnot", WugnotToken{}) + pl.RegisterGRC20Interface("gno.land/r/demo/obl", OBLToken{}) + pl.RegisterGRC20Interface("gno.land/r/demo/gns", GNSToken{}) // STAKER RegisterGRC20Interface("gno.land/r/demo/bar", BarToken{}) diff --git a/staker/staker_register.gno b/staker/staker_register.gno index 22937a77..b461f1d2 100644 --- a/staker/staker_register.gno +++ b/staker/staker_register.gno @@ -3,15 +3,12 @@ package staker import ( "std" - "gno.land/r/demo/users" - "gno.land/p/demo/ufmt" + "gno.land/r/demo/users" "gno.land/r/demo/consts" ) -var registered = []GRC20Pair{} - type GRC20Interface interface { Transfer() func(to users.AddressOrName, amount uint64) TransferFrom() func(from, to users.AddressOrName, amount uint64) @@ -19,109 +16,121 @@ type GRC20Interface interface { 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}) -} +var ( + registered = make(map[string]GRC20Interface) + locked = false // mutex +) -func removeGRC20Interface(pkgPath string) { - i, found := findGRC20(pkgPath) - if !found { - return +func GetRegisteredTokens() []string { + tokens := make([]string, 0, len(registered)) + for k := range registered { + tokens = append(tokens, k) } - - registered = append(registered[:i], registered[i+1:]...) + return tokens } func RegisterGRC20Interface(pkgPath string, igrc20 GRC20Interface) { - // only admin can register - // r3v4_xxx: below logic can't be used in test case - // r3v4_xxx: however must be used in production + caller := std.GetOrigCaller() + if caller != consts.GNOSWAP_ADMIN { + panic(ufmt.Sprintf("[STAKER] staker_register.gno__RegisterGRC20Interface() || unauthorized address(%s) to register", caller.String())) + } - // caller := std.GetOrigCaller() - // if caller != consts.GNOSWAP_ADMIN { - // panic("unauthorized address to register") - // } + pkgPath = handleNative(pkgPath) - _, found := findGRC20(pkgPath) - if !found { - appendGRC20Interface(pkgPath, igrc20) + _, found := registered[pkgPath] + if found { + panic(ufmt.Sprintf("[STAKER] staker_register.gno__RegisterGRC20Interface() || pkgPath(%s) already registered", pkgPath)) } + + registered[pkgPath] = igrc20 } func UnregisterGRC20Interface(pkgPath string) { - // do not allow realm to unregister - std.AssertOriginCall() - // only admin can unregister caller := std.GetOrigCaller() if caller != consts.GNOSWAP_ADMIN { - panic("unauthorized address to unregister") + panic(ufmt.Sprintf("[STAKER] staker_register.gno__UnregisterGRC20Interface() || unauthorized address(%s) to unregister", caller.String())) } - _, found := findGRC20(pkgPath) + pkgPath = handleNative(pkgPath) + + _, found := registered[pkgPath] if found { - removeGRC20Interface(pkgPath) + delete(registered, pkgPath) } } func transferByRegisterCall(pkgPath string, to std.Address, amount uint64) bool { - i, found := findGRC20(pkgPath) + pkgPath = handleNative(pkgPath) + + _, found := registered[pkgPath] if !found { panic(ufmt.Sprintf("[STAKER] staker_register.gno__transferByRegisterCall() || pkgPath(%s) not found", pkgPath)) - // return false } - registered[i].igrc20.Transfer()(users.AddressOrName(to), amount) + if !locked { + locked = true + registered[pkgPath].Transfer()(users.AddressOrName(to), amount) + defer func() { + locked = false + }() + } else { + panic("[POOl] staker_register.gno__transferByRegisterCall() || expected locked to be false") + } return true } func transferFromByRegisterCall(pkgPath string, from, to std.Address, amount uint64) bool { - i, found := findGRC20(pkgPath) + pkgPath = handleNative(pkgPath) + + _, found := registered[pkgPath] if !found { panic(ufmt.Sprintf("[STAKER] staker_register.gno__transferFromByRegisterCall() || pkgPath(%s) not found", pkgPath)) - // return false } - registered[i].igrc20.TransferFrom()(users.AddressOrName(from), users.AddressOrName(to), amount) + if !locked { + locked = true + registered[pkgPath].TransferFrom()(users.AddressOrName(from), users.AddressOrName(to), amount) + defer func() { + locked = false + }() + } else { + panic("[POOl] staker_register.gno__transferFromByRegisterCall() || expected locked to be false") + } return true } func balanceOfByRegisterCall(pkgPath string, owner std.Address) uint64 { - i, found := findGRC20(pkgPath) + pkgPath = handleNative(pkgPath) + + _, found := registered[pkgPath] if !found { panic(ufmt.Sprintf("[STAKER] staker_register.gno__balanceOfByRegisterCall() || pkgPath(%s) not found", pkgPath)) - // return false } - balance := registered[i].igrc20.BalanceOf()(users.AddressOrName(owner)) + balance := registered[pkgPath].BalanceOf()(users.AddressOrName(owner)) return balance } func approveByRegisterCall(pkgPath string, spender std.Address, amount uint64) bool { - i, found := findGRC20(pkgPath) + pkgPath = handleNative(pkgPath) + + _, found := registered[pkgPath] if !found { panic(ufmt.Sprintf("[STAKER] staker_register.gno__approveByRegisterCall() || pkgPath(%s) not found", pkgPath)) - // return false } - registered[i].igrc20.Approve()(users.AddressOrName(spender), amount) + registered[pkgPath].Approve()(users.AddressOrName(spender), amount) return true } + +func handleNative(pkgPath string) string { + if pkgPath == consts.GNOT { + return consts.WRAPPED_WUGNOT + } + + return pkgPath +}