diff --git a/_setup/bar/bar.gno b/_setup/bar/bar.gno index f2ef662e..42bad433 100644 --- a/_setup/bar/bar.gno +++ b/_setup/bar/bar.gno @@ -37,16 +37,16 @@ func init() { bar.Mint(tr01, 50000000) bar.Approve(lp01, poolAddr, 50000000) - bar.Approve(lp02, poolAddr, 50000000) - bar.Approve(lp03, 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(lp01, lp01, 50000000) + // bar.Approve(lp02, lp02, 50000000) + // bar.Approve(lp03, lp03, 50000000) + // bar.Approve(tr01, tr01, 50000000) - bar.Approve(posAddr, poolAddr, 50000000) + // bar.Approve(poolAddr, registryAddr, 50000000) } // method proxies as public functions. @@ -80,7 +80,6 @@ func Allowance(owner, spender users.AddressOrName) uint64 { // setters. func Transfer(to users.AddressOrName, amount uint64) { - // caller := std.GetOrigCaller() caller := std.PrevRealm().Addr() err := bar.Transfer(caller, to.Resolve(), amount) if err != nil { @@ -89,7 +88,6 @@ func Transfer(to users.AddressOrName, amount uint64) { } func Approve(spender users.AddressOrName, amount uint64) { - // caller := std.GetOrigCaller() caller := std.PrevRealm().Addr() err := bar.Approve(caller, spender.Resolve(), amount) if err != nil { @@ -98,8 +96,8 @@ func Approve(spender users.AddressOrName, amount uint64) { } func TransferFrom(from, to users.AddressOrName, amount uint64) { - // caller := std.GetOrigCaller() caller := std.PrevRealm().Addr() + err := bar.TransferFrom(caller, from.Resolve(), to.Resolve(), amount) if err != nil { panic(err.Error()) @@ -111,7 +109,6 @@ func TransferFrom(from, to users.AddressOrName, amount uint64) { func Faucet() { // FIXME: add limits? // FIXME: add payment in gnot? - // caller := std.GetOrigCaller() caller := std.PrevRealm().Addr() bar.Mint(caller, 1000*10000) // 1k } @@ -119,7 +116,6 @@ func Faucet() { func FaucetL() { // FIXME: add limits? // FIXME: add payment in gnot? - // caller := std.GetOrigCaller() caller := std.PrevRealm().Addr() bar.Mint(caller, 50000000000) // 50_000_000_000 } @@ -127,14 +123,12 @@ func FaucetL() { // administration. func Mint(address users.AddressOrName, amount uint64) { - // caller := std.GetOrigCaller() caller := std.PrevRealm().Addr() assertIsAdmin(caller) bar.Mint(address.Resolve(), amount) } func Burn(address users.AddressOrName, amount uint64) { - // caller := std.GetOrigCaller() caller := std.PrevRealm().Addr() assertIsAdmin(caller) bar.Burn(address.Resolve(), amount) diff --git a/_setup/baz/baz.gno b/_setup/baz/baz.gno new file mode 100644 index 00000000..46b56794 --- /dev/null +++ b/_setup/baz/baz.gno @@ -0,0 +1,166 @@ +package baz + +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 ( + baz *grc20.AdminToken + admin std.Address = "g1jg8mtutu9khhfwc4nxmuhcpftf0pajdhfvsqf5" // TODO: helper to change admin +) + +func init() { + baz = grc20.NewAdminToken("Baz", "BAZ", 4) + baz.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") + ) + + 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) +} + +// method proxies as public functions. +// + +// getters. +func GetGRC20() *grc20.AdminToken { + return baz +} + +func TotalSupply() uint64 { + return baz.TotalSupply() +} + +func BalanceOf(owner users.AddressOrName) uint64 { + balance, err := baz.BalanceOf(owner.Resolve()) + if err != nil { + panic(err) + } + return balance +} + +func Allowance(owner, spender users.AddressOrName) uint64 { + allowance, err := baz.Allowance(owner.Resolve(), spender.Resolve()) + if err != nil { + panic(err) + } + return allowance +} + +// setters. + +func Transfer(to users.AddressOrName, amount uint64) { + // caller := std.GetOrigCaller() + caller := std.PrevRealm().Addr() + err := baz.Transfer(caller, to.Resolve(), amount) + if err != nil { + panic(err.Error()) + } +} + +func Approve(spender users.AddressOrName, amount uint64) { + // caller := std.GetOrigCaller() + caller := std.PrevRealm().Addr() + err := baz.Approve(caller, spender.Resolve(), amount) + if err != nil { + panic(err.Error()) + } +} + +func TransferFrom(from, to users.AddressOrName, amount uint64) { + // caller := std.GetOrigCaller() + caller := std.PrevRealm().Addr() + err := baz.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.GetOrigCaller() + caller := std.PrevRealm().Addr() + baz.Mint(caller, 1000*10000) // 1k +} + +func FaucetL() { + // FIXME: add limits? + // FIXME: add payment in gnot? + // caller := std.GetOrigCaller() + caller := std.PrevRealm().Addr() + baz.Mint(caller, 50000000000) // 50_000_000_000 +} + +// administration. + +func Mint(address users.AddressOrName, amount uint64) { + // caller := std.GetOrigCaller() + caller := std.PrevRealm().Addr() + assertIsAdmin(caller) + baz.Mint(address.Resolve(), amount) +} + +func Burn(address users.AddressOrName, amount uint64) { + // caller := std.GetOrigCaller() + caller := std.PrevRealm().Addr() + assertIsAdmin(caller) + baz.Burn(address.Resolve(), amount) +} + +// render. +// + +func Render(path string) string { + parts := strings.Split(path, "/") + c := len(parts) + + switch { + case path == "": + return baz.RenderHome() + case c == 2 && parts[0] == "balance": + owner := users.AddressOrName(parts[1]) + balance, _ := baz.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/baz/baz_test.gno b/_setup/baz/baz_test.gno new file mode 100644 index 00000000..fb830e7a --- /dev/null +++ b/_setup/baz/baz_test.gno @@ -0,0 +1,73 @@ +package foo + +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/_setup/baz/gno.mod b/_setup/baz/gno.mod new file mode 100644 index 00000000..0982b354 --- /dev/null +++ b/_setup/baz/gno.mod @@ -0,0 +1 @@ +module gno.land/r/baz \ No newline at end of file diff --git a/_setup/foo/foo.gno b/_setup/foo/foo.gno index 7ac0f6ea..4a9aa981 100644 --- a/_setup/foo/foo.gno +++ b/_setup/foo/foo.gno @@ -10,6 +10,7 @@ import ( // for swap "gno.land/p/demo/testutils" + // for registry call ) var ( @@ -37,16 +38,16 @@ func init() { foo.Mint(tr01, 50000000) foo.Approve(lp01, poolAddr, 50000000) - foo.Approve(lp02, poolAddr, 50000000) - foo.Approve(lp03, 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(lp01, lp01, 50000000) + // foo.Approve(lp02, lp02, 50000000) + // foo.Approve(lp03, lp03, 50000000) + // foo.Approve(tr01, tr01, 50000000) - foo.Approve(posAddr, poolAddr, 50000000) + // foo.Approve(posAddr, poolAddr, 50000000) } // method proxies as public functions. @@ -80,7 +81,6 @@ func Allowance(owner, spender users.AddressOrName) uint64 { // setters. func Transfer(to users.AddressOrName, amount uint64) { - // caller := std.GetOrigCaller() caller := std.PrevRealm().Addr() err := foo.Transfer(caller, to.Resolve(), amount) if err != nil { @@ -89,7 +89,6 @@ func Transfer(to users.AddressOrName, amount uint64) { } func Approve(spender users.AddressOrName, amount uint64) { - // caller := std.GetOrigCaller() caller := std.PrevRealm().Addr() err := foo.Approve(caller, spender.Resolve(), amount) if err != nil { @@ -98,7 +97,6 @@ func Approve(spender users.AddressOrName, amount uint64) { } func TransferFrom(from, to users.AddressOrName, amount uint64) { - // caller := std.GetOrigCaller() caller := std.PrevRealm().Addr() err := foo.TransferFrom(caller, from.Resolve(), to.Resolve(), amount) if err != nil { @@ -111,7 +109,6 @@ func TransferFrom(from, to users.AddressOrName, amount uint64) { func Faucet() { // FIXME: add limits? // FIXME: add payment in gnot? - // caller := std.GetOrigCaller() caller := std.PrevRealm().Addr() foo.Mint(caller, 1000*10000) // 1k } @@ -119,7 +116,6 @@ func Faucet() { func FaucetL() { // FIXME: add limits? // FIXME: add payment in gnot? - // caller := std.GetOrigCaller() caller := std.PrevRealm().Addr() foo.Mint(caller, 50000000000) // 50_000_000_000 } @@ -127,14 +123,12 @@ func FaucetL() { // administration. func Mint(address users.AddressOrName, amount uint64) { - // caller := std.GetOrigCaller() caller := std.PrevRealm().Addr() assertIsAdmin(caller) foo.Mint(address.Resolve(), amount) } func Burn(address users.AddressOrName, amount uint64) { - // caller := std.GetOrigCaller() caller := std.PrevRealm().Addr() assertIsAdmin(caller) foo.Burn(address.Resolve(), amount) diff --git a/pool/_TEST_pool_multi_token_test.gno b/pool/_TEST_pool_multi_token_test.gno new file mode 100644 index 00000000..482f7be5 --- /dev/null +++ b/pool/_TEST_pool_multi_token_test.gno @@ -0,0 +1,182 @@ +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_router_test.gno b/pool/_TEST_pool_router_test.gnoa similarity index 100% rename from pool/_TEST_pool_router_test.gno rename to pool/_TEST_pool_router_test.gnoa diff --git a/pool/getter_pool.gno b/pool/getter_pool.gno index f24fa58f..d5f5e6d4 100644 --- a/pool/getter_pool.gno +++ b/pool/getter_pool.gno @@ -1,9 +1,6 @@ package pool import ( - "std" - - "gno.land/p/demo/grc/grc20" "gno.land/p/demo/ufmt" ) @@ -15,12 +12,12 @@ func (pool *Pool) GetLiquidity() bigint { return pool.liquidity } -func (pool *Pool) GetToken0() *grc20.AdminToken { - return pool.token0 +func (pool *Pool) GetToken0Path() string { + return pool.token0Path } -func (pool *Pool) GetToken1() *grc20.AdminToken { - return pool.token1 +func (pool *Pool) GetToken1Path() string { + return pool.token1Path } func (pool *Pool) GetFee() uint16 { @@ -87,9 +84,3 @@ func (pool *Pool) GetTickBitmaps() TickBitmaps { func (pool *Pool) GetSqrtPriceX96() bigint { return pool.slot0.sqrtPriceX96 } - -func (pool *Pool) PrintUserBalance(name string, target std.Address) { - println(name) - println(" > Token0:", balanceOf(pool.token0, target)) - println(" > Token1:", balanceOf(pool.token1, target)) -} diff --git a/pool/pool.gno b/pool/pool.gno index f332dbfd..c596c99e 100644 --- a/pool/pool.gno +++ b/pool/pool.gno @@ -8,15 +8,12 @@ import ( "gno.land/r/demo/users" g "gno.land/r/gov" - - bar "gno.land/r/bar" - foo "gno.land/r/foo" ) // only position contract can call this function func Mint( - pToken0 string, - pToken1 string, + pToken0Path string, + pToken1Path string, pFee uint16, recipient std.Address, tickLower int32, @@ -27,7 +24,7 @@ func Mint( require(liquidityAmount > 0, ufmt.Sprintf("[POOL] pool.gno__Mint() || liquidityAmount(%s) > 0", liquidityAmount)) - pool := GetPool(pToken0, pToken1, pFee) + pool := GetPool(pToken0Path, pToken1Path, pFee) _, amount0Int, amount1Int := pool.modifyPosition( ModifyPositionParams{ recipient, // owner @@ -38,50 +35,50 @@ func Mint( ) amount0 := bigint(amount0Int) - requireUnsigned(amount0, ufmt.Sprintf("[POOL] pool.gno__Mint() || amount0(%s) >= 0", amount0)) + requireUnsigned(amount0, ufmt.Sprintf("[POOL] pool.gno__Mint() || amount0(%d) >= 0", amount0)) amount1 := bigint(amount1Int) - requireUnsigned(amount1, ufmt.Sprintf("[POOL] pool.gno__Mint() || amount1(%s) >= 0", amount1)) + requireUnsigned(amount1, ufmt.Sprintf("[POOL] pool.gno__Mint() || amount1(%d) >= 0", amount1)) var balance0Before, balance1Before bigint if amount0 > 0 { - // r3v4_xxx - // when dynamic import is supported, we can use transfer() - // ~~ FROM HERE - balance0Before = balanceOf(pool.token0, GetOrigPkgAddr()) + balance0Before := bigint(balanceOfByRegisterCall(pToken0Path, GetOrigPkgAddr())) - from := a2u(GetOrigCaller()) // token should be transferred from actual user(GetOrigCaller), not from the realm(PrevRealm) - to := a2u(GetOrigPkgAddr()) + from := GetOrigCaller() // token should be transferred from actual user(GetOrigCaller), not from the realm(PrevRealm) + to := GetOrigPkgAddr() - foo.TransferFrom(from, to, uint64(amount0)) + ok := transferFromByRegisterCall(pToken0Path, from, to, uint64(amount0)) + if !ok { + panic("[POOL] pool.gno__Mint() || transferFromByRegisterCall(pToken0Path, from, to, uint64(amount0)) failed") + } require( - balance0Before+amount0 <= balanceOf(pool.token0, GetOrigPkgAddr()), + balance0Before+amount0 <= bigint(balanceOfByRegisterCall(pToken0Path, GetOrigPkgAddr())), ufmt.Sprintf( - "[POOL] pool.gno__Mint() || balance0Before(%s) + amount0(%s) <= balanceOf(pool.token0, GetOrigPkgAddr())(%s)", - balance0Before, amount0, balanceOf(pool.token0, GetOrigPkgAddr()), + "[POOL] pool.gno__Mint() || balance0Before(%d) + amount0(%d) <= balanceOfByRegisterCall(pToken0Path, GetOrigPkgAddr())(%d)", + balance0Before, amount0, balanceOfByRegisterCall(pToken0Path, GetOrigPkgAddr()), ), ) - } // ~~ TO HERE + } if amount1 > 0 { - // r3v4_xxx - // when dynamic import is supported, we can use transfer() - // ~~ FROM HERE - balance1Before = balanceOf(pool.token1, GetOrigPkgAddr()) + balance1Before = bigint(balanceOfByRegisterCall(pToken1Path, GetOrigPkgAddr())) - from := a2u(GetOrigCaller()) // token should be transferred from actual user(GetOrigCaller), not from the realm(PrevRealm) - to := a2u(GetOrigPkgAddr()) + from := GetOrigCaller() // token should be transferred from actual user(GetOrigCaller), not from the realm(PrevRealm) + to := GetOrigPkgAddr() - bar.TransferFrom(from, to, uint64(amount1)) + ok := transferFromByRegisterCall(pToken1Path, from, to, uint64(amount1)) + if !ok { + panic("[POOL] pool.gno__Mint() || transferFromByRegisterCall(pToken1Path, from, to, uint64(amount1)) failed") + } require( - balance1Before+amount1 <= balanceOf(pool.token1, GetOrigPkgAddr()), + balance1Before+amount1 <= bigint(balanceOfByRegisterCall(pToken1Path, GetOrigPkgAddr())), ufmt.Sprintf( - "[POOL] pool.gno__Mint() || balance1Before(%s) + amount1(%s) <= balanceOf(pool.token1, GetOrigPkgAddr())(%s)", - balance1Before, amount1, balanceOf(pool.token1, GetOrigPkgAddr()), + "[POOL] pool.gno__Mint() || balance1Before(%d) + amount1(%d) <= balanceOfByRegisterCall(pToken1Path, GetOrigPkgAddr())(%d)", + balance1Before, amount1, balanceOfByRegisterCall(pToken1Path, GetOrigPkgAddr()), ), ) - } // ~~ TO HERE + } pool.balances.token0 += amount0 pool.balances.token1 += amount1 @@ -91,8 +88,8 @@ func Mint( // only position contract can call this function func Burn( - pToken0 string, - pToken1 string, + pToken0Path string, + pToken1Path string, pFee uint16, tickLower int32, tickUpper int32, @@ -102,7 +99,7 @@ func Burn( requireUnsigned(amount, ufmt.Sprintf("[POOL] pool.gno__Burn() || amount(%s) >= 0", amount)) - pool := GetPool(pToken0, pToken1, pFee) + pool := GetPool(pToken0Path, pToken1Path, pFee) position, amount0Int, amount1Int := pool.modifyPosition( ModifyPositionParams{ @@ -128,10 +125,10 @@ func Burn( return amount0, amount1 } -// only position contract can call this function +// // only position contract can call this function func Collect( - pToken0 string, - pToken1 string, + pToken0Path string, + pToken1Path string, pFee uint16, recipient std.Address, tickLower int32, @@ -144,7 +141,7 @@ func Collect( requireUnsigned(amount0Requested, ufmt.Sprintf("pool.gno__Collect() || amount0Requested(%s) >= 0", amount0Requested)) requireUnsigned(amount1Requested, ufmt.Sprintf("pool.gno__Collect() || amount1Requested(%s) >= 0", amount1Requested)) - pool := GetPool(pToken0, pToken1, pFee) + pool := GetPool(pToken0Path, pToken1Path, pFee) key := positionGetKey(PrevRealmAddr(), tickLower, tickUpper) position, exist := pool.positions[key] @@ -156,13 +153,11 @@ func Collect( amount1 := min(amount1Requested, position.tokensOwed1) requireUnsigned(amount1, ufmt.Sprintf("[POOL] pool.gno__Collect() || amount1(%s) >= 0", amount1)) - // collect(pool.token0, pool.balances.token0, amount0, recipient) require(pool.balances.token0 >= amount0, ufmt.Sprintf("[POOL] pool.gno__Collect() || pool.balances.token0(%s) >= amount0(%s)", pool.balances.token0, amount0)) - foo.Transfer(a2u(recipient), uint64(amount0)) + transferFromByRegisterCall(pToken0Path, GetOrigPkgAddr(), recipient, uint64(amount0)) - // collect(pool.token1, pool.balances.token1, amount1, recipient) require(pool.balances.token1 >= amount1, ufmt.Sprintf("[POOL] pool.gno__Collect() || pool.balances.token1(%s) >= amount1(%s)", pool.balances.token1, amount1)) - bar.Transfer(a2u(recipient), uint64(amount1)) + transferFromByRegisterCall(pToken1Path, GetOrigPkgAddr(), recipient, uint64(amount1)) // adjust position position.tokensOwed0 -= amount0 @@ -180,8 +175,8 @@ func Collect( } func Swap( - pToken0 string, - pToken1 string, + pToken0Path string, + pToken1Path string, pFee uint16, recipient std.Address, zeroForOne bool, @@ -190,7 +185,7 @@ func Swap( ) (bigint, bigint) { require(amountSpecified != 0, "[POOL] pool.gno__Swap() || amountSpecified can't be zero") - pool := GetPool(pToken0, pToken1, pFee) + pool := GetPool(pToken0Path, pToken1Path, pFee) require(pool.liquidity > 0, ufmt.Sprintf("[POOL] math_logic.gno__swapAmount() || pool.liquidity(%d) must be > 0", pool.liquidity)) slot0Start := pool.slot0 @@ -381,31 +376,33 @@ func Swap( if zeroForOne { if amount1 < 0 { - require(pool.balances.token1 > (-1*amount1), ufmt.Sprintf("[POOL] pool.gno__Swap() || pool.balances.token1(%s) > (-1 * amount1)(%s)", pool.balances.token1, (-1*amount1))) + 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)) + if !ok { + panic("[POOL] pool.gno__Swap() || transferByRegisterCall(pToken1Path, recipient, uint64(-amount1)) failed") + } - // pool.token1.Transfer(recipient, uint64(-1*amount1)) - bar.Transfer(a2u(recipient), uint64(-1*amount1)) pool.balances.token1 += amount1 } - // r3v4_xxx - // transfer(pool.token0, amount0) - // ~~ FROM HERE - balance0Before := balanceOf(pool.token0, GetOrigPkgAddr()) + balance0Before := bigint(balanceOfByRegisterCall(pToken0Path, GetOrigPkgAddr())) - from := a2u(GetOrigCaller()) // token should be transferred from actual user(GetOrigCaller), not from the realm(PrevRealm) - to := a2u(GetOrigPkgAddr()) + txOrigin := GetOrigCaller() // token should be transferred from actual user(GetOrigCaller), not from the realm(PrevRealm) + poolPkg := GetOrigPkgAddr() - foo.TransferFrom(from, to, uint64(amount0)) + ok := transferFromByRegisterCall(pToken0Path, txOrigin, poolPkg, uint64(amount0)) + if !ok { + panic("[POOL] pool.gno__Swap() || transferFromByRegisterCall(pToken0Path, from, to, uint64(amount0)) failed") + } require( - balance0Before+bigint(amount0) <= balanceOf(pool.token0, GetOrigPkgAddr()), + balance0Before+amount0 <= bigint(balanceOfByRegisterCall(pToken0Path, GetOrigPkgAddr())), ufmt.Sprintf( - "[POOL] pool.gno__Swap() || balance0Before(%s) + amount0(%s) <= balanceOf(pool.token0, GetOrigPkgAddr())(%s)", - balance0Before, amount0, balanceOf(pool.token0, GetOrigPkgAddr()), + "[POOL] pool.gno__Swap() || balance0Before(%d) + amount0(%d) <= balanceOfByRegisterCall(pToken0Path, GetOrigPkgAddr())(%d)", + balance0Before, amount0, balanceOfByRegisterCall(pToken0Path, GetOrigPkgAddr()), ), ) - // TO HERE ~~ pool.balances.token0 += amount0 require(pool.balances.token0 >= 0, ufmt.Sprintf("[POOL] pool.gno__Swap() || pool.balances.token0(%s) >= 0__#1", pool.balances.token0)) @@ -413,33 +410,36 @@ func Swap( } else { if amount0 < 0 { - require(pool.balances.token0 > (-1*amount0), ufmt.Sprintf("[POOL] pool.gno__Swap() || pool.balances.token0(%s) > (-1 * amount0)(%s)", pool.balances.token0, (-1*amount0))) + 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)) + if !ok { + panic("[POOL] pool.gno__Swap() || transferByRegisterCall(pToken0Path, recipient, uint64(-amount0)) failed") + } - // pool.token0.Transfer(a2u(recipient), uint64(-amount0)) - foo.Transfer(a2u(recipient), uint64(-amount0)) pool.balances.token0 += amount0 } - // r3v4_xxx - // transfer(pool.token1, amount1) - // ~~ FROM HERE - balance1Before := balanceOf(pool.token1, GetOrigPkgAddr()) + balance1Before := bigint(balanceOfByRegisterCall(pToken1Path, GetOrigPkgAddr())) - from := a2u(GetOrigCaller()) - to := a2u(GetOrigPkgAddr()) + txOrigin := GetOrigCaller() // token should be transferred from actual user(GetOrigCaller), not from the realm(PrevRealm) + poolPkg := GetOrigPkgAddr() - bar.TransferFrom(from, to, uint64(amount1)) // token should be transferred from actual user(GetOrigCaller), not from the realm(PrevRealm) + ok := transferFromByRegisterCall(pToken1Path, txOrigin, poolPkg, uint64(amount1)) + if !ok { + panic("[POOL] pool.gno__Swap() || transferFromByRegisterCall(pToken1Path, from, to, uint64(amount1)) failed") + } require( - balance1Before+bigint(amount1) <= balanceOf(pool.token1, GetOrigPkgAddr()), + balance1Before+amount1 <= bigint(balanceOfByRegisterCall(pToken1Path, GetOrigPkgAddr())), ufmt.Sprintf( - "[POOL] pool.gno__Swap() || balance1Before(%s) + amount1(%s) <= balanceOf(pool.token1, GetOrigPkgAddr())(%s)", - balance1Before, amount1, balanceOf(pool.token1, GetOrigPkgAddr()), + "[POOL] pool.gno__Mint() || balance1Before(%d) + amount1(%d) <= balanceOfByRegisterCall(pToken1Path, GetOrigPkgAddr())(%d)", + balance1Before, amount1, balanceOfByRegisterCall(pToken1Path, GetOrigPkgAddr()), ), ) - // TO HERE ~~ pool.balances.token1 += amount1 + require(pool.balances.token0 >= 0, ufmt.Sprintf("[POOL] pool.gno__Swap() || pool.balances.token0(%s) >= 0__#2", pool.balances.token0)) require(pool.balances.token1 >= 0, ufmt.Sprintf("[POOL] pool.gno__Swap() || pool.balances.token1(%s) >= 0__#2", pool.balances.token1)) } @@ -469,38 +469,38 @@ 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( +// 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())) - pool := GetPool(pToken0, pToken1, pFee) +// pool := GetPool(pToken0, pToken1, 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)) +// // pool.token0.Transfer(a2u(recipient), uint64(amount0)) +// foo.Transfer(a2u(recipient), uint64(amount0)) - // pool.token1.Transfer(a2u(recipient), uint64(amount1)) - bar.Transfer(a2u(recipient), uint64(amount1)) +// // pool.token1.Transfer(a2u(recipient), uint64(amount1)) +// bar.Transfer(a2u(recipient), uint64(amount1)) - return amount0, amount1 -} +// return amount0, amount1 +// } func (pool *Pool) modifyPosition(params ModifyPositionParams) (PositionInfo, bigint, bigint) { position := pool.updatePosition( diff --git a/pool/pool_manager.gno b/pool/pool_manager.gno index 5fd58b03..fc9d482a 100644 --- a/pool/pool_manager.gno +++ b/pool/pool_manager.gno @@ -4,9 +4,6 @@ import ( "std" "strconv" - bar "gno.land/r/bar" - foo "gno.land/r/foo" - "gno.land/p/demo/ufmt" ) @@ -20,31 +17,31 @@ var ( func InitManual() { require(!initialized, "[POOl] pool_manager.gno__InitManual() || contract must not be initialized") - feeAmountTickSpacing[100] = 2 - feeAmountTickSpacing[500] = 10 - feeAmountTickSpacing[3000] = 60 - feeAmountTickSpacing[10000] = 200 + feeAmountTickSpacing[100] = 2 // 0.01% + feeAmountTickSpacing[500] = 10 // 0.05% + feeAmountTickSpacing[3000] = 60 // 0.3% + feeAmountTickSpacing[10000] = 200 // 1% admins = append(admins, PrevRealmAddr()) initialized = true } func CreatePool( - tokenA string, // XXX inter-contract token0 pkg_path - tokenB string, // XXX inter-contract token1 pkg_path + tokenAPath string, + tokenBPath string, fee uint16, sqrtPriceX96 bigint, ) *Pool { require(initialized, "[POOl] pool_manager.gno__CreatePool() || contract must be initialized") - require(tokenA != tokenB, ufmt.Sprintf("[POOl] pool_manager.gno__CreatePool() || token pair cannot be the same__tokenA(%s) != tokenB(%s)", tokenA, tokenB)) + require(tokenAPath != tokenBPath, ufmt.Sprintf("[POOl] pool_manager.gno__CreatePool() || token pair cannot be the same__tokenAPath(%s) != tokenBPath(%s)", tokenAPath, tokenBPath)) // r3v4_xxx: check whether token pair has been deployed successfuly - var token0, token1 string - if tokenA < tokenB { - token0 = tokenA - token1 = tokenB + var token0Path, token1Path string + if tokenAPath < tokenBPath { + token0Path = tokenAPath + token1Path = tokenBPath } else { - token0 = tokenB - token1 = tokenA + token0Path = tokenBPath + token1Path = tokenAPath } // check tickSpacing for fee @@ -52,14 +49,14 @@ func CreatePool( require(tickSpacing > 0, ufmt.Sprintf("[POOL] pool_manager.gno__CreatePool() || tickSpacing(%d) > 0", tickSpacing)) // calculate poolKey - poolKey := GetPoolKey(token0, token1, fee) + poolKey := GetPoolKey(token0Path, token1Path, fee) // check whether the pool already exist pool, exist := pools[poolKey] require(!exist, ufmt.Sprintf("[POOl] pool_manager.gno__CreatePool() || pool(%s) already exist", poolKey)) if !exist { - pool = newPool(token0, token1, fee, tickSpacing, sqrtPriceX96) + pool = newPool(token0Path, token1Path, fee, tickSpacing, sqrtPriceX96) pools[poolKey] = pool } @@ -81,17 +78,17 @@ func GetPoolFromPoolKey(poolKey string) *Pool { return pool } -func GetPoolKey(token0, token1 string, fee uint16) string { - if token0 < token1 { - return token0 + "_" + token1 + "_" + strconv.Itoa(int(fee)) +func GetPoolKey(token0Path, token1Path string, fee uint16) string { + if token0Path < token1Path { + return token0Path + ":" + token1Path + ":" + strconv.Itoa(int(fee)) } else { - return token1 + "_" + token0 + "_" + strconv.Itoa(int(fee)) + return token1Path + ":" + token0Path + ":" + strconv.Itoa(int(fee)) } } func newPool( - token0 string, - token1 string, + token0Path string, + token1Path string, fee uint16, tickSpacing int32, sqrtPriceX96 bigint, @@ -116,10 +113,9 @@ func newPool( token1: 0, } - // r3v4_xxx: dynamic import need to support multi token return &Pool{ - token0: foo.GetGRC20(), // token0.GetGRC20() // gno.land/r/foo.GetGRC20() - token1: bar.GetGRC20(), // token1.GetGRC20() // gno.land/r/bar.GetGRC20() + token0Path: token0Path, + token1Path: token1Path, balances: balances, fee: fee, tickSpacing: tickSpacing, diff --git a/pool/pool_register.gno b/pool/pool_register.gno new file mode 100644 index 00000000..009774d9 --- /dev/null +++ b/pool/pool_register.gno @@ -0,0 +1,122 @@ +package pool + +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() + if caller != APPROVED_CALLER { + panic("unauthorized 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/pool/type.gno b/pool/type.gno index e9982310..8387e2e1 100644 --- a/pool/type.gno +++ b/pool/type.gno @@ -2,8 +2,6 @@ package pool import ( "std" - - "gno.land/p/demo/grc/grc20" ) type Slot0 struct { @@ -87,8 +85,8 @@ type Positions map[string]PositionInfo // positionKey => PositionInfo // type Pool describes a single Pool/s state // A pool is identificed with a unique key (token0, token1, fee), where token0 < token1 type Pool struct { - token0 *grc20.AdminToken // XXX inter-contract, => call {pkg_path}.GetGRC20() - token1 *grc20.AdminToken // XXX inter-contract, => call {pkg_path}.GetGRC20() + token0Path string + token1Path string balances Balances