diff --git a/_deploy/p/gnoswap/uint256/arithmetic.gno b/_deploy/p/gnoswap/uint256/arithmetic.gno index c3e2ed837..a10436f8e 100644 --- a/_deploy/p/gnoswap/uint256/arithmetic.gno +++ b/_deploy/p/gnoswap/uint256/arithmetic.gno @@ -416,6 +416,10 @@ func (z *Uint) isBitSet(n uint) bool { return (z.arr[n/64] & (1 << (n % 64))) != 0 } +func (z *Uint) IsOverflow() bool { + return z.isBitSet(255) +} + // addTo computes x += y. // Requires len(x) >= len(y). func addTo(x, y []uint64) uint64 { diff --git a/_deploy/p/gnoswap/uint256/arithmetic_test.gno b/_deploy/p/gnoswap/uint256/arithmetic_test.gno index 9f45a5077..09d83eb18 100644 --- a/_deploy/p/gnoswap/uint256/arithmetic_test.gno +++ b/_deploy/p/gnoswap/uint256/arithmetic_test.gno @@ -6,6 +6,49 @@ type binOp2Test struct { x, y, want string } +func TestIsOverflow(t *testing.T) { + tests := []struct { + name string + input *Uint + expected bool + }{ + { + name: "Number greater than max value", + input: &Uint{arr: [4]uint64{ + ^uint64(0), ^uint64(0), ^uint64(0), ^uint64(0), + }}, + expected: true, + }, + { + name: "Max value", + input: &Uint{arr: [4]uint64{ + ^uint64(0), ^uint64(0), ^uint64(0), ^uint64(0) >> 1, + }}, + expected: false, + }, + { + name: "0", + input: &Uint{arr: [4]uint64{0, 0, 0, 0}}, + expected: false, + }, + { + name: "Only 255th bit set", + input: &Uint{arr: [4]uint64{ + 0, 0, 0, uint64(1) << 63, + }}, + expected: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := tt.input.IsOverflow(); got != tt.expected { + t.Errorf("IsOverflow() = %v, expected %v", got, tt.expected) + } + }) + } +} + func TestAdd(t *testing.T) { tests := []binOp2Test{ {"0", "1", "1"}, diff --git a/position/api.gno b/position/api.gno index bc172ef11..d2b960835 100644 --- a/position/api.gno +++ b/position/api.gno @@ -143,9 +143,8 @@ func ApiGetPosition(lpTokenId uint64) string { for _, position := range r.Response { owner, err := gnft.OwnerOf(tokenIdFrom(position.LpTokenId)) if err != nil { - panic(ufmt.Sprintf("owner not found for tokenId: %d", position.LpTokenId)) + owner = consts.ZERO_ADDRESS } - positionNode := json.ObjectNode("", map[string]*json.Node{ "lpTokenId": json.NumberNode("lpTokenId", float64(position.LpTokenId)), "burned": json.BoolNode("burned", position.Burned), @@ -212,9 +211,8 @@ func ApiGetPositionsByPoolPath(poolPath string) string { for _, position := range r.Response { owner, err := gnft.OwnerOf(tokenIdFrom(position.LpTokenId)) if err != nil { - panic(ufmt.Sprintf("owner not found for tokenId: %d", position.LpTokenId)) + owner = consts.ZERO_ADDRESS } - positionNode := json.ObjectNode("", map[string]*json.Node{ "lpTokenId": json.NumberNode("lpTokenId", float64(position.LpTokenId)), "burned": json.BoolNode("burned", position.Burned), @@ -285,9 +283,8 @@ func ApiGetPositionsByAddress(address std.Address) string { for _, position := range r.Response { owner, err := gnft.OwnerOf(tokenIdFrom(position.LpTokenId)) if err != nil { - panic(ufmt.Sprintf("owner not found for tokenId: %d", position.LpTokenId)) + owner = consts.ZERO_ADDRESS } - positionNode := json.ObjectNode("", map[string]*json.Node{ "lpTokenId": json.NumberNode("lpTokenId", float64(position.LpTokenId)), "burned": json.BoolNode("burned", position.Burned), @@ -428,7 +425,7 @@ func rpcMakePosition(lpTokenId uint64) RpcPosition { owner, err := gnft.OwnerOf(tokenIdFrom(lpTokenId)) if err != nil { - panic(ufmt.Sprintf("owner not found for tokenId: %d", lpTokenId)) + owner = consts.ZERO_ADDRESS } return RpcPosition{ diff --git a/position/getter.gno b/position/getter.gno index 69e1edd8e..c4964e7a3 100644 --- a/position/getter.gno +++ b/position/getter.gno @@ -3,8 +3,6 @@ package position import ( "std" - "gno.land/p/demo/ufmt" - u256 "gno.land/p/gnoswap/uint256" "gno.land/r/gnoswap/v1/gnft" @@ -85,7 +83,8 @@ func PositionIsInRange(tokenId uint64) bool { func PositionGetPositionOwner(tokenId uint64) std.Address { owner, err := gnft.OwnerOf(tokenIdFrom(tokenId)) if err != nil { - panic(ufmt.Sprintf("owner not found for tokenId: %d", tokenId)) + panic(newErrorWithDetail( + errDataNotFound, err.Error())) } return owner } diff --git a/position/position.gno b/position/position.gno index 7de0b00d7..f1c924eb7 100644 --- a/position/position.gno +++ b/position/position.gno @@ -520,7 +520,6 @@ func mint(params MintParams) (uint64, *u256.Uint, *u256.Uint, *u256.Uint) { } tokenId := getNextId() - gnft.Mint(params.mintTo, tokenIdFrom(tokenId)) // owner, tokenId pool := pl.GetPoolFromPoolPath(poolKey) diff --git a/position/tests/__TEST_fee_collect_with_two_user_test.gnoA b/position/tests/__TEST_fee_collect_with_two_user_test.gnoA index baa6d7157..4d127c574 100644 --- a/position/tests/__TEST_fee_collect_with_two_user_test.gnoA +++ b/position/tests/__TEST_fee_collect_with_two_user_test.gnoA @@ -1,4 +1,4 @@ -package position +package tests import ( "std" diff --git a/position/tests/__TEST_position_api_test.gnoA b/position/tests/__TEST_position_api_test.gnoA index bcce61003..2de41729b 100644 --- a/position/tests/__TEST_position_api_test.gnoA +++ b/position/tests/__TEST_position_api_test.gnoA @@ -1,4 +1,4 @@ -package position +package tests import ( "std" diff --git a/position/tests/__TEST_position_full_test.gnoA b/position/tests/__TEST_position_full_test.gnoA index f49b2d064..c793b8975 100644 --- a/position/tests/__TEST_position_full_test.gnoA +++ b/position/tests/__TEST_position_full_test.gnoA @@ -1,4 +1,4 @@ -package position +package tests import ( "std" @@ -70,7 +70,7 @@ func TestDecreaseLiquidity(t *testing.T) { tokenId, liquidity, fee0, fee1, amount0, amount1, poolPath := DecreaseLiquidity( uint64(1), - uint64(50), + "50", "0", "0", max_timeout, diff --git a/position/tests/__TEST_position_full_with_emission_test.gnoA b/position/tests/__TEST_position_full_with_emission_test.gnoA index babffe3fa..05a74992c 100644 --- a/position/tests/__TEST_position_full_with_emission_test.gnoA +++ b/position/tests/__TEST_position_full_with_emission_test.gnoA @@ -1,4 +1,4 @@ -package position +package tests import ( "std" @@ -115,7 +115,7 @@ func testDecreaseLiquidity(t *testing.T) { tokenId, liquidity, fee0, fee1, amount0, amount1, poolPath := DecreaseLiquidity( uint64(1), - uint64(50), + "50", "0", "0", max_timeout, diff --git a/position/tests/__TEST_position_increase_burned_position_test.gnoA b/position/tests/__TEST_position_increase_burned_position_test.gnoA index 29d986473..9d4992db2 100644 --- a/position/tests/__TEST_position_increase_burned_position_test.gnoA +++ b/position/tests/__TEST_position_increase_burned_position_test.gnoA @@ -1,4 +1,4 @@ -package position +package tests import ( "std" @@ -60,7 +60,7 @@ func TestIncreaseLiquidity(t *testing.T) { bar.Approve(a2u(consts.POOL_ADDR), 3678979) foo.Approve(a2u(consts.POOL_ADDR), 10000000) - pool := getPoolFromLpTokenId(t,uint64(1)) + pool := getPoolFromLpTokenId(t, uint64(1)) oldLiquidity := pool.Liquidity() _, _, m0, m1, _ := IncreaseLiquidity( @@ -82,18 +82,18 @@ func TestIncreaseLiquidity(t *testing.T) { func TestDecreaseLiquidity(t *testing.T) { std.TestSetRealm(adminRealm) - oldLiquidity := getPoolFromLpTokenId(t,uint64(1)).Liquidity() + oldLiquidity := getPoolFromLpTokenId(t, uint64(1)).Liquidity() DecreaseLiquidity( uint64(1), // tokenId - 50, // liquidityRatio + "191222635", // liquidity "0", // amount0Min "0", // amount1Min max_timeout, // deadline false, // unwrapResult ) - newLiquidity := getPoolFromLpTokenId(t,uint64(1)).Liquidity() + newLiquidity := getPoolFromLpTokenId(t, uint64(1)).Liquidity() uassert.Equal(t, true, newLiquidity.Lt(oldLiquidity)) // check fee left @@ -112,8 +112,8 @@ func TestDecreaseLiquidityToBurnPosition(t *testing.T) { // burn it DecreaseLiquidity( - uint64(1), // tokenId - 100, // liquidityRatio + uint64(1), // tokenId + "191222635", "0", // amount0Min "0", // amount1Min max_timeout, // deadline @@ -129,7 +129,7 @@ func TestIncreaseLiquidityBurnedPosition(t *testing.T) { bar.Approve(a2u(consts.POOL_ADDR), consts.UINT64_MAX) foo.Approve(a2u(consts.POOL_ADDR), consts.UINT64_MAX) - pool := getPoolFromLpTokenId(t,uint64(1)) + pool := getPoolFromLpTokenId(t, uint64(1)) oldLiquidity := pool.Liquidity() position := MustGetPosition(uint64(1)) diff --git a/position/tests/__TEST_position_increase_decrease_test.gnoA b/position/tests/__TEST_position_increase_decrease_test.gnoA index bc1ff69bf..128a81a89 100644 --- a/position/tests/__TEST_position_increase_decrease_test.gnoA +++ b/position/tests/__TEST_position_increase_decrease_test.gnoA @@ -1,4 +1,4 @@ -package position +package tests import ( "std" @@ -52,7 +52,7 @@ func TestIncreaseLiquidity(t *testing.T) { bar.Approve(a2u(consts.POOL_ADDR), 3678979) foo.Approve(a2u(consts.POOL_ADDR), 10000000) - pool := getPoolFromLpTokenId(t,uint64(1)) + pool := getPoolFromLpTokenId(t, uint64(1)) oldLiquidity := pool.Liquidity() _, _, m0, m1, _ := IncreaseLiquidity( @@ -158,18 +158,18 @@ func TestSwap2(t *testing.T) { func TestDecreaseLiquidity(t *testing.T) { std.TestSetRealm(adminRealm) - oldLiquidity := getPoolFromLpTokenId(t,uint64(1)).Liquidity() + oldLiquidity := getPoolFromLpTokenId(t, uint64(1)).Liquidity() DecreaseLiquidity( uint64(1), // tokenId - 50, // liquidityRatio + "191222635", // liquidity "0", // amount0Min "0", // amount1Min max_timeout, // deadline false, // unwrapResult ) - newLiquidity := getPoolFromLpTokenId(t,uint64(1)).Liquidity() + newLiquidity := getPoolFromLpTokenId(t, uint64(1)).Liquidity() uassert.Equal(t, true, newLiquidity.Lt(oldLiquidity)) // check fee left @@ -182,18 +182,18 @@ func TestDecreaseLiquidity(t *testing.T) { func TestDecreaseLiquidityAllThenAgainShouldPanic(t *testing.T) { std.TestSetRealm(adminRealm) - oldLiquidity := getPoolFromLpTokenId(t,uint64(1)).Liquidity() + oldLiquidity := getPoolFromLpTokenId(t, uint64(1)).Liquidity() DecreaseLiquidity( uint64(1), // tokenId - 100, // liquidityRatio + "191222635", // liquidity "0", // amount0Min "0", // amount1Min max_timeout, // deadline false, // unwrapResult ) - newLiquidity := getPoolFromLpTokenId(t,uint64(1)).Liquidity() + newLiquidity := getPoolFromLpTokenId(t, uint64(1)).Liquidity() uassert.Equal(t, true, newLiquidity.Lt(oldLiquidity)) uassert.Equal(t, newLiquidity.ToString(), "0") @@ -204,7 +204,7 @@ func TestDecreaseLiquidityAllThenAgainShouldPanic(t *testing.T) { func() { DecreaseLiquidity( uint64(1), // tokenId - 100, // liquidityRatio + "100", // liquidity "0", // amount0Min "0", // amount1Min max_timeout, // deadline diff --git a/position/tests/__TEST_position_mint_gnot_grc20_in-range_out-range_test.gnoA b/position/tests/__TEST_position_mint_gnot_grc20_in-range_out-range_test.gnoA index 271185f64..c04472ef2 100644 --- a/position/tests/__TEST_position_mint_gnot_grc20_in-range_out-range_test.gnoA +++ b/position/tests/__TEST_position_mint_gnot_grc20_in-range_out-range_test.gnoA @@ -1,4 +1,4 @@ -package position +package tests import ( "std" diff --git a/position/tests/__TEST_position_mint_swap_burn_left_test.gnoA b/position/tests/__TEST_position_mint_swap_burn_left_test.gnoA index e240c13ce..fba24905b 100644 --- a/position/tests/__TEST_position_mint_swap_burn_left_test.gnoA +++ b/position/tests/__TEST_position_mint_swap_burn_left_test.gnoA @@ -1,4 +1,4 @@ -package position +package tests import ( "std" @@ -235,7 +235,7 @@ func TestDecreaseAllPos01(t *testing.T) { std.TestSetRealm(adminRealm) tokenId, liquidity, fee0, fee1, amount0, amount1, poolPath := DecreaseLiquidity( uint64(1), // tokenId - 100, // liquidityRatio + "318704392", // liquidity "0", // amount0Min "0", // amount1Min max_timeout, // deadline @@ -263,7 +263,7 @@ func TestDecreaseAllPos02(t *testing.T) { std.TestSetRealm(adminRealm) tokenId, liquidity, fee0, fee1, amount0, amount1, poolPath := DecreaseLiquidity( uint64(2), // tokenId - 100, // liquidityRatio + "124373229", // liquidityRatio "0", // amount0Min "0", // amount1Min max_timeout, // deadline diff --git a/position/tests/__TEST_position_native_increase_decrease_test.gnoA b/position/tests/__TEST_position_native_increase_decrease_test.gnoA index d21b3135f..379aea67d 100644 --- a/position/tests/__TEST_position_native_increase_decrease_test.gnoA +++ b/position/tests/__TEST_position_native_increase_decrease_test.gnoA @@ -1,4 +1,4 @@ -package position +package tests import ( "std" @@ -157,7 +157,7 @@ func testDecreaseLiquidityWrapped(t *testing.T) { _, _, _, _, a0, a1, _ := DecreaseLiquidity( // tokenId, liquidity, fee0, fee1, amount0, amount1, poolPath uint64(1), // tokenId - 20, // liquidityRatio + "19122263", // liquidityRatio "0", // amount0Min "0", // amount1Min max_timeout, // deadline @@ -165,10 +165,10 @@ func testDecreaseLiquidityWrapped(t *testing.T) { ) userWugnotBalance = wugnot.BalanceOf(admin) // wrapped result, so wunogt increased - uassert.Equal(t, userWugnotBalance, uint64(11999999)) + uassert.Equal(t, uint64(2999999), userWugnotBalance) userUgnotBalance = ugnotBalanceOf(t, adminAddr) // wrapped result, so ugnot didn't change - uassert.Equal(t, userUgnotBalance, uint64(10)) + uassert.Equal(t, uint64(10), userUgnotBalance) newLiquidity := getPoolFromLpTokenId(t, uint64(1)).Liquidity() uassert.Equal(t, true, newLiquidity.Lt(oldLiquidity)) @@ -187,6 +187,7 @@ func testDecreaseLiquidityUnwrapped(t *testing.T) { std.TestSetRealm(adminRealm) oldLiquidity := getPoolFromLpTokenId(t, uint64(1)).Liquidity() + println("oldLiquidity ", oldLiquidity.ToString()) userWugnotBalance := wugnot.BalanceOf(admin) uassert.Equal(t, userWugnotBalance, uint64(11999999)) @@ -196,7 +197,7 @@ func testDecreaseLiquidityUnwrapped(t *testing.T) { _, _, _, _, a0, a1, _ := DecreaseLiquidity( // tokenId, liquidity, fee0, fee1, amount0, amount1, poolPath uint64(1), // tokenId - 50, // liquidityRatio + "50", // liquidityRatio "0", // amount0Min "0", // amount1Min max_timeout, // deadline diff --git a/position/tests/__TEST_position_native_mint_swap_burn_test.gnoA b/position/tests/__TEST_position_native_mint_swap_burn_test.gnoA index 2461786d3..b809a5ac6 100644 --- a/position/tests/__TEST_position_native_mint_swap_burn_test.gnoA +++ b/position/tests/__TEST_position_native_mint_swap_burn_test.gnoA @@ -1,4 +1,4 @@ -package position +package tests import ( "std" @@ -171,7 +171,7 @@ func testDecreaseWithNoUnwrap(t *testing.T) { std.TestSetRealm(adminRealm) DecreaseLiquidity( uint64(1), - uint64(10), + "15164540", "0", "0", int64(9999999999), @@ -198,7 +198,7 @@ func testDecreaseWithUnwrap(t *testing.T) { std.TestSetRealm(adminRealm) DecreaseLiquidity( uint64(1), - uint64(10), + "13648086", "0", "0", int64(9999999999), diff --git a/position/tests/__TEST_position_reposition_gnot_pair_test.gnoA b/position/tests/__TEST_position_reposition_gnot_pair_test.gnoA index 1dc217834..168279fbc 100644 --- a/position/tests/__TEST_position_reposition_gnot_pair_test.gnoA +++ b/position/tests/__TEST_position_reposition_gnot_pair_test.gnoA @@ -1,22 +1,71 @@ -package position +package tests import ( "std" "testing" "gno.land/p/demo/uassert" - + pusers "gno.land/p/demo/users" + "gno.land/r/demo/users" + "gno.land/r/demo/wugnot" "gno.land/r/gnoswap/v1/common" "gno.land/r/gnoswap/v1/consts" - - "gno.land/r/demo/wugnot" + "gno.land/r/gnoswap/v1/gnft" "gno.land/r/gnoswap/v1/gns" + pl "gno.land/r/gnoswap/v1/pool" +) - "gno.land/r/gnoswap/v1/gnft" +const ( + ugnotDenom string = "ugnot" + ugnotPath string = "gno.land/r/gnoswap/v1/pool:ugnot" + wugnotPath string = "gno.land/r/demo/wugnot" + gnsPath string = "gno.land/r/gnoswap/v1/gns" + barPath string = "gno.land/r/onbloc/bar" + bazPath string = "gno.land/r/onbloc/baz" + fooPath string = "gno.land/r/onbloc/foo" + oblPath string = "gno.land/r/onbloc/obl" + quxPath string = "gno.land/r/onbloc/qux" + + fee100 uint32 = 100 + fee500 uint32 = 500 + fee3000 uint32 = 3000 + fee10000 uint32 = 10000 + maxApprove uint64 = 18446744073709551615 + max_timeout int64 = 9999999999 + maxSqrtPriceLimitX96 string = "1461446703485210103287273052203988822378723970341" + + TIER_1 uint64 = 1 + TIER_2 uint64 = 2 + TIER_3 uint64 = 3 +) - pl "gno.land/r/gnoswap/v1/pool" +var ( + adminAddr = std.Address(consts.ADMIN) + admin = pusers.AddressOrName(adminAddr) + adminRealm = std.NewUserRealm(users.Resolve(admin)) + rouRealm = std.NewCodeRealm(consts.ROUTER_PATH) ) +func ugnotBalanceOf(t *testing.T, addr std.Address) uint64 { + t.Helper() + + testBanker := std.GetBanker(std.BankerTypeRealmIssue) + + coins := testBanker.GetCoins(addr) + if len(coins) == 0 { + return 0 + } + + return uint64(coins.AmountOf("ugnot")) +} + +func getPoolFromLpTokenId(t *testing.T, lpTokenId uint64) *pl.Pool { + t.Helper() + + position := MustGetPosition(lpTokenId) + return pl.GetPoolFromPoolPath(position.poolKey) +} + func TestRepositionCoinPair(t *testing.T) { testPoolInitCreatePool(t) testMintPosition01InRange(t) @@ -161,7 +210,7 @@ func testDecreaseLiquidityInPosition(t *testing.T) { lpTokenId := uint64(1) - ownerOfPosition := gnft.OwnerOf(tokenIdFrom(lpTokenId)) + ownerOfPosition, _ := gnft.OwnerOf(tokenIdFrom(lpTokenId)) uassert.Equal(t, ownerOfPosition, adminAddr) // approve fee0, fee1 to pool @@ -170,7 +219,7 @@ func testDecreaseLiquidityInPosition(t *testing.T) { tokenId, liquidity, fee0, fee1, amount0, amount1, poolPath := DecreaseLiquidity( lpTokenId, - 100, + "318704392", "0", "0", max_timeout, @@ -181,7 +230,7 @@ func testDecreaseLiquidityInPosition(t *testing.T) { uassert.Equal(t, amount0, "17941988") uassert.Equal(t, amount1, "51233948") - ownerOfPosition = gnft.OwnerOf(tokenIdFrom(lpTokenId)) + ownerOfPosition, _ = gnft.OwnerOf(tokenIdFrom(lpTokenId)) uassert.Equal(t, ownerOfPosition, adminAddr) position := MustGetPosition(lpTokenId) diff --git a/position/tests/__TEST_position_reposition_grc20_pair_test.gnoA b/position/tests/__TEST_position_reposition_grc20_pair_test.gnoA index c26d7a599..ab9cab405 100644 --- a/position/tests/__TEST_position_reposition_grc20_pair_test.gnoA +++ b/position/tests/__TEST_position_reposition_grc20_pair_test.gnoA @@ -1,19 +1,58 @@ -package position +package tests import ( "std" "testing" + "gno.land/p/demo/testutils" "gno.land/p/demo/uassert" + pusers "gno.land/p/demo/users" + "gno.land/r/demo/users" "gno.land/r/gnoswap/v1/consts" + "gno.land/r/gnoswap/v1/gnft" + "gno.land/r/gnoswap/v1/gns" pl "gno.land/r/gnoswap/v1/pool" - "gno.land/r/gnoswap/v1/gns" "gno.land/r/onbloc/bar" "gno.land/r/onbloc/foo" +) - "gno.land/r/gnoswap/v1/gnft" +const ( + ugnotDenom string = "ugnot" + ugnotPath string = "gno.land/r/gnoswap/v1/pool:ugnot" + wugnotPath string = "gno.land/r/demo/wugnot" + gnsPath string = "gno.land/r/gnoswap/v1/gns" + barPath string = "gno.land/r/onbloc/bar" + bazPath string = "gno.land/r/onbloc/baz" + fooPath string = "gno.land/r/onbloc/foo" + oblPath string = "gno.land/r/onbloc/obl" + quxPath string = "gno.land/r/onbloc/qux" + + fee100 uint32 = 100 + fee500 uint32 = 500 + fee3000 uint32 = 3000 + fee10000 uint32 = 10000 + maxApprove uint64 = 18446744073709551615 + max_timeout int64 = 9999999999 + maxSqrtPriceLimitX96 string = "1461446703485210103287273052203988822378723970341" + + TIER_1 uint64 = 1 + TIER_2 uint64 = 2 + TIER_3 uint64 = 3 +) + +const ( + // define addresses to use in tests + addr01 = testutils.TestAddress("addr01") + addr02 = testutils.TestAddress("addr02") +) + +var ( + adminAddr = std.Address(consts.ADMIN) + admin = pusers.AddressOrName(adminAddr) + adminRealm = std.NewUserRealm(users.Resolve(admin)) + rouRealm = std.NewCodeRealm(consts.ROUTER_PATH) ) func TestPoolInitCreatePool(t *testing.T) { @@ -153,20 +192,20 @@ func TestDecreaseLiquidityInPosition(t *testing.T) { lpTokenId := uint64(1) - ownerOfPosition := gnft.OwnerOf(tokenIdFrom(lpTokenId)) + ownerOfPosition, _ := gnft.OwnerOf(tokenIdFrom(lpTokenId)) uassert.Equal(t, ownerOfPosition, adminAddr) // approve fee0, feelpTokenId to pool bar.Approve(a2u(consts.POOL_ADDR), consts.UINT64_MAX) foo.Approve(a2u(consts.POOL_ADDR), consts.UINT64_MAX) - tokenId, liquidity, fee0, fee1, amount0, amount1, poolPath := DecreaseLiquidity(lpTokenId, 100, "0", "0", max_timeout, false) + tokenId, liquidity, fee0, fee1, amount0, amount1, poolPath := DecreaseLiquidity(lpTokenId, "318704392", "0", "0", max_timeout, false) uassert.Equal(t, tokenId, lpTokenId) uassert.Equal(t, amount0, "19628840") uassert.Equal(t, amount1, "46667220") - ownerOfPosition = gnft.OwnerOf(tokenIdFrom(lpTokenId)) + ownerOfPosition, _ = gnft.OwnerOf(tokenIdFrom(lpTokenId)) uassert.Equal(t, ownerOfPosition, adminAddr) position := MustGetPosition(lpTokenId) diff --git a/position/tests/__TEST_position_reposition_grc20_pair_with_swap_test.gnoA b/position/tests/__TEST_position_reposition_grc20_pair_with_swap_test.gnoA index 2dae15a3c..b4290f6d2 100644 --- a/position/tests/__TEST_position_reposition_grc20_pair_with_swap_test.gnoA +++ b/position/tests/__TEST_position_reposition_grc20_pair_with_swap_test.gnoA @@ -1,21 +1,60 @@ -package position +package tests import ( "std" "testing" + "gno.land/p/demo/testutils" "gno.land/p/demo/uassert" + pusers "gno.land/p/demo/users" + "gno.land/r/demo/users" "gno.land/r/gnoswap/v1/consts" - - pl "gno.land/r/gnoswap/v1/pool" - "gno.land/r/gnoswap/v1/gnft" "gno.land/r/gnoswap/v1/gns" + pl "gno.land/r/gnoswap/v1/pool" + "gno.land/r/onbloc/bar" "gno.land/r/onbloc/foo" ) +const ( + ugnotDenom string = "ugnot" + ugnotPath string = "gno.land/r/gnoswap/v1/pool:ugnot" + wugnotPath string = "gno.land/r/demo/wugnot" + gnsPath string = "gno.land/r/gnoswap/v1/gns" + barPath string = "gno.land/r/onbloc/bar" + bazPath string = "gno.land/r/onbloc/baz" + fooPath string = "gno.land/r/onbloc/foo" + oblPath string = "gno.land/r/onbloc/obl" + quxPath string = "gno.land/r/onbloc/qux" + + fee100 uint32 = 100 + fee500 uint32 = 500 + fee3000 uint32 = 3000 + fee10000 uint32 = 10000 + maxApprove uint64 = 18446744073709551615 + max_timeout int64 = 9999999999 + maxSqrtPriceLimitX96 string = "1461446703485210103287273052203988822378723970341" + + TIER_1 uint64 = 1 + TIER_2 uint64 = 2 + TIER_3 uint64 = 3 +) + +const ( + // define addresses to use in tests + addr01 = testutils.TestAddress("addr01") + addr02 = testutils.TestAddress("addr02") +) + +var ( + adminAddr = std.Address(consts.ADMIN) + admin = pusers.AddressOrName(adminAddr) + adminRealm = std.NewUserRealm(users.Resolve(admin)) + rouRealm = std.NewCodeRealm(consts.ROUTER_PATH) +) + func TestPoolInitCreatePool(t *testing.T) { std.TestSetRealm(adminRealm) @@ -199,14 +238,14 @@ func TestDecreaseLiquidity03(t *testing.T) { _lpTokenId := uint64(03) - ownerOfPosition := gnft.OwnerOf(tokenIdFrom(_lpTokenId)) + ownerOfPosition, _ := gnft.OwnerOf(tokenIdFrom(_lpTokenId)) uassert.Equal(t, ownerOfPosition, adminAddr) // approve fee0, fee1 to pool ( for withdrawal protocol fee ) bar.Approve(a2u(consts.POOL_ADDR), consts.UINT64_MAX) foo.Approve(a2u(consts.POOL_ADDR), consts.UINT64_MAX) - tokenId, liquidity, fee0, fee1, amount0, amount1, poolPath := DecreaseLiquidity(_lpTokenId, 100, "0", "0", max_timeout, false) + tokenId, liquidity, fee0, fee1, amount0, amount1, poolPath := DecreaseLiquidity(_lpTokenId, "167312775", "0", "0", max_timeout, false) uassert.Equal(t, tokenId, _lpTokenId) uassert.Equal(t, amount0, "18651446") @@ -215,7 +254,7 @@ func TestDecreaseLiquidity03(t *testing.T) { uassert.Equal(t, fee0, "127") uassert.Equal(t, fee1, "0") - ownerOfPosition = gnft.OwnerOf(tokenIdFrom(_lpTokenId)) + ownerOfPosition, _ = gnft.OwnerOf(tokenIdFrom(_lpTokenId)) uassert.Equal(t, ownerOfPosition, adminAddr) position := MustGetPosition(_lpTokenId) diff --git a/position/tests/__TEST_position_same_user_same_pool_diff_range_diff_position_swap_fee_test.gnoA b/position/tests/__TEST_position_same_user_same_pool_diff_range_diff_position_swap_fee_test.gnoA index 4c34ea6d6..93343ddc2 100644 --- a/position/tests/__TEST_position_same_user_same_pool_diff_range_diff_position_swap_fee_test.gnoA +++ b/position/tests/__TEST_position_same_user_same_pool_diff_range_diff_position_swap_fee_test.gnoA @@ -1,20 +1,59 @@ -package position +package tests import ( "std" "testing" + "gno.land/p/demo/testutils" "gno.land/p/demo/uassert" + pusers "gno.land/p/demo/users" + "gno.land/r/demo/users" "gno.land/r/gnoswap/v1/consts" - + "gno.land/r/gnoswap/v1/gns" pl "gno.land/r/gnoswap/v1/pool" - "gno.land/r/gnoswap/v1/gns" "gno.land/r/onbloc/bar" "gno.land/r/onbloc/foo" ) +const ( + ugnotDenom string = "ugnot" + ugnotPath string = "gno.land/r/gnoswap/v1/pool:ugnot" + wugnotPath string = "gno.land/r/demo/wugnot" + gnsPath string = "gno.land/r/gnoswap/v1/gns" + barPath string = "gno.land/r/onbloc/bar" + bazPath string = "gno.land/r/onbloc/baz" + fooPath string = "gno.land/r/onbloc/foo" + oblPath string = "gno.land/r/onbloc/obl" + quxPath string = "gno.land/r/onbloc/qux" + + fee100 uint32 = 100 + fee500 uint32 = 500 + fee3000 uint32 = 3000 + fee10000 uint32 = 10000 + maxApprove uint64 = 18446744073709551615 + max_timeout int64 = 9999999999 + maxSqrtPriceLimitX96 string = "1461446703485210103287273052203988822378723970341" + + TIER_1 uint64 = 1 + TIER_2 uint64 = 2 + TIER_3 uint64 = 3 +) + +const ( + // define addresses to use in tests + addr01 = testutils.TestAddress("addr01") + addr02 = testutils.TestAddress("addr02") +) + +var ( + adminAddr = std.Address(consts.ADMIN) + admin = pusers.AddressOrName(adminAddr) + adminRealm = std.NewUserRealm(users.Resolve(admin)) + rouRealm = std.NewCodeRealm(consts.ROUTER_PATH) +) + func TestPoolInitCreatePool(t *testing.T) { std.TestSetRealm(adminRealm) diff --git a/position/tests/__TEST_position_same_user_same_pool_same_range_diff_position_swap_fee_test.gnoA b/position/tests/__TEST_position_same_user_same_pool_same_range_diff_position_swap_fee_test.gnoA index 269e80e18..fc824ac3e 100644 --- a/position/tests/__TEST_position_same_user_same_pool_same_range_diff_position_swap_fee_test.gnoA +++ b/position/tests/__TEST_position_same_user_same_pool_same_range_diff_position_swap_fee_test.gnoA @@ -1,20 +1,59 @@ -package position +package tests import ( "std" "testing" + "gno.land/p/demo/testutils" "gno.land/p/demo/uassert" + pusers "gno.land/p/demo/users" + "gno.land/r/demo/users" "gno.land/r/gnoswap/v1/consts" - + "gno.land/r/gnoswap/v1/gns" pl "gno.land/r/gnoswap/v1/pool" - "gno.land/r/gnoswap/v1/gns" "gno.land/r/onbloc/bar" "gno.land/r/onbloc/foo" ) +const ( + ugnotDenom string = "ugnot" + ugnotPath string = "gno.land/r/gnoswap/v1/pool:ugnot" + wugnotPath string = "gno.land/r/demo/wugnot" + gnsPath string = "gno.land/r/gnoswap/v1/gns" + barPath string = "gno.land/r/onbloc/bar" + bazPath string = "gno.land/r/onbloc/baz" + fooPath string = "gno.land/r/onbloc/foo" + oblPath string = "gno.land/r/onbloc/obl" + quxPath string = "gno.land/r/onbloc/qux" + + fee100 uint32 = 100 + fee500 uint32 = 500 + fee3000 uint32 = 3000 + fee10000 uint32 = 10000 + maxApprove uint64 = 18446744073709551615 + max_timeout int64 = 9999999999 + maxSqrtPriceLimitX96 string = "1461446703485210103287273052203988822378723970341" + + TIER_1 uint64 = 1 + TIER_2 uint64 = 2 + TIER_3 uint64 = 3 +) + +const ( + // define addresses to use in tests + addr01 = testutils.TestAddress("addr01") + addr02 = testutils.TestAddress("addr02") +) + +var ( + adminAddr = std.Address(consts.ADMIN) + admin = pusers.AddressOrName(adminAddr) + adminRealm = std.NewUserRealm(users.Resolve(admin)) + rouRealm = std.NewCodeRealm(consts.ROUTER_PATH) +) + func TestPoolInitCreatePool(t *testing.T) { std.TestSetRealm(adminRealm) diff --git a/position/tests/__TEST_position_test_two_position_used_single_swap_test.gnoA b/position/tests/__TEST_position_test_two_position_used_single_swap_test.gnoA index cc5300952..890a495e0 100644 --- a/position/tests/__TEST_position_test_two_position_used_single_swap_test.gnoA +++ b/position/tests/__TEST_position_test_two_position_used_single_swap_test.gnoA @@ -1,20 +1,59 @@ -package position +package tests import ( "std" "testing" + "gno.land/p/demo/testutils" "gno.land/p/demo/uassert" + pusers "gno.land/p/demo/users" + "gno.land/r/demo/users" "gno.land/r/gnoswap/v1/consts" - + "gno.land/r/gnoswap/v1/gns" pl "gno.land/r/gnoswap/v1/pool" - "gno.land/r/gnoswap/v1/gns" "gno.land/r/onbloc/bar" "gno.land/r/onbloc/foo" ) +const ( + ugnotDenom string = "ugnot" + ugnotPath string = "gno.land/r/gnoswap/v1/pool:ugnot" + wugnotPath string = "gno.land/r/demo/wugnot" + gnsPath string = "gno.land/r/gnoswap/v1/gns" + barPath string = "gno.land/r/onbloc/bar" + bazPath string = "gno.land/r/onbloc/baz" + fooPath string = "gno.land/r/onbloc/foo" + oblPath string = "gno.land/r/onbloc/obl" + quxPath string = "gno.land/r/onbloc/qux" + + fee100 uint32 = 100 + fee500 uint32 = 500 + fee3000 uint32 = 3000 + fee10000 uint32 = 10000 + maxApprove uint64 = 18446744073709551615 + max_timeout int64 = 9999999999 + maxSqrtPriceLimitX96 string = "1461446703485210103287273052203988822378723970341" + + TIER_1 uint64 = 1 + TIER_2 uint64 = 2 + TIER_3 uint64 = 3 +) + +const ( + // define addresses to use in tests + addr01 = testutils.TestAddress("addr01") + addr02 = testutils.TestAddress("addr02") +) + +var ( + adminAddr = std.Address(consts.ADMIN) + admin = pusers.AddressOrName(adminAddr) + adminRealm = std.NewUserRealm(users.Resolve(admin)) + rouRealm = std.NewCodeRealm(consts.ROUTER_PATH) +) + func TestPoolInitCreatePool(t *testing.T) { std.TestSetRealm(adminRealm) diff --git a/position/tests/__TEST_position_tokens_owed_left_grc20_pair_more_action_test.gnoA b/position/tests/__TEST_position_tokens_owed_left_grc20_pair_more_action_test.gnoA index c60deadb2..cd45b65e9 100644 --- a/position/tests/__TEST_position_tokens_owed_left_grc20_pair_more_action_test.gnoA +++ b/position/tests/__TEST_position_tokens_owed_left_grc20_pair_more_action_test.gnoA @@ -1,21 +1,67 @@ -package position +package tests import ( "std" "testing" + "gno.land/p/demo/testutils" "gno.land/p/demo/uassert" + pusers "gno.land/p/demo/users" + "gno.land/r/demo/users" "gno.land/r/gnoswap/v1/consts" - - pl "gno.land/r/gnoswap/v1/pool" - "gno.land/r/gnoswap/v1/gnft" "gno.land/r/gnoswap/v1/gns" + pl "gno.land/r/gnoswap/v1/pool" + "gno.land/r/onbloc/bar" "gno.land/r/onbloc/foo" ) +func getPoolFromLpTokenId(t *testing.T, lpTokenId uint64) *pl.Pool { + t.Helper() + + position := MustGetPosition(lpTokenId) + return pl.GetPoolFromPoolPath(position.poolKey) +} + +const ( + ugnotDenom string = "ugnot" + ugnotPath string = "gno.land/r/gnoswap/v1/pool:ugnot" + wugnotPath string = "gno.land/r/demo/wugnot" + gnsPath string = "gno.land/r/gnoswap/v1/gns" + barPath string = "gno.land/r/onbloc/bar" + bazPath string = "gno.land/r/onbloc/baz" + fooPath string = "gno.land/r/onbloc/foo" + oblPath string = "gno.land/r/onbloc/obl" + quxPath string = "gno.land/r/onbloc/qux" + + fee100 uint32 = 100 + fee500 uint32 = 500 + fee3000 uint32 = 3000 + fee10000 uint32 = 10000 + maxApprove uint64 = 18446744073709551615 + max_timeout int64 = 9999999999 + maxSqrtPriceLimitX96 string = "1461446703485210103287273052203988822378723970341" + + TIER_1 uint64 = 1 + TIER_2 uint64 = 2 + TIER_3 uint64 = 3 +) + +const ( + // define addresses to use in tests + addr01 = testutils.TestAddress("addr01") + addr02 = testutils.TestAddress("addr02") +) + +var ( + adminAddr = std.Address(consts.ADMIN) + admin = pusers.AddressOrName(adminAddr) + adminRealm = std.NewUserRealm(users.Resolve(admin)) + rouRealm = std.NewCodeRealm(consts.ROUTER_PATH) +) + func TestPoolInitCreatePool(t *testing.T) { std.TestSetRealm(adminRealm) @@ -260,7 +306,7 @@ func TestDecreaseLiquidityPosition02(t *testing.T) { lpTokenId := uint64(2) - ownerOfPosition := gnft.OwnerOf(tokenIdFrom(lpTokenId)) + ownerOfPosition, _ := gnft.OwnerOf(tokenIdFrom(lpTokenId)) uassert.Equal(t, ownerOfPosition, adminAddr) unclaimedFee0, unclaimedFee1 := unclaimedFee(lpTokenId) @@ -273,7 +319,7 @@ func TestDecreaseLiquidityPosition02(t *testing.T) { tokenId, liquidity, fee0, fee1, amount0, amount1, poolPath := DecreaseLiquidity( lpTokenId, - 100, + "872163793", "0", "0", max_timeout, @@ -283,7 +329,7 @@ func TestDecreaseLiquidityPosition02(t *testing.T) { uassert.Equal(t, amount0, "5459371") uassert.Equal(t, amount1, "55436931") - ownerOfPosition = gnft.OwnerOf(tokenIdFrom(lpTokenId)) + ownerOfPosition, _ = gnft.OwnerOf(tokenIdFrom(lpTokenId)) uassert.Equal(t, ownerOfPosition, adminAddr) defer func() { diff --git a/position/tests/__TEST_position_tokens_owed_left_pair_more_action_exact_test.gnoA b/position/tests/__TEST_position_tokens_owed_left_pair_more_action_exact_test.gnoA index 306fce987..14ecb6b4d 100644 --- a/position/tests/__TEST_position_tokens_owed_left_pair_more_action_exact_test.gnoA +++ b/position/tests/__TEST_position_tokens_owed_left_pair_more_action_exact_test.gnoA @@ -1,22 +1,68 @@ -package position +package tests import ( "std" "testing" + "gno.land/p/demo/testutils" "gno.land/p/demo/uassert" + pusers "gno.land/p/demo/users" + "gno.land/r/demo/users" "gno.land/r/gnoswap/v1/consts" - + "gno.land/r/gnoswap/v1/gnft" + "gno.land/r/gnoswap/v1/gns" pl "gno.land/r/gnoswap/v1/pool" rr "gno.land/r/gnoswap/v1/router" - "gno.land/r/gnoswap/v1/gnft" - "gno.land/r/gnoswap/v1/gns" "gno.land/r/onbloc/bar" "gno.land/r/onbloc/foo" ) +const ( + ugnotDenom string = "ugnot" + ugnotPath string = "gno.land/r/gnoswap/v1/pool:ugnot" + wugnotPath string = "gno.land/r/demo/wugnot" + gnsPath string = "gno.land/r/gnoswap/v1/gns" + barPath string = "gno.land/r/onbloc/bar" + bazPath string = "gno.land/r/onbloc/baz" + fooPath string = "gno.land/r/onbloc/foo" + oblPath string = "gno.land/r/onbloc/obl" + quxPath string = "gno.land/r/onbloc/qux" + + fee100 uint32 = 100 + fee500 uint32 = 500 + fee3000 uint32 = 3000 + fee10000 uint32 = 10000 + maxApprove uint64 = 18446744073709551615 + max_timeout int64 = 9999999999 + maxSqrtPriceLimitX96 string = "1461446703485210103287273052203988822378723970341" + + TIER_1 uint64 = 1 + TIER_2 uint64 = 2 + TIER_3 uint64 = 3 +) + +const ( + // define addresses to use in tests + addr01 = testutils.TestAddress("addr01") + addr02 = testutils.TestAddress("addr02") +) + +var ( + adminAddr = std.Address(consts.ADMIN) + admin = pusers.AddressOrName(adminAddr) + adminRealm = std.NewUserRealm(users.Resolve(admin)) + rouRealm = std.NewCodeRealm(consts.ROUTER_PATH) +) + +func getPoolFromLpTokenId(t *testing.T, lpTokenId uint64) *pl.Pool { + t.Helper() + + position := MustGetPosition(lpTokenId) + return pl.GetPoolFromPoolPath(position.poolKey) +} + func TestPoolInitCreatePool(t *testing.T) { std.TestSetRealm(adminRealm) @@ -75,15 +121,15 @@ func TestSwap1(t *testing.T) { bar.Approve(a2u(consts.ROUTER_ADDR), consts.UINT64_MAX) foo.Approve(a2u(consts.ROUTER_ADDR), consts.UINT64_MAX) - amount0, amount1 := rr.SwapRoute( + amount0, amount1 := rr.ExactOutSwapRoute( barPath, fooPath, "5000000", - "EXACT_OUT", "gno.land/r/onbloc/bar:gno.land/r/onbloc/foo:3000", "100", "6550809", ) + println("amount0, amount1:", amount0, amount1) uassert.Equal(t, amount0, "6508448") uassert.Equal(t, amount1, "-4992500") // -5000000 * 99.85% std.TestSkipHeights(10) @@ -192,11 +238,10 @@ func TestSwap2(t *testing.T) { bar.Approve(a2u(consts.ROUTER_ADDR), consts.UINT64_MAX) foo.Approve(a2u(consts.ROUTER_ADDR), consts.UINT64_MAX) - amount0, amount1 := rr.SwapRoute( + amount0, amount1 := rr.ExactInSwapRoute( barPath, fooPath, "5000000", - "EXACT_IN", "gno.land/r/onbloc/bar:gno.land/r/onbloc/foo:3000", "100", "2866635", @@ -255,11 +300,10 @@ func TestSwap3(t *testing.T) { bar.Approve(a2u(consts.ROUTER_ADDR), consts.UINT64_MAX) foo.Approve(a2u(consts.ROUTER_ADDR), consts.UINT64_MAX) - amount0, amount1 := rr.SwapRoute( + amount0, amount1 := rr.ExactInSwapRoute( barPath, fooPath, "1000000", - "EXACT_IN", "gno.land/r/onbloc/bar:gno.land/r/onbloc/foo:3000", "100", "559130", @@ -301,16 +345,18 @@ func TestDecreaseLiquidityPosition02(t *testing.T) { lpTokenId := uint64(2) - ownerOfPosition := gnft.OwnerOf(tokenIdFrom(lpTokenId)) + ownerOfPosition, _ := gnft.OwnerOf(tokenIdFrom(lpTokenId)) uassert.Equal(t, ownerOfPosition, adminAddr) // approve fee0, feelpTokenId to pool bar.Approve(a2u(consts.POOL_ADDR), consts.UINT64_MAX) foo.Approve(a2u(consts.POOL_ADDR), consts.UINT64_MAX) + oldLiquidities := getPoolFromLpTokenId(t, lpTokenId).Liquidity() + println("oldLiquidities:", oldLiquidities.ToString()) tokenId, liquidity, fee0, fee1, amount0, amount1, poolPath := DecreaseLiquidity( lpTokenId, - 31, + "31", "2091922", "1005220", max_timeout, @@ -322,7 +368,7 @@ func TestDecreaseLiquidityPosition02(t *testing.T) { uassert.Equal(t, amount0, "4183844") uassert.Equal(t, amount1, "2010439") - ownerOfPosition = gnft.OwnerOf(tokenIdFrom(lpTokenId)) + ownerOfPosition, _ = gnft.OwnerOf(tokenIdFrom(lpTokenId)) uassert.Equal(t, ownerOfPosition, adminAddr) unclaimedFee0, unclaimedFee1 := unclaimedFee(tokenId) @@ -348,16 +394,19 @@ func TestDecreaseLiquidityPosition02All(t *testing.T) { lpTokenId := uint64(2) - ownerOfPosition := gnft.OwnerOf(tokenIdFrom(lpTokenId)) + ownerOfPosition, _ := gnft.OwnerOf(tokenIdFrom(lpTokenId)) uassert.Equal(t, ownerOfPosition, adminAddr) // approve fee0, feelpTokenId to pool bar.Approve(a2u(consts.POOL_ADDR), consts.UINT64_MAX) foo.Approve(a2u(consts.POOL_ADDR), consts.UINT64_MAX) + oldLiquidities := getPoolFromLpTokenId(t, lpTokenId).Liquidity() + println("oldLiquidities:", oldLiquidities.ToString()) + tokenId, liquidity, fee0, fee1, amount0, amount1, poolPath := DecreaseLiquidity( lpTokenId, - 100, + "100", "0", "0", max_timeout, @@ -369,7 +418,7 @@ func TestDecreaseLiquidityPosition02All(t *testing.T) { uassert.Equal(t, amount0, "9312428") uassert.Equal(t, amount1, "4474850") - ownerOfPosition = gnft.OwnerOf(tokenIdFrom(lpTokenId)) + ownerOfPosition, _ = gnft.OwnerOf(tokenIdFrom(lpTokenId)) uassert.Equal(t, ownerOfPosition, adminAddr) unclaimedFee0, unclaimedFee1 := unclaimedFee(tokenId) diff --git a/position/tests/__TEST_position_unclaimed_fee_test.gnoA b/position/tests/__TEST_position_unclaimed_fee_test.gnoA index a3f1634ac..1b8dd8789 100644 --- a/position/tests/__TEST_position_unclaimed_fee_test.gnoA +++ b/position/tests/__TEST_position_unclaimed_fee_test.gnoA @@ -1,24 +1,68 @@ -package position +package tests import ( "std" "testing" + "gno.land/p/demo/testutils" "gno.land/p/demo/uassert" + pusers "gno.land/p/demo/users" + i256 "gno.land/p/gnoswap/int256" + "gno.land/r/demo/users" "gno.land/r/gnoswap/v1/consts" - + "gno.land/r/gnoswap/v1/gnft" + "gno.land/r/gnoswap/v1/gns" pl "gno.land/r/gnoswap/v1/pool" "gno.land/r/onbloc/bar" "gno.land/r/onbloc/foo" +) - "gno.land/r/gnoswap/v1/gnft" - "gno.land/r/gnoswap/v1/gns" +const ( + ugnotDenom string = "ugnot" + ugnotPath string = "gno.land/r/gnoswap/v1/pool:ugnot" + wugnotPath string = "gno.land/r/demo/wugnot" + gnsPath string = "gno.land/r/gnoswap/v1/gns" + barPath string = "gno.land/r/onbloc/bar" + bazPath string = "gno.land/r/onbloc/baz" + fooPath string = "gno.land/r/onbloc/foo" + oblPath string = "gno.land/r/onbloc/obl" + quxPath string = "gno.land/r/onbloc/qux" + + fee100 uint32 = 100 + fee500 uint32 = 500 + fee3000 uint32 = 3000 + fee10000 uint32 = 10000 + maxApprove uint64 = 18446744073709551615 + max_timeout int64 = 9999999999 + maxSqrtPriceLimitX96 string = "1461446703485210103287273052203988822378723970341" + + TIER_1 uint64 = 1 + TIER_2 uint64 = 2 + TIER_3 uint64 = 3 +) - i256 "gno.land/p/gnoswap/int256" +const ( + // define addresses to use in tests + addr01 = testutils.TestAddress("addr01") + addr02 = testutils.TestAddress("addr02") +) + +var ( + adminAddr = std.Address(consts.ADMIN) + admin = pusers.AddressOrName(adminAddr) + adminRealm = std.NewUserRealm(users.Resolve(admin)) + rouRealm = std.NewCodeRealm(consts.ROUTER_PATH) ) +func getPoolFromLpTokenId(t *testing.T, lpTokenId uint64) *pl.Pool { + t.Helper() + + position := MustGetPosition(lpTokenId) + return pl.GetPoolFromPoolPath(position.poolKey) +} + func TestPoolInitCreatePool(t *testing.T) { std.TestSetRealm(adminRealm) @@ -218,16 +262,16 @@ func TestCollectFeeAfterSwap(t *testing.T) { func TestDecreaseLiquidityUpperPosition(t *testing.T) { std.TestSetRealm(adminRealm) - ownerOfPosition := gnft.OwnerOf(tokenIdFrom(3)) + ownerOfPosition, _ := gnft.OwnerOf(tokenIdFrom(3)) uassert.Equal(t, ownerOfPosition, adminAddr) - tokenId, liquidity, fee0, fee1, amount0, amount1, poolPath := DecreaseLiquidity(uint64(3), 100, "0", "0", max_timeout, false) + tokenId, liquidity, fee0, fee1, amount0, amount1, poolPath := DecreaseLiquidity(uint64(3), "318704392", "0", "0", max_timeout, false) uassert.Equal(t, tokenId, uint64(3)) - uassert.Equal(t, amount0, "49999999") + uassert.Equal(t, amount0, "16644469") uassert.Equal(t, amount1, "0") - ownerOfPosition = gnft.OwnerOf(tokenIdFrom(3)) + ownerOfPosition, _ = gnft.OwnerOf(tokenIdFrom(3)) uassert.Equal(t, ownerOfPosition, adminAddr) } diff --git a/position/utils.gno b/position/utils.gno index ada23d422..fa9f4cddd 100644 --- a/position/utils.gno +++ b/position/utils.gno @@ -55,7 +55,10 @@ func assertTokenExists(tokenId uint64) { func assertOnlyOwnerOfToken(tokenId uint64, caller std.Address) { owner, err := gnft.OwnerOf(tokenIdFrom(tokenId)) if err != nil { - panic(ufmt.Sprintf("owner not found for tokenId: %d", tokenId)) + panic(newErrorWithDetail( + errDataNotFound, + ufmt.Sprintf("tokenId(%d) doesn't exist", tokenId), + )) } assertCallerIsOwner(tokenId, owner, caller) } @@ -261,11 +264,10 @@ func exists(tokenId uint64) bool { // output: bool func isOwner(tokenId uint64, addr std.Address) bool { owner, err := gnft.OwnerOf(tokenIdFrom(tokenId)) - if err != nil { - panic(ufmt.Sprintf("owner not found for tokenId: %d", tokenId)) - } - if owner == addr { - return true + if err == nil { + if owner == addr { + return true + } } return false } @@ -276,10 +278,7 @@ func isOwner(tokenId uint64, addr std.Address) bool { // output: bool func isOperator(tokenId uint64, addr std.Address) bool { operator, err := gnft.GetApproved(tokenIdFrom(tokenId)) - if err != nil { - panic(ufmt.Sprintf("operator not found for tokenId: %d", tokenId)) - } - if operator == addr { + if err == nil && operator == addr { return true } return false @@ -294,10 +293,7 @@ func isStaked(tokenId grc721.TokenID) bool { exist := gnft.Exists(tokenId) if exist { owner, err := gnft.OwnerOf(tokenId) - if err != nil { - panic(ufmt.Sprintf("owner not found for tokenId: %d", tokenId)) - } - if owner == consts.STAKER_ADDR { + if err == nil && owner == consts.STAKER_ADDR { return true } } diff --git a/router/_helper_test.gno b/router/_helper_test.gno index 5c1dc39b9..94523a131 100644 --- a/router/_helper_test.gno +++ b/router/_helper_test.gno @@ -372,6 +372,7 @@ func CreatePoolWithoutFee(t *testing.T) { CreatePool(t, barPath, fooPath, fee500, common.TickMathGetSqrtRatioAtTick(0).ToString(), users.Resolve(admin)) CreatePool(t, bazPath, fooPath, fee3000, common.TickMathGetSqrtRatioAtTick(0).ToString(), users.Resolve(admin)) CreatePool(t, barPath, bazPath, fee3000, common.TickMathGetSqrtRatioAtTick(0).ToString(), users.Resolve(admin)) + CreatePool(t, barPath, bazPath, fee500, common.TickMathGetSqrtRatioAtTick(0).ToString(), users.Resolve(admin)) } func CreateSecondPoolWithoutFee(t *testing.T) { @@ -436,3 +437,53 @@ func MakeSecondMintPositionWithoutFee(t *testing.T) (uint64, string, string, str users.Resolve(admin), ) } + +func MakeThirdMintPositionWithoutFee(t *testing.T) (uint64, string, string, string) { + t.Helper() + + std.TestSetRealm(adminRealm) + + TokenApprove(t, barPath, admin, pool, consts.UINT64_MAX) + TokenApprove(t, fooPath, admin, pool, consts.UINT64_MAX) + + return pn.Mint( + barPath, + fooPath, + fee500, + -887220, + 887220, + "50000", + "50000", + "0", + "0", + max_timeout, + users.Resolve(admin), + users.Resolve(admin), + ) +} + +func MakeForthMintPositionWithoutFee(t *testing.T) (uint64, string, string, string) { + t.Helper() + + // make actual data to test resetting not only position's state but also pool's state + std.TestSetRealm(adminRealm) + + TokenApprove(t, barPath, admin, pool, consts.UINT64_MAX) + TokenApprove(t, bazPath, admin, pool, consts.UINT64_MAX) + + // mint position + return pn.Mint( + barPath, + bazPath, + fee500, + -887220, + 887220, + "50000", + "50000", + "0", + "0", + max_timeout, + users.Resolve(admin), + users.Resolve(admin), + ) +} diff --git a/router/base.gno b/router/base.gno index 6a0d47d3e..31c764ba8 100644 --- a/router/base.gno +++ b/router/base.gno @@ -103,7 +103,7 @@ func (op *baseSwapOperation) validateRouteQuote(quote string, i int) (*i256.Int, // calculate amount to swap for this route toSwap := i256.Zero().Mul(op.amountSpecified, i256.NewInt(int64(qt))) - toSwap = toSwap.Div(toSwap, i256.NewInt(100)) + toSwap = i256.Zero().Div(toSwap, i256.NewInt(100)) return toSwap, nil } @@ -118,10 +118,6 @@ func (op *baseSwapOperation) processRoutes(swapType SwapType) (*u256.Uint, *u256 return nil, nil, err } - if swapType == ExactOut { - toSwap = i256.Zero().Neg(toSwap) - } - amountIn, amountOut, err := op.processRoute(route, toSwap, swapType) if err != nil { return nil, nil, err diff --git a/router/base_test.gno b/router/base_test.gno index 11d6ca5e8..92e30333d 100644 --- a/router/base_test.gno +++ b/router/base_test.gno @@ -3,11 +3,9 @@ package router import ( "errors" "std" - "strings" "testing" i256 "gno.land/p/gnoswap/int256" - u256 "gno.land/p/gnoswap/uint256" "gno.land/r/gnoswap/v1/consts" "gno.land/p/demo/uassert" @@ -199,6 +197,7 @@ func TestProcessRoute(t *testing.T) { t.Run("Single hop route", func(t *testing.T) { CreatePoolWithoutFee(t) + MakeThirdMintPositionWithoutFee(t) route := "gno.land/r/onbloc/foo:gno.land/r/onbloc/bar:500" toSwap := i256.NewInt(1000) swapType := ExactIn @@ -206,7 +205,7 @@ func TestProcessRoute(t *testing.T) { amountIn, amountOut, err := op.processRoute(route, toSwap, swapType) uassert.Equal(t, err, nil) - uassert.Equal(t, amountIn.ToString(), "0") - uassert.Equal(t, amountOut.ToString(), "0") + uassert.Equal(t, amountIn.ToString(), "1000") + uassert.Equal(t, amountOut.ToString(), "979") }) } diff --git a/router/errors.gno b/router/errors.gno index c0137e2f3..48cdccf33 100644 --- a/router/errors.gno +++ b/router/errors.gno @@ -7,18 +7,19 @@ import ( ) var ( - errNoPermission = errors.New("[GNOSWAP-ROUTER-001] caller has no permission") - errNotRegistered = errors.New("[GNOSWAP-ROUTER-002] not registered token") - errAlreadyRegistered = errors.New("[GNOSWAP-ROUTER-003] already registered token") - errLocked = errors.New("[GNOSWAP-ROUTER-004] can't transfer token while locked") - errInvalidInput = errors.New("[GNOSWAP-ROUTER-005] invalid input data") - errInvalidPoolFeeTier = errors.New("[GNOSWAP-ROUTER-006] invalid pool fee tier") - errInvalidSwapFee = errors.New("[GNOSWAP-ROUTER-007] invalid swap fee") - errInvalidSwapType = errors.New("[GNOSWAP-ROUTER-008] invalid swap type") - errInvalidPoolPath = errors.New("[GNOSWAP-ROUTER-009] invalid pool path") - errWrapUnwrap = errors.New("[GNOSWAP-ROUTER-010] wrap, unwrap failed") - errWugnotMinimum = errors.New("[GNOSWAP-ROUTER-011] less than minimum amount ") - errSlippage = errors.New("[GNOSWAP-ROUTER-012] slippage") + errNoPermission = errors.New("[GNOSWAP-ROUTER-001] caller has no permission") + errSlippage = errors.New("[GNOSWAP-ROUTER-002] slippage check failed") + errInvalidRoutesAndQuotes = errors.New("[GNOSWAP-ROUTER-003] invalid routes and quotes") + errExpired = errors.New("[GNOSWAP-ROUTER-004] transaction expired") + errInvalidInput = errors.New("[GNOSWAP-ROUTER-005] invalid input data") + errInvalidPoolFeeTier = errors.New("[GNOSWAP-ROUTER-006] invalid pool fee tier") + errInvalidSwapFee = errors.New("[GNOSWAP-ROUTER-007] invalid swap fee") + errInvalidSwapType = errors.New("[GNOSWAP-ROUTER-008] invalid swap type") + errInvalidPoolPath = errors.New("[GNOSWAP-ROUTER-009] invalid pool path") + errWrapUnwrap = errors.New("[GNOSWAP-ROUTER-010] wrap, unwrap failed") + errWugnotMinimum = errors.New("[GNOSWAP-ROUTER-011] less than minimum amount ") + errQuoteParser = errors.New("[GNOSWAP-ROUTER-012] quote parse failed") + errHopsOutOfRange = errors.New("[GNOSWAP-ROUTER-013] number of hops must be 1~3") ) func addDetailToError(err error, detail string) string { diff --git a/router/exact_in.gno b/router/exact_in.gno index 10c53c224..41ba01fc6 100644 --- a/router/exact_in.gno +++ b/router/exact_in.gno @@ -30,7 +30,9 @@ func ExactInSwapRoute( RouteArr string, quoteArr string, amountOutMin string, + deadline int64, ) (string, string) { + checkDeadline(deadline) commonSwapSetup() baseParams := BaseSwapParams{ diff --git a/router/exact_in_test.gno b/router/exact_in_test.gno index 0ebeeccba..a180aa1e8 100644 --- a/router/exact_in_test.gno +++ b/router/exact_in_test.gno @@ -3,6 +3,7 @@ package router import ( "std" "testing" + "time" "gno.land/r/gnoswap/v1/consts" @@ -153,6 +154,7 @@ func TestExactInSwapRoute(t *testing.T) { tt.routeArr, tt.quoteArr, tt.amountOutMin, + time.Now().Add(time.Hour).Unix(), ) if !tt.wantErr { diff --git a/router/exact_out.gno b/router/exact_out.gno index 66c0619d7..d33bb4c52 100644 --- a/router/exact_out.gno +++ b/router/exact_out.gno @@ -30,7 +30,9 @@ func ExactOutSwapRoute( routeArr string, quoteArr string, amountInMax string, + deadline int64, ) (string, string) { + checkDeadline(deadline) commonSwapSetup() baseParams := BaseSwapParams{ @@ -87,7 +89,7 @@ func (op *ExactOutSwapOperation) Validate() error { // assign a signed reversed `amountOut` to `amountSpecified` // when it's an ExactOut - op.amountSpecified = new(i256.Int).Neg(amountOut) + op.amountSpecified = i256.Zero().Neg(amountOut) routes, quotes, err := tryParseRoutes(op.params.RouteArr, op.params.QuoteArr) if err != nil { diff --git a/router/protocol_fee_swap.gno b/router/protocol_fee_swap.gno index cf1941400..362620293 100644 --- a/router/protocol_fee_swap.gno +++ b/router/protocol_fee_swap.gno @@ -19,41 +19,14 @@ var ( swapFee = defaultSwapFeeBPS ) -func handleSwapFee( - outputToken string, - amount *u256.Uint, -) *u256.Uint { - if swapFee <= 0 { - return amount - } - - feeAmount := new(u256.Uint).Mul(amount, u256.NewUint(swapFee)) - feeAmount.Div(feeAmount, u256.NewUint(10000)) - feeAmountUint64 := feeAmount.Uint64() - - outputTeller := common.GetTokenTeller(outputToken) - outputTeller.TransferFrom(std.PrevRealm().Addr(), consts.PROTOCOL_FEE_ADDR, feeAmountUint64) - - prevAddr, prevRealm := getPrev() - - std.Emit( - "SwapRouteFee", - "prevAddr", prevAddr, - "prevRealm", prevRealm, - "internal_tokenPath", outputToken, - "internal_amount", ufmt.Sprintf("%d", feeAmountUint64), - ) - - toUserAfterProtocol := new(u256.Uint).Sub(amount, feeAmount) - return toUserAfterProtocol -} - // GetSwapFee returns current rate of swap fee // ref: https://docs.gnoswap.io/contracts/router/protocol_fee_swap.gno#getswapfee func GetSwapFee() uint64 { return swapFee } +// SetSwapFeeByAdmin modifies the swap fee +// Only admin can execute this function func SetSwapFeeByAdmin(fee uint64) { caller := std.PrevRealm().Addr() if err := common.AdminOnly(caller); err != nil { @@ -111,3 +84,32 @@ func setSwapFee(fee uint64) error { swapFee = fee return nil } + +func handleSwapFee( + outputToken string, + amount *u256.Uint, +) *u256.Uint { + if swapFee <= 0 { + return amount + } + + feeAmount := new(u256.Uint).Mul(amount, u256.NewUint(swapFee)) + feeAmount.Div(feeAmount, u256.NewUint(10000)) + feeAmountUint64 := feeAmount.Uint64() + + outputTeller := common.GetTokenTeller(outputToken) + outputTeller.TransferFrom(std.PrevRealm().Addr(), consts.PROTOCOL_FEE_ADDR, feeAmountUint64) + + prevAddr, prevRealm := getPrev() + + std.Emit( + "SwapRouteFee", + "prevAddr", prevAddr, + "prevRealm", prevRealm, + "internal_tokenPath", outputToken, + "internal_amount", ufmt.Sprintf("%d", feeAmountUint64), + ) + + toUserAfterProtocol := new(u256.Uint).Sub(amount, feeAmount) + return toUserAfterProtocol +} diff --git a/router/router.gno b/router/router.gno index e6b3a3106..4fa126cc3 100644 --- a/router/router.gno +++ b/router/router.gno @@ -5,32 +5,23 @@ import ( "strconv" "gno.land/p/demo/ufmt" - - "gno.land/r/gnoswap/v1/common" - "gno.land/r/gnoswap/v1/consts" - i256 "gno.land/p/gnoswap/int256" u256 "gno.land/p/gnoswap/uint256" "gno.land/r/demo/wugnot" - + "gno.land/r/gnoswap/v1/consts" en "gno.land/r/gnoswap/v1/emission" - sr "gno.land/r/gnoswap/v1/staker" ) -// Common validation and setup logic extracted from SwapRoute +// commonSwapSetup Common validation and setup logic extracted from SwapRoute func commonSwapSetup() { - common.IsHalted() + assertOnlyNotHalted() assertDirectCallOnly() en.MintAndDistributeGns() - if consts.EMISSION_REFACTORED { - sr.CalcPoolPositionRefactor() - } else { - sr.CalcPoolPosition() - } } +// handleSingleSwap handles a single swap operation. func handleSingleSwap(route string, amountSpecified *i256.Int) (*u256.Uint, *u256.Uint) { input, output, fee := getDataForSinglePath(route) singleParams := SingleSwapParams{ @@ -56,7 +47,7 @@ func handleMultiSwap( tokenIn: input, tokenOut: output, fee: fee, - recipient: std.PrevRealm().Addr(), + recipient: getPrevAddr(), amountSpecified: amountSpecified, } return multiSwap(swapParams, 0, numHops, route) @@ -66,14 +57,17 @@ func handleMultiSwap( tokenIn: input, tokenOut: output, fee: fee, - recipient: std.PrevRealm().Addr(), + recipient: getPrevAddr(), amountSpecified: amountSpecified, } return multiSwapNegative(swapParams, numHops-1, route) default: // Any invalid `SwapType` is caught in the `SwapRoute` function, // so no invalid values can get in here. - panic("should not reach here") + panic(addDetailToError( + errInvalidSwapType, + ufmt.Sprintf("unknown swapType(%s)", swapType), + )) } } @@ -85,11 +79,16 @@ func finalizeSwap( userBeforeWugnotBalance, userWrappedWugnot uint64, amountSpecified *u256.Uint, ) (string, string) { - if swapType == ExactOut && resultAmountOut.Lt(amountSpecified) { - panic(addDetailToError( - errSlippage, - ufmt.Sprintf("too few received for user (expected minimum: %s, actual: %s, swapType: %s)", amountSpecified.ToString(), resultAmountOut.ToString(), swapType.String()), - )) + + if swapType == ExactOut { + // If the pool's raw output is less than user wants, fail fast. + // (Optional: some designs skip this, and only check afterFee.) + if resultAmountOut.Lt(amountSpecified) { + panic(addDetailToError( + errSlippage, + ufmt.Sprintf("Received more than requested in [EXACT_OUT] requested=%s, actual=%s", amountSpecified.ToString(), resultAmountOut.ToString()), + )) + } } afterFee := handleSwapFee(outputToken, resultAmountOut) @@ -116,25 +115,39 @@ func finalizeSwap( } if swapType == ExactIn { - if !tokenAmountLimit.Lte(afterFee) { + // The user gave a fixed input => we must ensure final out >= tokenAmountLimit + // afterFee is the actual tokens user receives + if afterFee.Lt(tokenAmountLimit) { panic(addDetailToError( errSlippage, - ufmt.Sprintf("too few received for user (expected minimum: %s, actual: %s, swapType: %s)", tokenAmountLimit.ToString(), afterFee.ToString(), swapType.String()), + ufmt.Sprintf("ExactIn: too few received (min:%s, got:%s)", tokenAmountLimit.ToString(), afterFee.ToString()), )) } } else { - if !resultAmountIn.Lte(tokenAmountLimit) { + // swapType == ExactOut + // The user wants to get at least "amountSpecified" final tokens, + if resultAmountIn.Gt(tokenAmountLimit) { panic(addDetailToError( errSlippage, - ufmt.Sprintf("too much spent for user (expected maximum: %s, actual: %s, swapType: %s)", tokenAmountLimit.ToString(), resultAmountIn.ToString(), swapType.String()), + ufmt.Sprintf("ExactOut: too much spent (max:%s, used:%s)", tokenAmountLimit.ToString(), resultAmountIn.ToString()), )) } } - intAmountOut := i256.FromUint256(afterFee) - return resultAmountIn.ToString(), i256.Zero().Neg(intAmountOut).ToString() + intAmountOut := i256.FromUint256(afterFee) // final user out + negativeOut := i256.Zero().Neg(intAmountOut).ToString() + + return resultAmountIn.ToString(), negativeOut } +// validateRoutesAndQuotes validates the routes and quotes slices based on specific criteria. +// +// Parameters: +// - routes: A slice of strings representing route identifiers. +// - quotes: A slice of strings representing quote percentages corresponding to each route. +// +// Returns: +// - error: An error if the validation fails; nil if all checks pass. func validateRoutesAndQuotes(routes, quotes []string) error { if len(routes) < 1 || len(routes) > 7 { return ufmt.Errorf("route length(%d) must be 1~7", len(routes)) @@ -162,6 +175,16 @@ func validateRoutesAndQuotes(routes, quotes []string) error { return nil } +// tryParseRoutes parses and validates routes and quotes strings, returning them as slices. +// +// Parameters: +// - routes: A string containing route identifiers separated by commas (e.g., "route1,route2,route3"). +// - quotes: A string containing quote identifiers separated by commas (e.g., "quote1,quote2,quote3"). +// +// Returns: +// - []string: A slice of route identifiers parsed from the `routes` string. +// - []string: A slice of quote identifiers parsed from the `quotes` string. +// - error: An error if the validation between routes and quotes fails. func tryParseRoutes(routes, quotes string) ([]string, []string, error) { routesArr := splitSingleChar(routes, ',') quotesArr := splitSingleChar(quotes, ',') diff --git a/router/router_dry.gno b/router/router_dry.gno index 1f4b38bcb..f7c319cd8 100644 --- a/router/router_dry.gno +++ b/router/router_dry.gno @@ -1,6 +1,7 @@ package router import ( + "std" "strconv" "strings" @@ -8,6 +9,8 @@ import ( i256 "gno.land/p/gnoswap/int256" u256 "gno.land/p/gnoswap/uint256" + + "gno.land/r/gnoswap/v1/common" ) // DrySwapRoute simulates a token swap route without actually executing the swap. @@ -20,7 +23,11 @@ func DrySwapRoute( swapKind string, strRouteArr string, quoteArr string, + tokenAmountLimit string, ) string { + common.MustRegistered(inputToken) + common.MustRegistered(outputToken) + swapType, err := trySwapTypeFromStr(swapKind) if err != nil { panic(addDetailToError( @@ -30,17 +37,27 @@ func DrySwapRoute( } amountSpecified := i256.MustFromDecimal(specifiedAmount) - if amountSpecified.IsZero() || amountSpecified.IsNeg() { panic(addDetailToError( errInvalidInput, - ufmt.Sprintf("invalid amountSpecified(%s), must be positive", amountSpecified.ToString()), + ufmt.Sprintf("invalid amountSpecified(%s), must be positive", specifiedAmount), + )) + } + + amountLimit := i256.MustFromDecimal(tokenAmountLimit) + if amountLimit.IsZero() { + panic(addDetailToError( + errInvalidInput, + ufmt.Sprintf("invalid amountLimit(%s), should not be zero", tokenAmountLimit), )) } routes, quotes, err := tryParseRoutes(strRouteArr, quoteArr) if err != nil { - panic(err.Error()) + panic(addDetailToError( + errInvalidRoutesAndQuotes, + err.Error()), + ) } if swapType == ExactOut { @@ -52,32 +69,87 @@ func DrySwapRoute( for i, route := range routes { numHops := strings.Count(route, POOL_SEPARATOR) + 1 + assertHopsInRange(numHops) // don't need to check error here quote, _ := strconv.Atoi(quotes[i]) - - assertHopsInRange(numHops) + if quote < 0 || quote > 100 { + panic(addDetailToError( + errInvalidInput, + ufmt.Sprintf("quote(%d) must be 0~100", quote), + )) + } toSwap := i256.Zero().Mul(amountSpecified, i256.NewInt(int64(quote))) - toSwap = toSwap.Div(toSwap, i256.NewInt(100)) + toSwap = new(i256.Int).Div(toSwap, i256.NewInt(100)) - if numHops == 1 { // SINGLE - amountIn, amountOut := handleSingleSwap(route, toSwap) + if numHops == 1 { + amountIn, amountOut := handleSingleDrySwap(route, toSwap) resultAmountIn = new(u256.Uint).Add(resultAmountIn, amountIn) resultAmountOut = new(u256.Uint).Add(resultAmountOut, amountOut) } else { - amountIn, amountOut := handleMultiSwap(swapType, route, numHops, toSwap) + amountIn, amountOut := handleMultiDrySwap(swapType, route, numHops, toSwap) resultAmountIn = new(u256.Uint).Add(resultAmountIn, amountIn) resultAmountOut = new(u256.Uint).Add(resultAmountOut, amountOut) } } - return processResult(swapType, resultAmountIn, resultAmountOut, amountSpecified) + return processResult(swapType, resultAmountIn, resultAmountOut, amountSpecified, amountLimit) +} + +func handleSingleDrySwap(route string, amountSpecified *i256.Int) (*u256.Uint, *u256.Uint) { + input, output, fee := getDataForSinglePath(route) + singleParams := SingleSwapParams{ + tokenIn: input, + tokenOut: output, + fee: fee, + amountSpecified: amountSpecified, + } + + return singleDrySwap(singleParams) +} + +func handleMultiDrySwap( + swapType SwapType, + route string, + numHops int, + amountSpecified *i256.Int, +) (*u256.Uint, *u256.Uint) { + switch swapType { + case ExactIn: + input, output, fee := getDataForMultiPath(route, 0) // first data + swapParams := SwapParams{ + tokenIn: input, + tokenOut: output, + fee: fee, + recipient: std.PrevRealm().Addr(), + amountSpecified: amountSpecified, + } + return multiDrySwap(swapParams, 0, numHops, route) + case ExactOut: + input, output, fee := getDataForMultiPath(route, numHops-1) // last data + swapParams := SwapParams{ + tokenIn: input, + tokenOut: output, + fee: fee, + recipient: std.PrevRealm().Addr(), + amountSpecified: amountSpecified, + } + return multiDrySwapNegative(swapParams, numHops-1, route) + default: + panic(addDetailToError( + errInvalidSwapType, + ufmt.Sprintf("unknown swapType(%s)", swapType), + )) + } } -func processResult(swapType SwapType, resultAmountIn, resultAmountOut *u256.Uint, amountSpecified *i256.Int) string { +func processResult(swapType SwapType, resultAmountIn, resultAmountOut *u256.Uint, amountSpecified, amountLimit *i256.Int) string { switch swapType { case ExactIn: - if !i256.FromUint256(resultAmountIn).Eq(amountSpecified) { + if i256.FromUint256(resultAmountIn).Gt(amountSpecified) { + return "-1" + } + if i256.FromUint256(resultAmountOut).Lt(amountLimit) { return "-1" } return resultAmountOut.ToString() @@ -85,9 +157,14 @@ func processResult(swapType SwapType, resultAmountIn, resultAmountOut *u256.Uint if i256.FromUint256(resultAmountOut).Lt(amountSpecified) { return "-1" } + if i256.FromUint256(resultAmountIn).Gt(amountLimit) { + return "-1" + } return resultAmountIn.ToString() default: - // redundant case - panic("should not reach here") + panic(addDetailToError( + errInvalidSwapType, + ufmt.Sprintf("unknown swapType(%s)", swapType), + )) } } diff --git a/router/router_dry_test.gno b/router/router_dry_test.gno index 3968753a7..4ef062637 100644 --- a/router/router_dry_test.gno +++ b/router/router_dry_test.gno @@ -30,7 +30,7 @@ func TestProcessResult(t *testing.T) { name: "ExactIn - Input Mismatch", swapType: ExactIn, resultAmountIn: "99", - resultAmountOut: "95", + resultAmountOut: "5", amountSpecified: "100", expected: "-1", }, @@ -57,8 +57,12 @@ func TestProcessResult(t *testing.T) { resultAmountIn, _ := u256.FromDecimal(tt.resultAmountIn) resultAmountOut, _ := u256.FromDecimal(tt.resultAmountOut) amountSpecified, _ := i256.FromDecimal(tt.amountSpecified) + amountLimit, _ := i256.FromDecimal("10") + if tt.swapType == ExactOut { + amountLimit, _ = i256.FromDecimal("500") + } - result := processResult(tt.swapType, resultAmountIn, resultAmountOut, amountSpecified) + result := processResult(tt.swapType, resultAmountIn, resultAmountOut, amountSpecified, amountLimit) uassert.Equal(t, result, tt.expected) }) } diff --git a/router/router_test.gno b/router/router_test.gno index d8bda29be..b8b20bb8a 100644 --- a/router/router_test.gno +++ b/router/router_test.gno @@ -7,7 +7,6 @@ import ( "gno.land/p/demo/uassert" i256 "gno.land/p/gnoswap/int256" u256 "gno.land/p/gnoswap/uint256" - "gno.land/r/demo/wugnot" "gno.land/r/gnoswap/v1/consts" ) @@ -70,7 +69,7 @@ func TestFinalizeSwap(t *testing.T) { userWrappedWugnot: 0, amountSpecified: newUint256("100"), expectError: true, - errorMessage: "too few received for user", + errorMessage: "[GNOSWAP-ROUTER-002] slippage check failed || Received more than requested in [EXACT_OUT] requested=100, actual=90", }, { name: "GNOT: Slippage error", diff --git a/router/swap_inner.gno b/router/swap_inner.gno index 49dc60b20..163d082f0 100644 --- a/router/swap_inner.gno +++ b/router/swap_inner.gno @@ -51,12 +51,10 @@ func swapInner( data.tokenIn, data.tokenOut, data.fee, - recipient, zeroForOne, amountSpecified.ToString(), sqrtPriceLimitX96.ToString(), - data.payer, ) @@ -69,7 +67,20 @@ func swapInner( return poolRecv.Abs(), poolOut.Abs() } -// swapDryInner simulates a swap operation without executing it. +// swapDryInner performs a dry-run of a swap operation, calculating the potential +// received and output amounts without actually executing the swap. +// +// Parameters: +// - amountSpecified: The amount specified for the swap operation. It is an `i256.Int` +// representing the input token amount or output token amount (depending on the swap direction). +// - sqrtPriceLimitX96: The price limit of the swap expressed as a square root price in X96 format. +// This can be a user-defined limit or set to a default min or max price if not provided. +// - data: A `SwapCallbackData` structure containing details about the tokens being swapped, +// the fee structure, and other context. +// +// Returns: +// - poolRecv: The absolute value of the maximum amount received by the pool during the swap. +// - poolOut: The absolute value of the minimum amount output from the pool during the swap. func swapDryInner( amountSpecified *i256.Int, sqrtPriceLimitX96 *u256.Uint, @@ -90,7 +101,6 @@ func swapDryInner( data.tokenIn, data.tokenOut, data.fee, - zeroForOne, amountSpecified.ToString(), sqrtPriceLimitX96.ToString(), @@ -147,12 +157,12 @@ func calculateSqrtPriceLimitForSwap(zeroForOne bool, fee uint32, sqrtPriceLimitX if zeroForOne { minTick := getMinTick(fee) sqrtPriceLimitX96 = common.TickMathGetSqrtRatioAtTick(minTick) - return sqrtPriceLimitX96.Add(sqrtPriceLimitX96, u256.One()) + return new(u256.Uint).Add(sqrtPriceLimitX96, u256.One()) } maxTick := getMaxTick(fee) sqrtPriceLimitX96 = common.TickMathGetSqrtRatioAtTick(maxTick) - return sqrtPriceLimitX96.Sub(sqrtPriceLimitX96, u256.One()) + return new(u256.Uint).Sub(sqrtPriceLimitX96, u256.One()) } // getMinTick returns the minimum tick value for a given fee tier. diff --git a/router/swap_multi.gno b/router/swap_multi.gno index e0b8252c1..1af181246 100644 --- a/router/swap_multi.gno +++ b/router/swap_multi.gno @@ -12,7 +12,7 @@ import ( func multiSwap(params SwapParams, currentPoolIndex, numPools int, swapPath string) (*u256.Uint, *u256.Uint) { // firstAmountIn, lastAmountOut firstAmountIn := u256.Zero() - payer := std.PrevRealm().Addr() // user + payer := getPrevAddr() // user for { var recipient std.Address @@ -50,7 +50,6 @@ func multiSwap(params SwapParams, currentPoolIndex, numPools int, swapPath strin params.tokenIn = nextInput params.tokenOut = nextOutput params.fee = nextFee - params.amountSpecified = i256.FromUint256(amountOut) } } @@ -59,11 +58,12 @@ func multiSwapNegative(params SwapParams, numPools int, swapPath string) (*u256. firstAmountIn := u256.Zero() swapInfo := []SingleSwapParams{} - currentPoolIndex := numPools // CALCULATE BACKWARD INFO - for { - amountIn, _ := singleSwap( + currentPoolIndex := numPools + for currentPoolIndex >= 0 { + // Dry-run + amountIn, _ := singleDrySwap( SingleSwapParams{ tokenIn: params.tokenIn, tokenOut: params.tokenOut, @@ -78,32 +78,29 @@ func multiSwapNegative(params SwapParams, numPools int, swapPath string) (*u256. fee: params.fee, amountSpecified: params.amountSpecified, } - swapInfo = append(swapInfo, thisSwap) if currentPoolIndex == 0 { break - } else { - currentPoolIndex-- + } + currentPoolIndex-- - nextInput, nextOutput, nextFee := getDataForMultiPath(swapPath, currentPoolIndex) - intAmountIn := i256.FromUint256(amountIn) + nextInput, nextOutput, nextFee := getDataForMultiPath(swapPath, currentPoolIndex) - params.tokenIn = nextInput - params.tokenOut = nextOutput - params.fee = nextFee - params.amountSpecified = i256.Zero().Neg(intAmountIn) - } + intAmountIn := i256.FromUint256(amountIn) + params.tokenIn = nextInput + params.tokenOut = nextOutput + params.fee = nextFee + params.amountSpecified = i256.Zero().Neg(intAmountIn) } // PROCESS FORWARD INFO - currentPoolIndex = len(swapInfo) - payer := std.PrevRealm().Addr() // first payer ≈ user - for { + currentPoolIndex = len(swapInfo) - 1 + payer := getPrevAddr() // first payer ≈ user + for currentPoolIndex >= 0 { var recipient std.Address - currentPoolIndex-- if currentPoolIndex == 0 { - recipient = std.PrevRealm().Addr() // params.recipient ≈ user + recipient = params.recipient // params.recipient ≈ user } else { recipient = consts.ROUTER_ADDR } @@ -129,7 +126,83 @@ func multiSwapNegative(params SwapParams, numPools int, swapPath string) (*u256. return firstAmountIn, amountOut } - payer = consts.ROUTER_ADDR swapInfo[currentPoolIndex-1].amountSpecified = i256.FromUint256(amountOut) + payer = consts.ROUTER_ADDR + currentPoolIndex-- + } + return firstAmountIn, u256.Zero() +} + +func multiDrySwap(params SwapParams, currentPoolIndex, numPool int, swapPath string) (*u256.Uint, *u256.Uint) { // firstAmountIn, lastAmountOut + firstAmountIn := u256.Zero() + + payer := getPrevAddr() // user + + for { + currentPoolIndex++ + + amountIn, amountOut := swapDryInner( + params.amountSpecified, + u256.Zero(), + SwapCallbackData{ + params.tokenIn, + params.tokenOut, + params.fee, + payer, + }, + ) + + if currentPoolIndex == 1 { + firstAmountIn = amountIn + } + + if currentPoolIndex >= numPool { + return firstAmountIn, amountOut + } + + payer = consts.ROUTER_ADDR + + nextInput, nextOutput, nextFee := getDataForMultiPath(swapPath, currentPoolIndex) + params.tokenIn = nextInput + params.tokenOut = nextOutput + params.fee = nextFee + params.amountSpecified = i256.FromUint256(amountOut) + } + +} + +func multiDrySwapNegative(params SwapParams, currentPoolIndex int, swapPath string) (*u256.Uint, *u256.Uint) { + firstAmountIn := u256.Zero() + payer := consts.ROUTER_ADDR + + for { + amountIn, amountOut := swapDryInner( + params.amountSpecified, + u256.Zero(), + SwapCallbackData{ + params.tokenIn, + params.tokenOut, + params.fee, + payer, + }, + ) + + if currentPoolIndex == 0 { + firstAmountIn = amountIn + } + + currentPoolIndex-- + + if currentPoolIndex == -1 { + return firstAmountIn, amountOut + } + + nextInput, nextOutput, nextFee := getDataForMultiPath(swapPath, currentPoolIndex) + intAmountIn := i256.FromUint256(amountIn) + + params.amountSpecified = i256.Zero().Neg(intAmountIn) + params.tokenIn = nextInput + params.tokenOut = nextOutput + params.fee = nextFee } } diff --git a/router/swap_single.gno b/router/swap_single.gno index 0324f7cb6..87b743612 100644 --- a/router/swap_single.gno +++ b/router/swap_single.gno @@ -1,9 +1,9 @@ package router import ( - "std" - u256 "gno.land/p/gnoswap/uint256" + + "gno.land/r/gnoswap/v1/common" ) // singleSwap execute a swap within a single pool using the provided parameters. @@ -24,15 +24,52 @@ import ( // The function uses swapInner for the core swap logic and sets the proce limit to 0, // allowing the swap to execute at any price point within slippage bounds. func singleSwap(params SingleSwapParams) (*u256.Uint, *u256.Uint) { // amountIn, amountOut + common.MustRegistered(params.tokenIn) + common.MustRegistered(params.tokenOut) + amountIn, amountOut := swapInner( params.amountSpecified, - std.PrevRealm().Addr(), // if single swap => user will recieve - u256.Zero(), // sqrtPriceLimitX96 + getPrevAddr(), // if single swap => user will recieve + u256.Zero(), // sqrtPriceLimitX96 + SwapCallbackData{ + params.tokenIn, + params.tokenOut, + params.fee, + getPrevAddr(), // payer ==> msg.sender, + }, + ) + + return amountIn, amountOut +} + +// singleDrySwap simulates a single-token swap operation without executing it, +// returning the calculated input and output token amounts. +// +// Parameters: +// - params: A `SingleSwapParams` structure containing the following fields: +// - `tokenIn`: The address of the input token. +// - `tokenOut`: The address of the output token. +// - `amountSpecified`: The amount specified for the swap (input or output amount depending on the swap direction). +// - `fee`: The fee rate applied to the swap. +// +// Returns: +// - *u256.Uint: The calculated amount of the input token required for the swap (`amountIn`). +// - *u256.Uint: The calculated amount of the output token received from the swap (`amountOut`). +// +// Notes: +// - This function performs a simulation and does not alter the state or execute the actual swap. +func singleDrySwap(params SingleSwapParams) (*u256.Uint, *u256.Uint) { + common.MustRegistered(params.tokenIn) + common.MustRegistered(params.tokenOut) + + amountIn, amountOut := swapDryInner( + params.amountSpecified, + u256.Zero(), SwapCallbackData{ params.tokenIn, params.tokenOut, params.fee, - std.PrevRealm().Addr(), // payer ==> msg.sender, + getPrevAddr(), }, ) diff --git a/router/tests/__TEST_router_all_2_route_2_hop_test.gnoA b/router/tests/__TEST_router_all_2_route_2_hop_test.gnoA index da2c5d3f0..249796b6f 100644 --- a/router/tests/__TEST_router_all_2_route_2_hop_test.gnoA +++ b/router/tests/__TEST_router_all_2_route_2_hop_test.gnoA @@ -1,8 +1,9 @@ -package router +package tests import ( "std" "testing" + "time" "gno.land/p/demo/uassert" @@ -35,21 +36,24 @@ func TestPositionMint(t *testing.T) { qux.Approve(a2u(consts.POOL_ADDR), consts.UINT64_MAX) // Mint - pn.Mint(barPath, bazPath, uint32(500), int32(9000), int32(11000), "100000", "100000", "0", "0", max_timeout, adminAddr, adminAddr) - pn.Mint(bazPath, quxPath, uint32(500), int32(9000), int32(11000), "100000", "100000", "0", "0", max_timeout, adminAddr, adminAddr) + tokenId, liquidity, amount0, amount1 := pn.Mint(barPath, bazPath, uint32(500), int32(9000), int32(11000), "100000", "100000", "0", "0", max_timeout, adminAddr, adminAddr) + tokenId, liquidity, amount0, amount1 = pn.Mint(bazPath, quxPath, uint32(500), int32(9000), int32(11000), "100000", "100000", "0", "0", max_timeout, adminAddr, adminAddr) } func TestDrySwapRouteBarQuxExactIn(t *testing.T) { + std.TestSetRealm(adminRealm) + dryResult := DrySwapRoute( barPath, // inputToken quxPath, // outputToken "1000", // amountSpecified "EXACT_IN", // swapType - "gno.land/r/onbloc/bar:gno.land/r/onbloc/baz:500*POOL*gno.land/r/onbloc/baz:gno.land/r/onbloc/qux:500,gno.land/r/onbloc/bar:gno.land/r/onbloc/baz:500*POOL*gno.land/r/onbloc/baz:gno.land/r/onbloc/qux:500", // strRouteArr - "50,50", // quoteArr + "gno.land/r/onbloc/bar:gno.land/r/onbloc/baz:500*POOL*gno.land/r/onbloc/baz:gno.land/r/onbloc/qux:500", // strRouteArr + "100", // quoteArr + "100", ) - uassert.Equal(t, dryResult, "7346") + uassert.Equal(t, dryResult, "7337") } func TestSwapRouteBarQuxExactIn(t *testing.T) { @@ -59,12 +63,13 @@ func TestSwapRouteBarQuxExactIn(t *testing.T) { qux.Approve(a2u(consts.ROUTER_ADDR), 10000) amountIn, amountOut := ExactInSwapRoute( - barPath, // inputToken - quxPath, // outputToken - "1000", // amountSpecified + barPath, // inputToken + quxPath, // outputToken + "1000", // amountSpecified "gno.land/r/onbloc/bar:gno.land/r/onbloc/baz:500*POOL*gno.land/r/onbloc/baz:gno.land/r/onbloc/qux:500,gno.land/r/onbloc/bar:gno.land/r/onbloc/baz:500*POOL*gno.land/r/onbloc/baz:gno.land/r/onbloc/qux:500", // strRouteArr "50,50", // quoteArr "1", // tokenAmountLimit + time.Now().Add(time.Hour).Unix(), ) uassert.Equal(t, amountIn, "1000") @@ -81,6 +86,7 @@ func TestDrySwapRouteBarQuxExactOut(t *testing.T) { "EXACT_OUT", // swapType "gno.land/r/onbloc/bar:gno.land/r/onbloc/baz:500*POOL*gno.land/r/onbloc/baz:gno.land/r/onbloc/qux:500,gno.land/r/onbloc/bar:gno.land/r/onbloc/baz:500*POOL*gno.land/r/onbloc/baz:gno.land/r/onbloc/qux:500", // strRouteArr "50,50", // quoteArr + "10000", ) uassert.Equal(t, dryResult, "140") @@ -90,15 +96,16 @@ func TestSwapRouteBarQuxExactOut(t *testing.T) { std.TestSetRealm(adminRealm) amountIn, amountOut := ExactOutSwapRoute( - barPath, // inputToken - quxPath, // outputToken - "1000", // amountSpecified + barPath, // inputToken + quxPath, // outputToken + "1000", // amountSpecified "gno.land/r/onbloc/bar:gno.land/r/onbloc/baz:500*POOL*gno.land/r/onbloc/baz:gno.land/r/onbloc/qux:500,gno.land/r/onbloc/bar:gno.land/r/onbloc/baz:500*POOL*gno.land/r/onbloc/baz:gno.land/r/onbloc/qux:500", // strRouteArr - "50,50", // quoteArr + "60,40", // quoteArr "99999", // tokenAmountLimit + time.Now().Add(time.Hour).Unix(), ) - uassert.Equal(t, amountIn, "140") + uassert.Equal(t, amountIn, "141") uassert.Equal(t, amountOut, "-1001") } @@ -112,6 +119,7 @@ func TestDrySwapRouteQuxBarExactIn(t *testing.T) { "EXACT_IN", // swapType "gno.land/r/onbloc/qux:gno.land/r/onbloc/baz:500*POOL*gno.land/r/onbloc/baz:gno.land/r/onbloc/bar:500,gno.land/r/onbloc/qux:gno.land/r/onbloc/baz:500*POOL*gno.land/r/onbloc/baz:gno.land/r/onbloc/bar:500", // strRouteArr "30,70", // quoteArr + "1", ) uassert.Equal(t, dryResult, "135") @@ -121,12 +129,13 @@ func TestSwapRouteQuxBarExactIn(t *testing.T) { std.TestSetRealm(adminRealm) amountIn, amountOut := ExactInSwapRoute( - quxPath, // inputToken - barPath, // outputToken - "1000", // amountSpecified + quxPath, // inputToken + barPath, // outputToken + "1000", // amountSpecified "gno.land/r/onbloc/qux:gno.land/r/onbloc/baz:500*POOL*gno.land/r/onbloc/baz:gno.land/r/onbloc/bar:500,gno.land/r/onbloc/qux:gno.land/r/onbloc/baz:500*POOL*gno.land/r/onbloc/baz:gno.land/r/onbloc/bar:500", // strRouteArr "30,70", // quoteArr "1", // tokenAmountLimit + time.Now().Add(time.Hour).Unix(), ) uassert.Equal(t, amountIn, "1000") @@ -143,6 +152,7 @@ func TestDrySwapRouteQuxBarExactOut(t *testing.T) { "EXACT_OUT", // swapType "gno.land/r/onbloc/qux:gno.land/r/onbloc/baz:500*POOL*gno.land/r/onbloc/baz:gno.land/r/onbloc/bar:500,gno.land/r/onbloc/qux:gno.land/r/onbloc/baz:500*POOL*gno.land/r/onbloc/baz:gno.land/r/onbloc/bar:500", // strRouteArr "30,70", // quoteArr + "100000", ) uassert.Equal(t, dryResult, "7351") @@ -155,12 +165,13 @@ func TestSwapRouteQuxBarExactOut(t *testing.T) { bar.Approve(a2u(consts.ROUTER_ADDR), 10000) amountIn, amountOut := ExactOutSwapRoute( - quxPath, // inputToken - barPath, // outputToken - "1000", // amountSpecified + quxPath, // inputToken + barPath, // outputToken + "1000", // amountSpecified "gno.land/r/onbloc/qux:gno.land/r/onbloc/baz:500*POOL*gno.land/r/onbloc/baz:gno.land/r/onbloc/bar:500,gno.land/r/onbloc/qux:gno.land/r/onbloc/baz:500*POOL*gno.land/r/onbloc/baz:gno.land/r/onbloc/bar:500", // strRouteArr "30,70", // quoteArr "99999", // tokenAmountLimit + time.Now().Add(time.Hour).Unix(), ) uassert.Equal(t, amountIn, "7365") diff --git a/router/tests/__TEST_router_all_2_route_2_hop_with_emission_test.gnoA b/router/tests/__TEST_router_all_2_route_2_hop_with_emission_test.gnoA index 1822e4e30..43234badd 100644 --- a/router/tests/__TEST_router_all_2_route_2_hop_with_emission_test.gnoA +++ b/router/tests/__TEST_router_all_2_route_2_hop_with_emission_test.gnoA @@ -1,8 +1,9 @@ -package router +package tests import ( "std" "testing" + "time" "gno.land/p/demo/uassert" @@ -83,6 +84,7 @@ func testDrySwapRouteBarQuxExactIn(t *testing.T) { "EXACT_IN", // swapType "gno.land/r/onbloc/bar:gno.land/r/onbloc/baz:500*POOL*gno.land/r/onbloc/baz:gno.land/r/onbloc/qux:500,gno.land/r/onbloc/bar:gno.land/r/onbloc/baz:500*POOL*gno.land/r/onbloc/baz:gno.land/r/onbloc/qux:500", // strRouteArr "50,50", // quoteArr + "1", ) uassert.Equal(t, dryResult, "7346") @@ -97,12 +99,13 @@ func testSwapRouteBarQuxExactIn(t *testing.T) { qux.Approve(a2u(consts.ROUTER_ADDR), 10000) amountIn, amountOut := ExactInSwapRoute( - barPath, // inputToken - quxPath, // outputToken - "1000", // amountSpecified + barPath, // inputToken + quxPath, // outputToken + "1000", // amountSpecified "gno.land/r/onbloc/bar:gno.land/r/onbloc/baz:500*POOL*gno.land/r/onbloc/baz:gno.land/r/onbloc/qux:500,gno.land/r/onbloc/bar:gno.land/r/onbloc/baz:500*POOL*gno.land/r/onbloc/baz:gno.land/r/onbloc/qux:500", // strRouteArr "50,50", // quoteArr "1", // tokenAmountLimit + time.Now().Add(time.Hour).Unix(), ) std.TestSkipHeights(1) @@ -127,6 +130,7 @@ func testDrySwapRouteBarQuxExactOut(t *testing.T) { "EXACT_OUT", // swapType "gno.land/r/onbloc/bar:gno.land/r/onbloc/baz:500*POOL*gno.land/r/onbloc/baz:gno.land/r/onbloc/qux:500,gno.land/r/onbloc/bar:gno.land/r/onbloc/baz:500*POOL*gno.land/r/onbloc/baz:gno.land/r/onbloc/qux:500", // strRouteArr "50,50", // quoteArr + "100000", ) uassert.Equal(t, dryResult, "140") @@ -138,12 +142,13 @@ func testSwapRouteBarQuxExactOut(t *testing.T) { std.TestSetRealm(adminRealm) amountIn, amountOut := ExactOutSwapRoute( - barPath, // inputToken - quxPath, // outputToken - "1000", // amountSpecified + barPath, // inputToken + quxPath, // outputToken + "1000", // amountSpecified "gno.land/r/onbloc/bar:gno.land/r/onbloc/baz:500*POOL*gno.land/r/onbloc/baz:gno.land/r/onbloc/qux:500,gno.land/r/onbloc/bar:gno.land/r/onbloc/baz:500*POOL*gno.land/r/onbloc/baz:gno.land/r/onbloc/qux:500", // strRouteArr "50,50", // quoteArr "99999", // tokenAmountLimit + time.Now().Add(time.Hour).Unix(), ) std.TestSkipHeights(1) @@ -168,6 +173,7 @@ func testDrySwapRouteQuxBarExactIn(t *testing.T) { "EXACT_IN", // swapType "gno.land/r/onbloc/qux:gno.land/r/onbloc/baz:500*POOL*gno.land/r/onbloc/baz:gno.land/r/onbloc/bar:500,gno.land/r/onbloc/qux:gno.land/r/onbloc/baz:500*POOL*gno.land/r/onbloc/baz:gno.land/r/onbloc/bar:500", // strRouteArr "30,70", // quoteArr + "1", ) uassert.Equal(t, dryResult, "135") @@ -179,12 +185,13 @@ func testSwapRouteQuxBarExactIn(t *testing.T) { std.TestSetRealm(adminRealm) amountIn, amountOut := ExactInSwapRoute( - quxPath, // inputToken - barPath, // outputToken - "1000", // amountSpecified + quxPath, // inputToken + barPath, // outputToken + "1000", // amountSpecified "gno.land/r/onbloc/qux:gno.land/r/onbloc/baz:500*POOL*gno.land/r/onbloc/baz:gno.land/r/onbloc/bar:500,gno.land/r/onbloc/qux:gno.land/r/onbloc/baz:500*POOL*gno.land/r/onbloc/baz:gno.land/r/onbloc/bar:500", // strRouteArr "30,70", // quoteArr "1", // tokenAmountLimit + time.Now().Add(time.Hour).Unix(), ) std.TestSkipHeights(1) @@ -209,6 +216,7 @@ func testDrySwapRouteQuxBarExactOut(t *testing.T) { "EXACT_OUT", // swapType "gno.land/r/onbloc/qux:gno.land/r/onbloc/baz:500*POOL*gno.land/r/onbloc/baz:gno.land/r/onbloc/bar:500,gno.land/r/onbloc/qux:gno.land/r/onbloc/baz:500*POOL*gno.land/r/onbloc/baz:gno.land/r/onbloc/bar:500", // strRouteArr "30,70", // quoteArr + "100000", ) uassert.Equal(t, dryResult, "7351") @@ -223,12 +231,13 @@ func testSwapRouteQuxBarExactOut(t *testing.T) { bar.Approve(a2u(consts.ROUTER_ADDR), 10000) amountIn, amountOut := ExactOutSwapRoute( - quxPath, // inputToken - barPath, // outputToken - "1000", // amountSpecified + quxPath, // inputToken + barPath, // outputToken + "1000", // amountSpecified "gno.land/r/onbloc/qux:gno.land/r/onbloc/baz:500*POOL*gno.land/r/onbloc/baz:gno.land/r/onbloc/bar:500,gno.land/r/onbloc/qux:gno.land/r/onbloc/baz:500*POOL*gno.land/r/onbloc/baz:gno.land/r/onbloc/bar:500", // strRouteArr "30,70", // quoteArr "99999", // tokenAmountLimit + time.Now().Add(time.Hour).Unix(), ) std.TestSkipHeights(1) diff --git a/router/tests/__TEST_router_native_swap_amount_check_test.gnoA b/router/tests/__TEST_router_native_swap_amount_check_test.gnoA index ff16504e2..fcffe3c92 100644 --- a/router/tests/__TEST_router_native_swap_amount_check_test.gnoA +++ b/router/tests/__TEST_router_native_swap_amount_check_test.gnoA @@ -1,8 +1,9 @@ -package router +package tests import ( "std" "testing" + "time" "gno.land/p/demo/json" "gno.land/p/demo/uassert" @@ -59,6 +60,7 @@ func TestSwapRouteWugnotquxExactIn(t *testing.T) { "gno.land/r/demo/wugnot:gno.land/r/onbloc/qux:500", // strRouteArr "100", // quoteArr "1", // tokenAmountLimit + time.Now().Add(time.Hour).Unix(), ) }, ) diff --git a/router/tests/__TEST_router_spec_#1_ExactIn_test.gnoA b/router/tests/__TEST_router_spec_#1_ExactIn_test.gnoA index bea70ef98..0cf2d3cdd 100644 --- a/router/tests/__TEST_router_spec_#1_ExactIn_test.gnoA +++ b/router/tests/__TEST_router_spec_#1_ExactIn_test.gnoA @@ -1,8 +1,9 @@ -package router +package tests import ( "std" "testing" + "time" "gno.land/p/demo/uassert" @@ -71,12 +72,13 @@ func TestExactInputSinglePool(t *testing.T) { swapFee = uint64(0) amountIn, amountOut := ExactInSwapRoute( - barPath, // inputToken - bazPath, // outputToken - "3", // amountSpecified - poolPath, // strRouteArr - "100", // quoteArr - "1", // tokenAmountLimit + barPath, // inputToken + bazPath, // outputToken + "3", // amountSpecified + poolPath, // strRouteArr + "100", // quoteArr + "1", // tokenAmountLimit + time.Now().Add(time.Hour).Unix(), ) uassert.Equal(t, amountIn, "3") diff --git a/router/tests/__TEST_router_spec_#2_ExactIn_test.gnoA b/router/tests/__TEST_router_spec_#2_ExactIn_test.gnoA index 534ca2351..2e75c3350 100644 --- a/router/tests/__TEST_router_spec_#2_ExactIn_test.gnoA +++ b/router/tests/__TEST_router_spec_#2_ExactIn_test.gnoA @@ -1,8 +1,9 @@ -package router +package tests import ( "std" "testing" + "time" "gno.land/p/demo/uassert" @@ -48,12 +49,13 @@ func TestExactInputSinglePool1_to_0(t *testing.T) { user1Token1Before := foo.BalanceOf(a2u(consts.ADMIN)) amountIn, amountOut := ExactInSwapRoute( - barPath, // inputToken - fooPath, // outputToken - "3", // amountSpecified + barPath, // inputToken + fooPath, // outputToken + "3", // amountSpecified "gno.land/r/onbloc/foo:gno.land/r/onbloc/bar:3000", // strRouteArr "100", // quoteArr "1", // tokenAmountLimit + time.Now().Add(time.Hour).Unix(), ) uassert.Equal(t, amountIn, "3") diff --git a/router/tests/__TEST_router_spec_#3_ExactIn_test.gnoA b/router/tests/__TEST_router_spec_#3_ExactIn_test.gnoA index 776e9d6d3..95480303a 100644 --- a/router/tests/__TEST_router_spec_#3_ExactIn_test.gnoA +++ b/router/tests/__TEST_router_spec_#3_ExactIn_test.gnoA @@ -1,8 +1,9 @@ -package router +package tests import ( "std" "testing" + "time" "gno.land/p/demo/uassert" @@ -57,12 +58,13 @@ func TestSwapRouteFooBarExactIn(t *testing.T) { token2Before := foo.BalanceOf(a2u(consts.ADMIN)) amountIn, amountOut := ExactInSwapRoute( - fooPath, // inputToken - barPath, // outputToken - "5", // amountSpecified + fooPath, // inputToken + barPath, // outputToken + "5", // amountSpecified "gno.land/r/onbloc/foo:gno.land/r/onbloc/baz:3000*POOL*gno.land/r/onbloc/baz:gno.land/r/onbloc/bar:3000", // strRouteArr "100", // quoteArr "1", // tokenAmountLimit + time.Now().Add(time.Hour).Unix(), ) token0After := bar.BalanceOf(a2u(consts.ADMIN)) diff --git a/router/tests/__TEST_router_spec_#4_ExactIn_test.gnoA b/router/tests/__TEST_router_spec_#4_ExactIn_test.gnoA index 56ff32609..1a0e96395 100644 --- a/router/tests/__TEST_router_spec_#4_ExactIn_test.gnoA +++ b/router/tests/__TEST_router_spec_#4_ExactIn_test.gnoA @@ -1,8 +1,9 @@ -package router +package tests import ( "std" "testing" + "time" "gno.land/p/demo/uassert" @@ -57,12 +58,13 @@ func TestSwapRouteBarfooExactIn(t *testing.T) { token2Before := foo.BalanceOf(a2u(consts.ADMIN)) amountIn, amountOut := ExactInSwapRoute( - barPath, // inputToken - fooPath, // outputToken - "5", // amountSpecified + barPath, // inputToken + fooPath, // outputToken + "5", // amountSpecified "gno.land/r/onbloc/bar:gno.land/r/onbloc/baz:3000*POOL*gno.land/r/onbloc/baz:gno.land/r/onbloc/foo:3000", // strRouteArr "100", // quoteArr "1", // tokenAmountLimit + time.Now().Add(time.Hour).Unix(), ) token0After := bar.BalanceOf(a2u(consts.ADMIN)) diff --git a/router/tests/__TEST_router_spec_#5_ExactOut_test.gnoA b/router/tests/__TEST_router_spec_#5_ExactOut_test.gnoA index f026300fd..dcc3cf361 100644 --- a/router/tests/__TEST_router_spec_#5_ExactOut_test.gnoA +++ b/router/tests/__TEST_router_spec_#5_ExactOut_test.gnoA @@ -1,8 +1,9 @@ -package router +package tests import ( "std" "testing" + "time" "gno.land/p/demo/uassert" @@ -48,12 +49,13 @@ func TestSwapRouteBarBazExactOut(t *testing.T) { token1Before := baz.BalanceOf(a2u(consts.ADMIN)) amountIn, amountOut := ExactOutSwapRoute( - barPath, // inputToken - bazPath, // outputToken - "1", // amountSpecified + barPath, // inputToken + bazPath, // outputToken + "1", // amountSpecified "gno.land/r/onbloc/bar:gno.land/r/onbloc/baz:3000", // strRouteArr "100", // quoteArr "3", // tokenAmountLimit + time.Now().Add(time.Hour).Unix(), ) token0After := bar.BalanceOf(a2u(consts.ADMIN)) @@ -89,7 +91,7 @@ func TestSwapRouteWugnotquxExactInDifferentAmountCoinShouldPanic(t *testing.T) { uassert.PanicsWithMessage( t, - `[GNOSWAP-ROUTER-005] invalid input || router.gno__SwapRoute() || ugnot sent by user(12345) is not equal to amountSpecified(3)`, + `[GNOSWAP-POOL-008] requested data not found || expected poolPath(gno.land/r/demo/wugnot:gno.land/r/onbloc/qux:3000) to exist`, func() { ExactOutSwapRoute( consts.GNOT, // inputToken @@ -98,6 +100,7 @@ func TestSwapRouteWugnotquxExactInDifferentAmountCoinShouldPanic(t *testing.T) { "gno.land/r/demo/wugnot:gno.land/r/onbloc/qux:3000", // strRouteArr "100", // quoteArr "1", // tokenAmountLimit + time.Now().Add(time.Hour).Unix(), ) }, ) diff --git a/router/tests/__TEST_router_spec_#6_ExactOut_test.gnoA b/router/tests/__TEST_router_spec_#6_ExactOut_test.gnoA index ee52f2f30..fa5e95c67 100644 --- a/router/tests/__TEST_router_spec_#6_ExactOut_test.gnoA +++ b/router/tests/__TEST_router_spec_#6_ExactOut_test.gnoA @@ -1,8 +1,9 @@ -package router +package tests import ( "std" "testing" + "time" "gno.land/p/demo/uassert" @@ -46,12 +47,13 @@ func TestSwapRouteBazBarExactOut(t *testing.T) { token1Before := baz.BalanceOf(a2u(consts.ADMIN)) amountIn, amountOut := ExactOutSwapRoute( - bazPath, // inputToken - barPath, // outputToken - "1", // amountSpecified + bazPath, // inputToken + barPath, // outputToken + "1", // amountSpecified "gno.land/r/onbloc/baz:gno.land/r/onbloc/bar:3000", // strRouteArr "100", // quoteArr "3", // tokenAmountLimit + time.Now().Add(time.Hour).Unix(), ) token0After := bar.BalanceOf(a2u(consts.ADMIN)) diff --git a/router/tests/__TEST_router_spec_#7_ExactOut_test.gnoA b/router/tests/__TEST_router_spec_#7_ExactOut_test.gnoA index ad86bcf98..9d8b99922 100644 --- a/router/tests/__TEST_router_spec_#7_ExactOut_test.gnoA +++ b/router/tests/__TEST_router_spec_#7_ExactOut_test.gnoA @@ -1,8 +1,9 @@ -package router +package tests import ( "std" "testing" + "time" "gno.land/p/demo/uassert" @@ -55,12 +56,13 @@ func TestSwapRouteBarfooExactOut(t *testing.T) { token2Before := foo.BalanceOf(a2u(consts.ADMIN)) amountIn, amountOut := ExactOutSwapRoute( - barPath, // inputToken - fooPath, // outputToken - "1", // amountSpecified + barPath, // inputToken + fooPath, // outputToken + "1", // amountSpecified "gno.land/r/onbloc/bar:gno.land/r/onbloc/baz:3000*POOL*gno.land/r/onbloc/baz:gno.land/r/onbloc/foo:3000", // strRouteArr "100", // quoteArr "5", // tokenAmountLimit + time.Now().Add(time.Hour).Unix(), ) token0After := bar.BalanceOf(a2u(consts.ADMIN)) diff --git a/router/tests/__TEST_router_spec_#8_ExactOut_test.gnoA b/router/tests/__TEST_router_spec_#8_ExactOut_test.gnoA index b9899fb12..0ef832525 100644 --- a/router/tests/__TEST_router_spec_#8_ExactOut_test.gnoA +++ b/router/tests/__TEST_router_spec_#8_ExactOut_test.gnoA @@ -1,8 +1,9 @@ -package router +package tests import ( "std" "testing" + "time" "gno.land/p/demo/uassert" @@ -56,12 +57,13 @@ func TestSwapRouteFooBarExactOut(t *testing.T) { token2Before := foo.BalanceOf(a2u(consts.ADMIN)) amountIn, amountOut := ExactOutSwapRoute( - fooPath, // inputToken - barPath, // outputToken - "1", // amountSpecified + fooPath, // inputToken + barPath, // outputToken + "1", // amountSpecified "gno.land/r/onbloc/foo:gno.land/r/onbloc/baz:3000*POOL*gno.land/r/onbloc/baz:gno.land/r/onbloc/bar:3000", // strRouteArr "100", // quoteArr "5", // tokenAmountLimit + time.Now().Add(time.Hour).Unix(), ) token0After := bar.BalanceOf(a2u(consts.ADMIN)) diff --git a/router/tests/__TEST_router_swap_route_1route_1hop_all_liquidity_exact_in_test.gnoA b/router/tests/__TEST_router_swap_route_1route_1hop_all_liquidity_exact_in_test.gnoA index b6ae1f608..a2f912c93 100644 --- a/router/tests/__TEST_router_swap_route_1route_1hop_all_liquidity_exact_in_test.gnoA +++ b/router/tests/__TEST_router_swap_route_1route_1hop_all_liquidity_exact_in_test.gnoA @@ -1,8 +1,9 @@ -package router +package tests import ( "std" "testing" + "time" "gno.land/p/demo/uassert" @@ -50,6 +51,9 @@ func TestPositionMint(t *testing.T) { func TestSwapRouteBarBazExactIn(t *testing.T) { poolPath := "gno.land/r/onbloc/bar:gno.land/r/onbloc/baz:500" + CreatePoolWithoutFee(t) + MakeForthMintPositionWithoutFee(t) + std.TestSetRealm(adminRealm) bar.Approve(a2u(consts.POOL_ADDR), consts.UINT64_MAX) @@ -57,21 +61,22 @@ func TestSwapRouteBarBazExactIn(t *testing.T) { // spend all baz in pool amountIn, amountOut := ExactInSwapRoute( - barPath, // inputToken - bazPath, // outputToken - "140000", // amountSpecified - poolPath, // strRouteArr - "100", // quoteArr - "0", // tokenAmountLimit + barPath, // inputToken + bazPath, // outputToken + "140000", // amountSpecified + poolPath, // strRouteArr + "100", // quoteArr + "0", // tokenAmountLimit + time.Now().Add(time.Hour).Unix(), ) - uassert.Equal(t, amountIn, "135049") - uassert.Equal(t, amountOut, "-99848") + uassert.Equal(t, amountIn, "140000") + uassert.Equal(t, amountOut, "-105765") pool := pl.GetPool(barPath, bazPath, fee500) poolLiq := pool.Liquidity() - uassert.Equal(t, poolLiq.ToString(), "0") + uassert.Equal(t, poolLiq.ToString(), "435768") poolTick := pl.PoolGetSlot0Tick(poolPath) - uassert.Equal(t, poolTick, int32(-887270)) + uassert.Equal(t, poolTick, int32(-5569)) } diff --git a/router/tests/__TEST_router_swap_route_1route_1hop_all_liquidity_exact_out_test.gnoA b/router/tests/__TEST_router_swap_route_1route_1hop_all_liquidity_exact_out_test.gnoA index 1dc7598ed..a71a16871 100644 --- a/router/tests/__TEST_router_swap_route_1route_1hop_all_liquidity_exact_out_test.gnoA +++ b/router/tests/__TEST_router_swap_route_1route_1hop_all_liquidity_exact_out_test.gnoA @@ -1,8 +1,9 @@ -package router +package tests import ( "std" "testing" + "time" "gno.land/p/demo/uassert" @@ -50,6 +51,9 @@ func TestPositionMint(t *testing.T) { func TestSwapRouteBarBazExactOut(t *testing.T) { poolPath := "gno.land/r/onbloc/bar:gno.land/r/onbloc/baz:500" + CreatePoolWithoutFee(t) + MakeForthMintPositionWithoutFee(t) + std.TestSetRealm(adminRealm) bar.Approve(a2u(consts.POOL_ADDR), consts.UINT64_MAX) @@ -57,15 +61,16 @@ func TestSwapRouteBarBazExactOut(t *testing.T) { uassert.PanicsWithMessage( t, - `[GNOSWAP-ROUTER-012] slippage || router.gno__finalizeSwap() || too few received for user (expected minimum: 120000, actual: 99997, swapType: EXACT_OUT)`, + `[GNOSWAP-ROUTER-002] slippage check failed || ExactOut: too much spent (max:0, used:168406)`, func() { amountIn, amountOut := ExactOutSwapRoute( - barPath, // inputToken - bazPath, // outputToken - "120000", // amountSpecified - poolPath, // strRouteArr - "100", // quoteArr - "0", // tokenAmountLimit + barPath, // inputToken + bazPath, // outputToken + "120000", // amountSpecified + poolPath, // strRouteArr + "100", // quoteArr + "0", // tokenAmountLimit + time.Now().Add(time.Hour).Unix(), ) }, ) diff --git a/router/tests/__TEST_router_swap_route_1route_1hop_native_in_out_test_exact_in_test.gnoA b/router/tests/__TEST_router_swap_route_1route_1hop_native_in_out_test_exact_in_test.gnoA index 3d200f149..ad399ef8d 100644 --- a/router/tests/__TEST_router_swap_route_1route_1hop_native_in_out_test_exact_in_test.gnoA +++ b/router/tests/__TEST_router_swap_route_1route_1hop_native_in_out_test_exact_in_test.gnoA @@ -1,4 +1,4 @@ -package router +package tests import ( "std" diff --git a/router/tests/__TEST_router_swap_route_1route_1hop_out_range_test.gnoA b/router/tests/__TEST_router_swap_route_1route_1hop_out_range_test.gnoA index 20e6b4082..fc6adc1a4 100644 --- a/router/tests/__TEST_router_swap_route_1route_1hop_out_range_test.gnoA +++ b/router/tests/__TEST_router_swap_route_1route_1hop_out_range_test.gnoA @@ -1,8 +1,9 @@ -package router +package tests import ( "std" "testing" + "time" "gno.land/p/demo/json" "gno.land/p/demo/uassert" @@ -66,6 +67,7 @@ func TestDrySwapRouteBazBarExactIn(t *testing.T) { "EXACT_IN", // swapType "gno.land/r/onbloc/baz:gno.land/r/onbloc/bar:500", // strRouteArr "100", // quoteArr + "1", // tokenAmountLimit ) uassert.Equal(t, dryResult, "367") @@ -76,15 +78,16 @@ func TestSwapRouteBazBarExactIn(t *testing.T) { uassert.PanicsWithMessage( t, - `[GNOSWAP-ROUTER-012] slippage || router.gno__finalizeSwap() || too few received for user (expected minimum: 2710, actual: 367, swapType: EXACT_IN)`, + `[GNOSWAP-ROUTER-002] slippage check failed || ExactIn: too few received (min:2710, got:367)`, func() { ExactInSwapRoute( - bazPath, // inputToken - barPath, // outputToken - "1000", // amountSpecified + bazPath, // inputToken + barPath, // outputToken + "1000", // amountSpecified "gno.land/r/onbloc/baz:gno.land/r/onbloc/bar:500", // strRouteArr "100", // quoteArr "2710", // tokenAmountLimit ( too few recieved (expected 2710, got 300)) + time.Now().Add(time.Hour).Unix(), ) }, ) diff --git a/router/tests/__TEST_router_swap_route_1route_1hop_test.gnoA b/router/tests/__TEST_router_swap_route_1route_1hop_test.gnoA index 10958bbc2..c1a07948d 100644 --- a/router/tests/__TEST_router_swap_route_1route_1hop_test.gnoA +++ b/router/tests/__TEST_router_swap_route_1route_1hop_test.gnoA @@ -1,8 +1,9 @@ -package router +package tests import ( "std" "testing" + "time" "gno.land/p/demo/uassert" @@ -52,6 +53,7 @@ func TestDrySwapRouteBarBazExactIn(t *testing.T) { "EXACT_IN", // swapType "gno.land/r/onbloc/bar:gno.land/r/onbloc/baz:500", // strRouteArr "100", // quoteArr + "1", ) uassert.Equal(t, dryResult, "2711") @@ -64,12 +66,13 @@ func TestSwapRouteBarBazExactIn(t *testing.T) { baz.Approve(a2u(consts.ROUTER_ADDR), consts.UINT64_MAX) // ITS FOR 0.15% fee amountIn, amountOut := ExactInSwapRoute( - barPath, // inputToken - bazPath, // outputToken - "1000", // amountSpecified + barPath, // inputToken + bazPath, // outputToken + "1000", // amountSpecified "gno.land/r/onbloc/bar:gno.land/r/onbloc/baz:500", // strRouteArr "100", // quoteArr "2700", // tokenAmountLimit + time.Now().Add(time.Hour).Unix(), ) uassert.Equal(t, amountIn, "1000") @@ -83,12 +86,13 @@ func TestSwapRouteBarBazExactOut(t *testing.T) { baz.Approve(a2u(consts.ROUTER_ADDR), consts.UINT64_MAX) // ITS FOR 0.15% fee amountIn, amountOut := ExactOutSwapRoute( - barPath, // inputToken - bazPath, // outputToken - "1000", // amountSpecified + barPath, // inputToken + bazPath, // outputToken + "1000", // amountSpecified "gno.land/r/onbloc/bar:gno.land/r/onbloc/baz:500", // strRouteArr "100", // quoteArr "371", // tokenAmountLimit + time.Now().Add(time.Hour).Unix(), ) uassert.Equal(t, amountIn, "371") @@ -102,12 +106,13 @@ func TestSwapRouteBazBarExactIn(t *testing.T) { bar.Approve(a2u(consts.ROUTER_ADDR), consts.UINT64_MAX) // ITS FOR 0.15% fee amountIn, amountOut := ExactInSwapRoute( - bazPath, // inputToken - barPath, // outputToken - "1000", // amountSpecified + bazPath, // inputToken + barPath, // outputToken + "1000", // amountSpecified "gno.land/r/onbloc/baz:gno.land/r/onbloc/bar:500", // strRouteArr "100", // quoteArr "360", // tokenAmountLimit + time.Now().Add(time.Hour).Unix(), ) uassert.Equal(t, amountIn, "1000") @@ -119,12 +124,13 @@ func TestSwapRouteBazBarExactOut(t *testing.T) { bar.Approve(a2u(consts.ROUTER_ADDR), consts.UINT64_MAX) amountIn, amountOut := ExactOutSwapRoute( - bazPath, // inputToken - barPath, // outputToken - "3000", // amountSpecified + bazPath, // inputToken + barPath, // outputToken + "3000", // amountSpecified "gno.land/r/onbloc/baz:gno.land/r/onbloc/bar:500", // strRouteArr "100", // quoteArr "8200", // tokenAmountLimit + time.Now().Add(time.Hour).Unix(), ) uassert.Equal(t, amountIn, "8171") diff --git a/router/tests/__TEST_router_swap_route_1route_1hop_wrapped_native_in_out_test.gnoA b/router/tests/__TEST_router_swap_route_1route_1hop_wrapped_native_in_out_test.gnoA index e2545c74b..1c5af2de4 100644 --- a/router/tests/__TEST_router_swap_route_1route_1hop_wrapped_native_in_out_test.gnoA +++ b/router/tests/__TEST_router_swap_route_1route_1hop_wrapped_native_in_out_test.gnoA @@ -1,11 +1,10 @@ -package router +package tests import ( + "gno.land/p/demo/uassert" "std" "testing" - "gno.land/p/demo/uassert" - "gno.land/r/gnoswap/v1/common" "gno.land/r/gnoswap/v1/consts" @@ -56,6 +55,7 @@ func TestDrySwapRouteQuxGnotExactIn(t *testing.T) { "EXACT_IN", // swapType "gno.land/r/onbloc/qux:gno.land/r/demo/wugnot:500", // strRouteArr "100", // quoteArr + "1", ) uassert.Equal(t, dryResult, "2711") } @@ -70,6 +70,7 @@ func TestDrySwapRouteQuxGnotExactOut(t *testing.T) { "EXACT_OUT", // swapType "gno.land/r/onbloc/qux:gno.land/r/demo/wugnot:500", // strRouteArr "100", // quoteArr + "100000", ) uassert.Equal(t, dryResult, "370") } diff --git a/router/tests/__TEST_router_swap_route_1route_2hop_wrapped_native_in_out_test.gnoA b/router/tests/__TEST_router_swap_route_1route_2hop_wrapped_native_in_out_test.gnoA index a81ac6e68..ee5920cf5 100644 --- a/router/tests/__TEST_router_swap_route_1route_2hop_wrapped_native_in_out_test.gnoA +++ b/router/tests/__TEST_router_swap_route_1route_2hop_wrapped_native_in_out_test.gnoA @@ -1,4 +1,4 @@ -package router +package tests import ( "std" @@ -91,6 +91,7 @@ func TestDrySwapRouteBarGnotExactIn(t *testing.T) { "EXACT_IN", // swapType "gno.land/r/onbloc/bar:gno.land/r/onbloc/baz:500*POOL*gno.land/r/onbloc/baz:gno.land/r/onbloc/qux:500*POOL*gno.land/r/onbloc/qux:gno.land/r/demo/wugnot:500", // strRouteArr "100", // quoteArr + "1", ) uassert.Equal(t, dryResult, "19740") } @@ -105,6 +106,7 @@ func TestDrySwapRouteBarGnotExactOut(t *testing.T) { "EXACT_OUT", // swapType "gno.land/r/onbloc/bar:gno.land/r/onbloc/baz:500*POOL*gno.land/r/onbloc/baz:gno.land/r/onbloc/qux:500*POOL*gno.land/r/onbloc/qux:gno.land/r/demo/wugnot:500", // strRouteArr "100", // quoteArr + "2000000", ) uassert.Equal(t, dryResult, "1014") } @@ -119,6 +121,7 @@ func TestDrySwapRouteGnotBarExactIn(t *testing.T) { "EXACT_IN", // swapType "gno.land/r/demo/wugnot:gno.land/r/onbloc/qux:500*POOL*gno.land/r/onbloc/qux:gno.land/r/onbloc/baz:500*POOL*gno.land/r/onbloc/baz:gno.land/r/onbloc/bar:500", // strRouteArr "100", // quoteArr + "1", ) uassert.Equal(t, dryResult, "247") } @@ -133,6 +136,7 @@ func TestDrySwapRouteGnotBarExactOut(t *testing.T) { "EXACT_OUT", // swapType "gno.land/r/demo/wugnot:gno.land/r/onbloc/qux:500*POOL*gno.land/r/onbloc/qux:gno.land/r/onbloc/baz:500*POOL*gno.land/r/onbloc/baz:gno.land/r/onbloc/bar:500", // strRouteArr "100", // quoteArr + "100000", ) uassert.Equal(t, dryResult, "2027") } diff --git a/router/tests/__TEST_router_swap_route_1route_3hop_wrapped_native_middle_test.gnoA b/router/tests/__TEST_router_swap_route_1route_3hop_wrapped_native_middle_test.gnoA index c0bee3593..7b4f4dc0a 100644 --- a/router/tests/__TEST_router_swap_route_1route_3hop_wrapped_native_middle_test.gnoA +++ b/router/tests/__TEST_router_swap_route_1route_3hop_wrapped_native_middle_test.gnoA @@ -1,8 +1,9 @@ -package router +package tests import ( "std" "testing" + "time" "gno.land/p/demo/uassert" @@ -86,6 +87,7 @@ func TestDrySwapRouteGnsBarExactIn(t *testing.T) { "EXACT_IN", // swapType "gno.land/r/gnoswap/v1/gns:gno.land/r/demo/wugnot:100*POOL*gno.land/r/demo/wugnot:gno.land/r/onbloc/bar:100", // strRouteArr "100", // quoteArr + "1", ) uassert.Equal(t, dryResult, "7327") } @@ -103,6 +105,7 @@ func TestSwapRouteGnsBarExactIn(t *testing.T) { "gno.land/r/gnoswap/v1/gns:gno.land/r/demo/wugnot:100*POOL*gno.land/r/demo/wugnot:gno.land/r/onbloc/bar:100", // strRouteArr "100", // quoteArr "0", // tokenAmountLimit + time.Now().Add(time.Hour).Unix(), ) uassert.Equal(t, amountIn, "1000") diff --git a/router/tests/__TEST_router_swap_route_2route_2hop_test.gnoA b/router/tests/__TEST_router_swap_route_2route_2hop_test.gnoA index 84daa3f5e..40dfe2c62 100644 --- a/router/tests/__TEST_router_swap_route_2route_2hop_test.gnoA +++ b/router/tests/__TEST_router_swap_route_2route_2hop_test.gnoA @@ -1,8 +1,9 @@ -package router +package tests import ( "std" "testing" + "time" "gno.land/p/demo/uassert" @@ -54,6 +55,7 @@ func TestDrySwapRouteBarQuxExactIn(t *testing.T) { "EXACT_IN", // swapType "gno.land/r/onbloc/bar:gno.land/r/onbloc/baz:500*POOL*gno.land/r/onbloc/baz:gno.land/r/onbloc/qux:500,gno.land/r/onbloc/bar:gno.land/r/onbloc/baz:500*POOL*gno.land/r/onbloc/baz:gno.land/r/onbloc/qux:500", // strRouteArr "50,50", // quoteArr + "1", ) uassert.Equal(t, dryResult, "7346") @@ -66,12 +68,13 @@ func TestSwapRouteBarQuxExactIn(t *testing.T) { qux.Approve(a2u(consts.ROUTER_ADDR), 10000) amountIn, amountOut := ExactInSwapRoute( - barPath, // inputToken - quxPath, // outputToken - "1000", // amountSpecified + barPath, // inputToken + quxPath, // outputToken + "1000", // amountSpecified "gno.land/r/onbloc/bar:gno.land/r/onbloc/baz:500*POOL*gno.land/r/onbloc/baz:gno.land/r/onbloc/qux:500,gno.land/r/onbloc/bar:gno.land/r/onbloc/baz:500*POOL*gno.land/r/onbloc/baz:gno.land/r/onbloc/qux:500", // strRouteArr "50,50", // quoteArr "1", // tokenAmountLimit + time.Now().Add(time.Hour).Unix(), ) uassert.Equal(t, amountIn, "1000") @@ -82,12 +85,13 @@ func TestSwapRouteBarQuxExactOut(t *testing.T) { std.TestSetRealm(adminRealm) amountIn, amountOut := ExactOutSwapRoute( - barPath, // inputToken - quxPath, // outputToken - "1000", // amountSpecified + barPath, // inputToken + quxPath, // outputToken + "1000", // amountSpecified "gno.land/r/onbloc/bar:gno.land/r/onbloc/baz:500*POOL*gno.land/r/onbloc/baz:gno.land/r/onbloc/qux:500,gno.land/r/onbloc/bar:gno.land/r/onbloc/baz:500*POOL*gno.land/r/onbloc/baz:gno.land/r/onbloc/qux:500", // strRouteArr "50,50", // quoteArr "99999", // tokenAmountLimit + time.Now().Add(time.Hour).Unix(), ) uassert.Equal(t, amountIn, "140") @@ -98,12 +102,13 @@ func TestSwapRouteQuxBarExactIn(t *testing.T) { std.TestSetRealm(adminRealm) amountIn, amountOut := ExactInSwapRoute( - quxPath, // inputToken - barPath, // outputToken - "1000", // amountSpecified + quxPath, // inputToken + barPath, // outputToken + "1000", // amountSpecified "gno.land/r/onbloc/qux:gno.land/r/onbloc/baz:500*POOL*gno.land/r/onbloc/baz:gno.land/r/onbloc/bar:500,gno.land/r/onbloc/qux:gno.land/r/onbloc/baz:500*POOL*gno.land/r/onbloc/baz:gno.land/r/onbloc/bar:500", // strRouteArr "30,70", // quoteArr "1", // tokenAmountLimit + time.Now().Add(time.Hour).Unix(), ) uassert.Equal(t, amountIn, "1000") @@ -117,12 +122,13 @@ func TestwapRouteQuxBarExactOut(t *testing.T) { bar.Approve(a2u(consts.ROUTER_ADDR), 10000) amountIn, amountOut := ExactOutSwapRoute( - quxPath, // inputToken - barPath, // outputToken - "1000", // amountSpecified + quxPath, // inputToken + barPath, // outputToken + "1000", // amountSpecified "gno.land/r/onbloc/qux:gno.land/r/onbloc/baz:500*POOL*gno.land/r/onbloc/baz:gno.land/r/onbloc/bar:500,gno.land/r/onbloc/qux:gno.land/r/onbloc/baz:500*POOL*gno.land/r/onbloc/baz:gno.land/r/onbloc/bar:500", // strRouteArr "30,70", // quoteArr "99999", // tokenAmountLimit + time.Now().Add(time.Hour).Unix(), ) uassert.Equal(t, amountIn, "7365") diff --git a/router/utils.gno b/router/utils.gno index 67d27c081..61aed1c15 100644 --- a/router/utils.gno +++ b/router/utils.gno @@ -5,6 +5,7 @@ import ( "std" "strconv" "strings" + "time" "gno.land/p/demo/ufmt" pusers "gno.land/p/demo/users" @@ -13,12 +14,19 @@ import ( i256 "gno.land/p/gnoswap/int256" ) +// assertOnlyNotHalted panics if the contract is halted. +func assertOnlyNotHalted() { + common.IsHalted() +} + +// assertDirectCallOnly panics if the caller is not the user. func assertDirectCallOnly() { if common.GetLimitCaller() && std.PrevRealm().PkgPath() != "" { panic(addDetailToError(errNoPermission, "only user can call this function")) } } +// assertHopsInRange panics if the number of hops is not in the range 1~3. func assertHopsInRange(hops int) { if hops < 1 || hops > 3 { panic(addDetailToError( @@ -28,6 +36,7 @@ func assertHopsInRange(hops int) { } } +// getDataForSinglePath extracts token0, token1, and fee from a single path. func getDataForSinglePath(poolPath string) (string, string, uint32) { poolPathSplit, err := common.Split(poolPath, ":", 3) if err != nil { @@ -44,6 +53,7 @@ func getDataForSinglePath(poolPath string) (string, string, uint32) { return token0, token1, uint32(fee) } +// getDataForMultiPath extracts token0, token1, and fee from a multi path. func getDataForMultiPath(possiblePath string, poolIdx int) (string, string, uint32) { pools := strings.Split(possiblePath, "*POOL*") @@ -64,6 +74,7 @@ func getDataForMultiPath(possiblePath string, poolIdx int) (string, string, uint return token0, token1, fee } +// isStringInStringArr checks if a string is in a string array. func isStringInStringArr(arr []string, str string) bool { for _, a := range arr { if a == str { @@ -73,6 +84,7 @@ func isStringInStringArr(arr []string, str string) bool { return false } +// removeStringFromStringArr removes a string from a string array. func removeStringFromStringArr(arr []string, str string) []string { for i, a := range arr { if a == str { @@ -82,10 +94,19 @@ func removeStringFromStringArr(arr []string, str string) []string { return arr } +// a2u converts std.Address to pusers.AddressOrName. +// pusers is a package that contains the user-related functions. +// +// Input: +// - addr: the address to convert +// +// Output: +// - pusers.AddressOrName: the converted address func a2u(addr std.Address) pusers.AddressOrName { return pusers.AddressOrName(addr) } +// min returns the smaller of two integers. func min(a, b int) int { if a < b { return a @@ -93,6 +114,7 @@ func min(a, b int) int { return b } +// i256Min returns the smaller of two i256.Int. func i256Min(x, y *i256.Int) *i256.Int { if x.Lt(y) { return x @@ -100,6 +122,7 @@ func i256Min(x, y *i256.Int) *i256.Int { return y } +// i256Max returns the larger of two i256.Int. func i256Max(x, y *i256.Int) *i256.Int { if x.Gt(y) { return x @@ -107,19 +130,42 @@ func i256Max(x, y *i256.Int) *i256.Int { return y } +// getPrevRealm returns object of the previous realm. func prevRealm() string { return std.PrevRealm().PkgPath() } +// getPrevAddr returns the address of the previous realm. +func getPrevAddr() std.Address { + return std.PrevRealm().Addr() +} + +// isUserCall returns true if the caller is a user. func isUserCall() bool { return std.PrevRealm().IsUser() } +// getPrev returns the address and package path of the previous realm. func getPrev() (string, string) { prev := std.PrevRealm() return prev.Addr().String(), prev.PkgPath() } +// checkDeadline checks if the deadline is expired. +// If the deadline is expired, it panics. +// The deadline is expired if the current time is greater than the deadline. +// Input: +// - deadline: the deadline to check +func checkDeadline(deadline int64) { + now := time.Now().Unix() + if now > deadline { + panic(addDetailToError( + errExpired, + ufmt.Sprintf("transaction too old, now(%d) > deadline(%d)", now, deadline), + )) + } +} + // splitSingleChar splits a string by a single character separator. // // This function is optimized for splitting strings with a single-byte separator.