From fdcf9cb7e7ad25ca5c9e84b634e81e0609219623 Mon Sep 17 00:00:00 2001 From: Blake <104744707+r3v4s@users.noreply.github.com> Date: Tue, 10 Dec 2024 09:29:52 +0900 Subject: [PATCH 01/44] GSW-2020 build: support tlin in ci (#424) * build: support tlin in ci * fix: cd * fix: ci * fix: get changed directories * feat: run tlin for changed dir * build: bump tj-action version --- .github/workflows/tlin_check.yml | 43 ++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) create mode 100644 .github/workflows/tlin_check.yml diff --git a/.github/workflows/tlin_check.yml b/.github/workflows/tlin_check.yml new file mode 100644 index 000000000..9b6f23fb0 --- /dev/null +++ b/.github/workflows/tlin_check.yml @@ -0,0 +1,43 @@ +name: tlin-check + +on: + pull_request: + branches: + - main + +jobs: + tlin-check: + runs-on: ubuntu-latest + steps: + - name: checkout code + uses: actions/checkout@v3 + with: + ref: ${{ github.event.pull_request.head.ref }} + - name: checkout tlin + uses: actions/checkout@v3 + with: + repository: gnoverse/tlin + ref: main + path: ./tlin + - name: setup go + uses: actions/setup-go@v5 + with: + go-version: 1.22 + - name: changed directories + id: changed_directories + uses: tj-actions/changed-files@v45 + with: + dir_names: "true" + - name: list changed directories + run: | + echo "Changed directories: ${{ steps.changed_directories.outputs.all_changed_files }}" + - name: install tlin + run: | + cd tlin + go install ./cmd/tlin + - name: tlin check + run: | + for directory in ${{ steps.changed_directories.outputs.all_changed_files }}; do + echo "checking $directory ..." + tlin $directory + done From df74863ae77d22b42a3634fc9b074f990b86384e Mon Sep 17 00:00:00 2001 From: Blake <104744707+r3v4s@users.noreply.github.com> Date: Tue, 10 Dec 2024 10:01:18 +0900 Subject: [PATCH 02/44] GSW-1839 helper to reset position's state (#420) * test: helper to reset positions state * test: common logic and vars --- .../__TEST_0_INIT_TOKEN_REGISTER_test.gno | 176 ++++++++++++++++++ position/__TEST_0_INIT_VARS_HELPERS_test.gno | 69 +++++++ position/_helper_test.gno | 81 ++++++++ 3 files changed, 326 insertions(+) create mode 100644 position/__TEST_0_INIT_TOKEN_REGISTER_test.gno create mode 100644 position/__TEST_0_INIT_VARS_HELPERS_test.gno create mode 100644 position/_helper_test.gno diff --git a/position/__TEST_0_INIT_TOKEN_REGISTER_test.gno b/position/__TEST_0_INIT_TOKEN_REGISTER_test.gno new file mode 100644 index 000000000..8da3b3790 --- /dev/null +++ b/position/__TEST_0_INIT_TOKEN_REGISTER_test.gno @@ -0,0 +1,176 @@ +package position + +import ( + "std" + + "gno.land/r/onbloc/foo" + + "gno.land/r/onbloc/bar" + + "gno.land/r/onbloc/baz" + + "gno.land/r/onbloc/qux" + + "gno.land/r/demo/wugnot" + + "gno.land/r/onbloc/obl" + + "gno.land/r/gnoswap/v1/gns" + + "gno.land/r/onbloc/usdc" + + "gno.land/r/gnoswap/v1/consts" + + pusers "gno.land/p/demo/users" + + pl "gno.land/r/gnoswap/v1/pool" + rr "gno.land/r/gnoswap/v1/router" +) + +type FooToken struct{} + +func (FooToken) Transfer() func(to pusers.AddressOrName, amount uint64) { + return foo.Transfer +} +func (FooToken) TransferFrom() func(from, to pusers.AddressOrName, amount uint64) { + return foo.TransferFrom +} +func (FooToken) BalanceOf() func(owner pusers.AddressOrName) uint64 { + return foo.BalanceOf +} +func (FooToken) Approve() func(spender pusers.AddressOrName, amount uint64) { + return foo.Approve +} + +type BarToken struct{} + +func (BarToken) Transfer() func(to pusers.AddressOrName, amount uint64) { + return bar.Transfer +} +func (BarToken) TransferFrom() func(from, to pusers.AddressOrName, amount uint64) { + return bar.TransferFrom +} +func (BarToken) BalanceOf() func(owner pusers.AddressOrName) uint64 { + return bar.BalanceOf +} +func (BarToken) Approve() func(spender pusers.AddressOrName, amount uint64) { + return bar.Approve +} + +type BazToken struct{} + +func (BazToken) Transfer() func(to pusers.AddressOrName, amount uint64) { + return baz.Transfer +} +func (BazToken) TransferFrom() func(from, to pusers.AddressOrName, amount uint64) { + return baz.TransferFrom +} +func (BazToken) BalanceOf() func(owner pusers.AddressOrName) uint64 { + return baz.BalanceOf +} +func (BazToken) Approve() func(spender pusers.AddressOrName, amount uint64) { + return baz.Approve +} + +type QuxToken struct{} + +func (QuxToken) Transfer() func(to pusers.AddressOrName, amount uint64) { + return qux.Transfer +} +func (QuxToken) TransferFrom() func(from, to pusers.AddressOrName, amount uint64) { + return qux.TransferFrom +} +func (QuxToken) BalanceOf() func(owner pusers.AddressOrName) uint64 { + return qux.BalanceOf +} +func (QuxToken) Approve() func(spender pusers.AddressOrName, amount uint64) { + return qux.Approve +} + +type WugnotToken struct{} + +func (WugnotToken) Transfer() func(to pusers.AddressOrName, amount uint64) { + return wugnot.Transfer +} +func (WugnotToken) TransferFrom() func(from, to pusers.AddressOrName, amount uint64) { + return wugnot.TransferFrom +} +func (WugnotToken) BalanceOf() func(owner pusers.AddressOrName) uint64 { + return wugnot.BalanceOf +} +func (WugnotToken) Approve() func(spender pusers.AddressOrName, amount uint64) { + return wugnot.Approve +} + +type OBLToken struct{} + +func (OBLToken) Transfer() func(to pusers.AddressOrName, amount uint64) { + return obl.Transfer +} +func (OBLToken) TransferFrom() func(from, to pusers.AddressOrName, amount uint64) { + return obl.TransferFrom +} +func (OBLToken) BalanceOf() func(owner pusers.AddressOrName) uint64 { + return obl.BalanceOf +} +func (OBLToken) Approve() func(spender pusers.AddressOrName, amount uint64) { + return obl.Approve +} + +type GNSToken struct{} + +func (GNSToken) Transfer() func(to pusers.AddressOrName, amount uint64) { + return gns.Transfer +} + +func (GNSToken) TransferFrom() func(from, to pusers.AddressOrName, amount uint64) { + return gns.TransferFrom +} + +func (GNSToken) BalanceOf() func(owner pusers.AddressOrName) uint64 { + return gns.BalanceOf +} + +func (GNSToken) Approve() func(spender pusers.AddressOrName, amount uint64) { + return gns.Approve +} + +type USDCToken struct{} + +func (USDCToken) Transfer() func(to pusers.AddressOrName, amount uint64) { + return usdc.Transfer +} + +func (USDCToken) TransferFrom() func(from, to pusers.AddressOrName, amount uint64) { + return usdc.TransferFrom +} + +func (USDCToken) BalanceOf() func(owner pusers.AddressOrName) uint64 { + return usdc.BalanceOf +} + +func (USDCToken) Approve() func(spender pusers.AddressOrName, amount uint64) { + return usdc.Approve +} + +func init() { + std.TestSetRealm(std.NewUserRealm(consts.TOKEN_REGISTER)) + + pl.RegisterGRC20Interface("gno.land/r/onbloc/bar", BarToken{}) + pl.RegisterGRC20Interface("gno.land/r/onbloc/foo", FooToken{}) + pl.RegisterGRC20Interface("gno.land/r/onbloc/baz", BazToken{}) + pl.RegisterGRC20Interface("gno.land/r/onbloc/qux", QuxToken{}) + pl.RegisterGRC20Interface("gno.land/r/demo/wugnot", WugnotToken{}) + pl.RegisterGRC20Interface("gno.land/r/onbloc/obl", OBLToken{}) + pl.RegisterGRC20Interface("gno.land/r/gnoswap/v1/gns", GNSToken{}) + pl.RegisterGRC20Interface("gno.land/r/onbloc/usdc", USDCToken{}) + + rr.RegisterGRC20Interface("gno.land/r/onbloc/bar", BarToken{}) + rr.RegisterGRC20Interface("gno.land/r/onbloc/foo", FooToken{}) + rr.RegisterGRC20Interface("gno.land/r/onbloc/baz", BazToken{}) + rr.RegisterGRC20Interface("gno.land/r/onbloc/qux", QuxToken{}) + rr.RegisterGRC20Interface("gno.land/r/demo/wugnot", WugnotToken{}) + rr.RegisterGRC20Interface("gno.land/r/onbloc/obl", OBLToken{}) + rr.RegisterGRC20Interface("gno.land/r/gnoswap/v1/gns", GNSToken{}) + rr.RegisterGRC20Interface("gno.land/r/onbloc/usdc", USDCToken{}) +} diff --git a/position/__TEST_0_INIT_VARS_HELPERS_test.gno b/position/__TEST_0_INIT_VARS_HELPERS_test.gno new file mode 100644 index 000000000..f7972bb9a --- /dev/null +++ b/position/__TEST_0_INIT_VARS_HELPERS_test.gno @@ -0,0 +1,69 @@ +package position + +import ( + "std" + "testing" + + "gno.land/r/gnoswap/v1/consts" + + "gno.land/r/gnoswap/v1/gnft" + + pl "gno.land/r/gnoswap/v1/pool" +) + +var ( + admin std.Address = consts.ADMIN + test1 std.Address = std.Address("g1jg8mtutu9khhfwc4nxmuhcpftf0pajdhfvsqf5") + + fooPath string = "gno.land/r/onbloc/foo" + barPath string = "gno.land/r/onbloc/bar" + bazPath string = "gno.land/r/onbloc/baz" + quxPath string = "gno.land/r/onbloc/qux" + + oblPath string = "gno.land/r/onbloc/obl" + // wugnotPath string = "gno.land/r/demo/wugnot" // from consts + // gnsPath string = "gno.land/r/gnoswap/v1/gns" // from consts + + fee100 uint32 = 100 + fee500 uint32 = 500 + fee3000 uint32 = 3000 + + max_timeout int64 = 9999999999 +) + +// Realms to mock frames +var ( + adminRealm = std.NewUserRealm(admin) + posRealm = std.NewCodeRealm(consts.POSITION_PATH) + rouRealm = std.NewCodeRealm(consts.ROUTER_PATH) + stkRealm = std.NewCodeRealm(consts.STAKER_PATH) +) + +/* HELPER */ +func ugnotBalanceOf(addr std.Address) uint64 { + testBanker := std.GetBanker(std.BankerTypeRealmIssue) + + coins := testBanker.GetCoins(addr) + if len(coins) == 0 { + return 0 + } + + return uint64(coins.AmountOf("ugnot")) +} + +func isOwner(t *testing.T, tokenId uint64, addr std.Address) bool { + owner := gnft.OwnerOf(tid(tokenId)) + + if owner == addr { + return true + } + + t.Errorf("expected owner %v, got %v", addr, owner) + return false +} + +func getPoolFromLpTokenId(lpTokenId uint64) *pl.Pool { + position := positions[lpTokenId] + + return pl.GetPoolFromPoolPath(position.poolKey) +} diff --git a/position/_helper_test.gno b/position/_helper_test.gno new file mode 100644 index 000000000..4d87113fd --- /dev/null +++ b/position/_helper_test.gno @@ -0,0 +1,81 @@ +package position + +import ( + "std" + "testing" + + "gno.land/p/demo/uassert" + "gno.land/r/onbloc/bar" + "gno.land/r/onbloc/foo" + + "gno.land/r/gnoswap/v1/common" + "gno.land/r/gnoswap/v1/consts" + "gno.land/r/gnoswap/v1/gnft" + + pl "gno.land/r/gnoswap/v1/pool" +) + +// resetObject resets the object state(clear or make it default values) +func resetObject(t *testing.T) { + positions = make(map[uint64]Position) + nextId = 1 +} + +// burnAllNFT burns all NFTs +func burnAllNFT(t *testing.T) { + t.Helper() + + std.TestSetRealm(std.NewCodeRealm(consts.POSITION_PATH)) + for i := uint64(1); i <= gnft.TotalSupply(); i++ { + gnft.Burn(tid(i)) + } +} + +func TestBeforeResetObject(t *testing.T) { + // make actual data to test resetting not only position's state but also pool's state + std.TestSetRealm(adminRealm) + + // set pool create fee to 0 for testing + pl.SetPoolCreationFeeByAdmin(0) + pl.CreatePool(barPath, fooPath, fee500, common.TickMathGetSqrtRatioAtTick(0).ToString()) + + // mint position + bar.Approve(a2u(consts.POOL_ADDR), consts.UINT64_MAX) + foo.Approve(a2u(consts.POOL_ADDR), consts.UINT64_MAX) + + tokenId, liquidity, amount0, amount1 := Mint( + barPath, + fooPath, + fee500, + -887270, + 887270, + "50000", + "50000", + "0", + "0", + max_timeout, + admin, + admin, + ) + + uassert.Equal(t, tokenId, uint64(1), "tokenId should be 1") + uassert.Equal(t, liquidity, "50000", "liquidity should be 50000") + uassert.Equal(t, amount0, "50000", "amount0 should be 50000") + uassert.Equal(t, amount1, "50000", "amount1 should be 50000") + uassert.Equal(t, len(positions), 1, "positions should have 1 position") + uassert.Equal(t, nextId, uint64(2), "nextId should be 2") + uassert.Equal(t, gnft.TotalSupply(), uint64(1), "gnft total supply should be 1") + uassert.Equal(t, pl.PoolGetLiquidity("gno.land/r/onbloc/bar:gno.land/r/onbloc/foo:500"), "50000", "pool liquidity should be 50000") +} + +func TestResetObject(t *testing.T) { + resetObject(t) + + uassert.Equal(t, len(positions), 0, "positions should be empty") + uassert.Equal(t, nextId, uint64(1), "nextId should be 1") +} + +func TestBurnAllNFT(t *testing.T) { + burnAllNFT(t) + uassert.Equal(t, gnft.TotalSupply(), uint64(0), "gnft total supply should be 0") +} From 751edf843be3c9df48df3694b6c349eb10b59dcb Mon Sep 17 00:00:00 2001 From: Lee ByeongJun Date: Tue, 10 Dec 2024 15:08:27 +0900 Subject: [PATCH 03/44] refactor: pool.gno (#403) * use common assert * extract some functions * refact, test: transferAndVerify * refact: transferAndVerify helper * remove unnecessary conversion * reorganize execute flows * refactor: Swap * pool test * test: transferFromAndVerify * test: burn (placeholder) * remove prefix * condition as a function * test: position_update * X128 prefix * fix: Modify test code to change transferFromAndVerify function param --------- Co-authored-by: 0xTopaz --- pool/_RPC_dry.gno | 6 +- pool/errors.gno | 1 + pool/pool.gno | 846 +++++++++++++++++++++------------- pool/pool_test.gno | 702 ++++++++++++++++++++++++++++ pool/position_modify.gno | 10 +- pool/position_modify_test.gno | 41 +- pool/position_update.gno | 16 +- pool/position_update_test.gno | 85 ++++ pool/token_register.gno | 10 +- pool/type.gno | 62 ++- 10 files changed, 1421 insertions(+), 358 deletions(-) create mode 100644 pool/pool_test.gno create mode 100644 pool/position_update_test.gno diff --git a/pool/_RPC_dry.gno b/pool/_RPC_dry.gno index e589c139a..5ffe30c86 100644 --- a/pool/_RPC_dry.gno +++ b/pool/_RPC_dry.gno @@ -58,9 +58,11 @@ func DrySwap( feeGrowthGlobalX128 = pool.feeGrowthGlobal1X128 } - pool.slot0.unlocked = false + slot0 := pool.slot0 + slot0.unlocked = false + cache := newSwapCache(feeProtocol, pool.liquidity) - state := pool.newSwapState(amountSpecified, feeGrowthGlobalX128, cache.liquidityStart) // TODO: feeGrowthGlobalX128.Clone() or NOT + state := newSwapState(amountSpecified.Clone(), feeGrowthGlobalX128.Clone(), cache.liquidityStart.Clone(), slot0) exactInput := amountSpecified.Gt(i256.Zero()) diff --git a/pool/errors.gno b/pool/errors.gno index 5999c6461..2031f4ad9 100644 --- a/pool/errors.gno +++ b/pool/errors.gno @@ -32,6 +32,7 @@ var ( errInvalidTickAndTickSpacing = errors.New("[GNOSWAP-POOL-022] invalid tick and tick spacing requested") errInvalidAddress = errors.New("[GNOSWAP-POOL-023] invalid address") errInvalidTickRange = errors.New("[GNOSWAP-POOL-024] tickLower is greater than tickUpper") + errUnderflow = errors.New("[GNOSWAP-POOL-025] underflow") // TODO: make as common error code ) // addDetailToError adds detail to an error message diff --git a/pool/pool.gno b/pool/pool.gno index e54bc49fc..1ab2b8696 100644 --- a/pool/pool.gno +++ b/pool/pool.gno @@ -24,13 +24,13 @@ func Mint( recipient std.Address, tickLower int32, tickUpper int32, - _liquidityAmount string, // uint128 + _liquidityAmount string, positionCaller std.Address, -) (string, string) { // uint256 x2 +) (string, string) { common.IsHalted() if common.GetLimitCaller() { caller := std.PrevRealm().Addr() - if caller != consts.POSITION_ADDR { + if err := common.PositionOnly(caller); err != nil { panic(addDetailToError( errNoPermission, ufmt.Sprintf("pool.gno__Mint() || only position(%s) can call pool mint(), called from %s", consts.POSITION_ADDR, caller.String()), @@ -47,20 +47,14 @@ func Mint( } pool := GetPool(token0Path, token1Path, fee) - _, amount0, amount1 := pool.modifyPosition( - ModifyPositionParams{ - recipient, // owner - tickLower, // tickLower - tickUpper, // tickUpper - i256.FromUint256(liquidityAmount), // liquidityDelta - }, - ) + position := newModifyPositionParams(recipient, tickLower, tickUpper, i256.FromUint256(liquidityAmount)) + _, amount0, amount1 := pool.modifyPosition(position) - if amount0.Gt(i256.Zero()) { + if amount0.Gt(u256.Zero()) { pool.transferFromAndVerify(positionCaller, consts.POOL_ADDR, pool.token0Path, amount0, true) } - if amount1.Gt(i256.Zero()) { + if amount1.Gt(u256.Zero()) { pool.transferFromAndVerify(positionCaller, consts.POOL_ADDR, pool.token1Path, amount1, false) } @@ -77,12 +71,12 @@ func Burn( fee uint32, tickLower int32, tickUpper int32, - _liquidityAmount string, // uint128 + liquidityAmount string, // uint128 ) (string, string) { // uint256 x2 common.IsHalted() caller := std.PrevRealm().Addr() if common.GetLimitCaller() { - if caller != consts.POSITION_ADDR { + if err := common.PositionOnly(caller); err != nil { panic(addDetailToError( errNoPermission, ufmt.Sprintf("pool.gno__Burn() || only position(%s) can call pool burn(), called from %s", consts.POSITION_ADDR, caller.String()), @@ -90,21 +84,13 @@ func Burn( } } - liquidityAmount := u256.MustFromDecimal(_liquidityAmount) + liqAmount := u256.MustFromDecimal(liquidityAmount) pool := GetPool(token0Path, token1Path, fee) - position, amount0Int, amount1Int := pool.modifyPosition( // in256 x2 - ModifyPositionParams{ - caller, // msg.sender - tickLower, - tickUpper, - i256.Zero().Neg(i256.FromUint256(liquidityAmount)), - }, - ) - - amount0 := amount0Int.Abs() - amount1 := amount1Int.Abs() + liqDelta := i256.Zero().Neg(i256.FromUint256(liqAmount)) + posParams := newModifyPositionParams(caller, tickLower, tickUpper, liqDelta) + position, amount0, amount1 := pool.modifyPosition(posParams) if amount0.Gt(u256.Zero()) || amount1.Gt(u256.Zero()) { position.tokensOwed0 = new(u256.Uint).Add(position.tokensOwed0, amount0) @@ -129,13 +115,13 @@ func Collect( recipient std.Address, tickLower int32, tickUpper int32, - _amount0Requested string, // uint128 - _amount1Requested string, // uint128 -) (string, string) { // uint128 x2 + amount0Requested string, + amount1Requested string, +) (string, string) { common.IsHalted() if common.GetLimitCaller() { caller := std.PrevRealm().Addr() - if caller != consts.POSITION_ADDR { + if err := common.PositionOnly(caller); err != nil { panic(addDetailToError( errNoPermission, ufmt.Sprintf("pool.gno__Collect() || only position(%s) can call pool collect(), called from %s", consts.POSITION_ADDR, caller.String()), @@ -143,9 +129,6 @@ func Collect( } } - amount0Requested := u256.MustFromDecimal(_amount0Requested) - amount1Requested := u256.MustFromDecimal(_amount1Requested) - pool := GetPool(token0Path, token1Path, fee) positionKey := positionGetKey(std.PrevRealm().Addr(), tickLower, tickUpper) @@ -157,22 +140,16 @@ func Collect( )) } - // Smallest of three: amount0Requested, position.tokensOwed0, pool.balances.token0 - amount0 := u256Min(amount0Requested, position.tokensOwed0) - amount0 = u256Min(amount0, pool.balances.token0) + var amount0, amount1 *u256.Uint - // Update state first then transfer - position.tokensOwed0 = new(u256.Uint).Sub(position.tokensOwed0, amount0) - pool.balances.token0 = new(u256.Uint).Sub(pool.balances.token0, amount0) + // Smallest of three: amount0Requested, position.tokensOwed0, pool.balances.token0 + amount0Req := u256.MustFromDecimal(amount0Requested) + amount0, position.tokensOwed0, pool.balances.token0 = collectToken(amount0Req, position.tokensOwed0, pool.balances.token0) transferByRegisterCall(pool.token0Path, recipient, amount0.Uint64()) // Smallest of three: amount0Requested, position.tokensOwed0, pool.balances.token0 - amount1 := u256Min(amount1Requested, position.tokensOwed1) - amount1 = u256Min(amount1, pool.balances.token1) - - // Update state first then transfer - position.tokensOwed1 = new(u256.Uint).Sub(position.tokensOwed1, amount1) - pool.balances.token1 = new(u256.Uint).Sub(pool.balances.token1, amount1) + amount1Req := u256.MustFromDecimal(amount1Requested) + amount1, position.tokensOwed1, pool.balances.token1 = collectToken(amount1Req, position.tokensOwed1, pool.balances.token1) transferByRegisterCall(pool.token1Path, recipient, amount1.Uint64()) pool.positions[positionKey] = position @@ -180,6 +157,45 @@ func Collect( return amount0.ToString(), amount1.ToString() } +// collectToken handles the collection of a single token type (token0 or token1) +func collectToken( + amountReq, tokensOwed, poolBalance *u256.Uint, +) (amount, newTokensOwed, newPoolBalance *u256.Uint) { + // find smallest of three amounts + amount = u256Min(amountReq, tokensOwed) + amount = u256Min(amount, poolBalance) + + // value for update state + newTokensOwed = new(u256.Uint).Sub(tokensOwed, amount) + newPoolBalance = new(u256.Uint).Sub(poolBalance, amount) + + return amount, newTokensOwed, newPoolBalance +} + +// SwapResult encapsulates all state changes that occur as a result of a swap +// This type ensure all state transitions are atomic and can be applied at once. +type SwapResult struct { + Amount0 *i256.Int + Amount1 *i256.Int + NewSqrtPrice *u256.Uint + NewTick int32 + NewLiquidity *u256.Uint + NewProtocolFees ProtocolFees + FeeGrowthGlobal0X128 *u256.Uint + FeeGrowthGlobal1X128 *u256.Uint + SwapFee *u256.Uint +} + +// SwapComputation encapsulates pure computation logic for swap +type SwapComputation struct { + AmountSpecified *i256.Int + SqrtPriceLimitX96 *u256.Uint + ZeroForOne bool + ExactInput bool + InitialState SwapState + Cache SwapCache +} + // Swap swaps token0 for token1, or token1 for token0 // Returns swapped amount0, amount1 in string // ref: https://docs.gnoswap.io/contracts/pool/pool.gno#swap @@ -189,14 +205,14 @@ func Swap( fee uint32, recipient std.Address, zeroForOne bool, - _amountSpecified string, // int256 - _sqrtPriceLimitX96 string, // uint160 + amountSpecified string, + sqrtPriceLimitX96 string, payer std.Address, // router -) (string, string) { // int256 x2 +) (string, string) { common.IsHalted() if common.GetLimitCaller() { caller := std.PrevRealm().Addr() - if caller != consts.ROUTER_ADDR { + if err := common.RouterOnly(caller); err != nil { panic(addDetailToError( errNoPermission, ufmt.Sprintf("pool.gno__Swap() || only router(%s) can call pool swap(), called from %s", consts.ROUTER_ADDR, caller.String()), @@ -204,248 +220,413 @@ func Swap( } } - if _amountSpecified == "0" { + if amountSpecified == "0" { panic(addDetailToError( errInvalidSwapAmount, ufmt.Sprintf("pool.gno__Swap() || amountSpecified == 0"), )) } - amountSpecified := i256.MustFromDecimal(_amountSpecified) - sqrtPriceLimitX96 := u256.MustFromDecimal(_sqrtPriceLimitX96) - pool := GetPool(token0Path, token1Path, fee) + slot0Start := pool.slot0 + if !slot0Start.unlocked { + panic(errLockedPool) + } - if !(slot0Start.unlocked) { - panic(addDetailToError( - errLockedPool, - ufmt.Sprintf("pool.gno__Swap() || slot0Start.unlocked(false) must be unlocked)"), - )) + slot0Start.unlocked = false + defer func() { slot0Start.unlocked = true }() + + amounts := i256.MustFromDecimal(amountSpecified) + sqrtPriceLimit := u256.MustFromDecimal(sqrtPriceLimitX96) + + validatePriceLimits(pool, zeroForOne, sqrtPriceLimit) + + feeGrowthGlobalX128 := getFeeGrowthGlobal(pool, zeroForOne) + feeProtocol := getFeeProtocol(slot0Start, zeroForOne) + cache := newSwapCache(feeProtocol, pool.liquidity) + + state := newSwapState(amounts, feeGrowthGlobalX128, cache.liquidityStart, pool.slot0) + + comp := SwapComputation{ + AmountSpecified: amounts, + SqrtPriceLimitX96: sqrtPriceLimit, + ZeroForOne: zeroForOne, + ExactInput: amounts.Gt(i256.Zero()), + InitialState: state, + Cache: cache, + } + + result, err := computeSwap(pool, comp) + if err != nil { + panic(err) + } + + applySwapResult(pool, result) + + // actual swap + pool.swapTransfers(zeroForOne, payer, recipient, result.Amount0, result.Amount1) + + prevAddr, prevRealm := getPrev() + + std.Emit( + "Swap", + "prevAddr", prevAddr, + "prevRealm", prevRealm, + "poolPath", GetPoolPath(token0Path, token1Path, fee), + "zeroForOne", ufmt.Sprintf("%t", zeroForOne), + "amountSpecified", amountSpecified, + "sqrtPriceLimitX96", sqrtPriceLimitX96, + "payer", payer.String(), + "recipient", recipient.String(), + "internal_amount0", result.Amount0.ToString(), + "internal_amount1", result.Amount1.ToString(), + "internal_protocolFee0", pool.protocolFees.token0.ToString(), + "internal_protocolFee1", pool.protocolFees.token1.ToString(), + "internal_swapFee", result.SwapFee.ToString(), + "internal_sqrtPriceX96", pool.slot0.sqrtPriceX96.ToString(), + ) + + return result.Amount0.ToString(), result.Amount1.ToString() +} + +// computeSwap performs the core swap computation without modifying pool state +// The function follows these state transitions: +// 1. Initial State: Provided by `SwapComputation.InitialState` +// 2. Stepping State: For each step: +// - Compute next tick and price target +// - Calculate amounts and fees +// - Update state (remaining amount, fees, liquidity) +// - Handle tick transitions if necessary +// 3. Final State: Aggregated in SwapResult +// +// The computation continues until either: +// - The entire amount is consumed (`amountSpecifiedRemaining` = 0) +// - The price limit is reached (`sqrtPriceX96` = `sqrtPriceLimitX96`) +// +// Returns an error if the computation fails at any step +func computeSwap(pool *Pool, comp SwapComputation) (*SwapResult, error) { + state := comp.InitialState + swapFee := u256.Zero() + + + var newFee *u256.Uint + var err error + + // Compute swap steps until completion + for shouldContinueSwap(state, comp.SqrtPriceLimitX96) { + state, newFee, err = computeSwapStep(state, pool, comp.ZeroForOne, comp.SqrtPriceLimitX96, comp.ExactInput, comp.Cache, swapFee) + if err != nil { + return nil, err + } + swapFee = newFee + } + + // Calculate final amounts + amount0 := state.amountCalculated + amount1 := i256.Zero().Sub(comp.AmountSpecified, state.amountSpecifiedRemaining) + if comp.ZeroForOne == comp.ExactInput { + amount0, amount1 = amount1, amount0 + } + + // Prepare result + result := &SwapResult{ + Amount0: amount0, + Amount1: amount1, + NewSqrtPrice: state.sqrtPriceX96, + NewTick: state.tick, + NewLiquidity: state.liquidity, + NewProtocolFees: ProtocolFees{ + token0: pool.protocolFees.token0, + token1: pool.protocolFees.token1, + }, + FeeGrowthGlobal0X128: pool.feeGrowthGlobal0X128, + FeeGrowthGlobal1X128: pool.feeGrowthGlobal1X128, + SwapFee: swapFee, + } + + // Update protocol fees if necessary + if comp.ZeroForOne { + if state.protocolFee.Gt(u256.Zero()) { + result.NewProtocolFees.token0 = new(u256.Uint).Add(result.NewProtocolFees.token0, state.protocolFee) + } + result.FeeGrowthGlobal0X128 = state.feeGrowthGlobalX128 + } else { + if state.protocolFee.Gt(u256.Zero()) { + result.NewProtocolFees.token1 = new(u256.Uint).Add(result.NewProtocolFees.token1, state.protocolFee) + } + result.FeeGrowthGlobal1X128 = state.feeGrowthGlobalX128 } - var feeProtocol uint8 - var feeGrowthGlobalX128 *u256.Uint + return result, nil +} +// applySwapResult updates pool state with computed results. +// All state changes are applied at once to maintain consistency +func applySwapResult(pool *Pool, result *SwapResult) { + pool.slot0.sqrtPriceX96 = result.NewSqrtPrice + pool.slot0.tick = result.NewTick + pool.liquidity = result.NewLiquidity + pool.protocolFees = result.NewProtocolFees + pool.feeGrowthGlobal0X128 = result.FeeGrowthGlobal0X128 + pool.feeGrowthGlobal1X128 = result.FeeGrowthGlobal1X128 +} + +// validatePriceLimits ensures the provided price limit is valid for the swap direction +// The function enforces that: +// For zeroForOne (selling token0): +// - Price limit must be below current price +// - Price limit must be above MIN_SQRT_RATIO +// For !zeroForOne (selling token1): +// - Price limit must be above current price +// - Price limit must be below MAX_SQRT_RATIO +func validatePriceLimits(pool *Pool, zeroForOne bool, sqrtPriceLimitX96 *u256.Uint) { if zeroForOne { minSqrtRatio := u256.MustFromDecimal(consts.MIN_SQRT_RATIO) - cond1 := sqrtPriceLimitX96.Lt(slot0Start.sqrtPriceX96) + cond1 := sqrtPriceLimitX96.Lt(pool.slot0.sqrtPriceX96) cond2 := sqrtPriceLimitX96.Gt(minSqrtRatio) if !(cond1 && cond2) { panic(addDetailToError( errPriceOutOfRange, - ufmt.Sprintf("pool.gno__Swap() || sqrtPriceLimitX96(%s) < slot0Start.sqrtPriceX96(%s) && sqrtPriceLimitX96(%s) > consts.MIN_SQRT_RATIO(%s)", sqrtPriceLimitX96.ToString(), slot0Start.sqrtPriceX96.ToString(), sqrtPriceLimitX96.ToString(), consts.MIN_SQRT_RATIO), + ufmt.Sprintf("pool.gno__Swap() || sqrtPriceLimitX96(%s) < slot0Start.sqrtPriceX96(%s) && sqrtPriceLimitX96(%s) > consts.MIN_SQRT_RATIO(%s)", + sqrtPriceLimitX96.ToString(), + pool.slot0.sqrtPriceX96.ToString(), + sqrtPriceLimitX96.ToString(), + consts.MIN_SQRT_RATIO), )) } - feeProtocol = slot0Start.feeProtocol % 16 - feeGrowthGlobalX128 = pool.feeGrowthGlobal0X128 - } else { maxSqrtRatio := u256.MustFromDecimal(consts.MAX_SQRT_RATIO) - cond1 := sqrtPriceLimitX96.Gt(slot0Start.sqrtPriceX96) + cond1 := sqrtPriceLimitX96.Gt(pool.slot0.sqrtPriceX96) cond2 := sqrtPriceLimitX96.Lt(maxSqrtRatio) if !(cond1 && cond2) { panic(addDetailToError( errPriceOutOfRange, - ufmt.Sprintf("pool.gno__Swap() || sqrtPriceLimitX96(%s) > slot0Start.sqrtPriceX96(%s) && sqrtPriceLimitX96(%s) < consts.MAX_SQRT_RATIO(%s)", sqrtPriceLimitX96.ToString(), slot0Start.sqrtPriceX96.ToString(), sqrtPriceLimitX96.ToString(), consts.MAX_SQRT_RATIO), + ufmt.Sprintf("pool.gno__Swap() || sqrtPriceLimitX96(%s) > slot0Start.sqrtPriceX96(%s) && sqrtPriceLimitX96(%s) < consts.MAX_SQRT_RATIO(%s)", + sqrtPriceLimitX96.ToString(), + pool.slot0.sqrtPriceX96.ToString(), + sqrtPriceLimitX96.ToString(), + consts.MAX_SQRT_RATIO), )) } - - feeProtocol = slot0Start.feeProtocol / 16 - feeGrowthGlobalX128 = pool.feeGrowthGlobal1X128 } +} - pool.slot0.unlocked = false - cache := newSwapCache(feeProtocol, pool.liquidity) - state := pool.newSwapState(amountSpecified, feeGrowthGlobalX128, cache.liquidityStart) +// getFeeProtocol returns the appropriate fee protocol based on zero for one +func getFeeProtocol(slot0 Slot0, zeroForOne bool) uint8 { + if zeroForOne { + return slot0.feeProtocol % 16 + } + return slot0.feeProtocol / 16 +} - exactInput := amountSpecified.Gt(i256.Zero()) +// getFeeGrowthGlobal returns the appropriate fee growth global based on zero for one +func getFeeGrowthGlobal(pool *Pool, zeroForOne bool) *u256.Uint { + if zeroForOne { + return pool.feeGrowthGlobal0X128 + } + return pool.feeGrowthGlobal1X128 +} - // continue swapping as long as we haven't used the entire input/output and haven't reached the price limit - swapFee := u256.Zero() - for !(state.amountSpecifiedRemaining.IsZero()) && !(state.sqrtPriceX96.Eq(sqrtPriceLimitX96)) { - var step StepComputations - step.sqrtPriceStartX96 = state.sqrtPriceX96 - - step.tickNext, step.initialized = pool.tickBitmapNextInitializedTickWithInOneWord( - state.tick, - pool.tickSpacing, - zeroForOne, - ) +func shouldContinueSwap(state SwapState, sqrtPriceLimitX96 *u256.Uint) bool { + return !(state.amountSpecifiedRemaining.IsZero()) && !(state.sqrtPriceX96.Eq(sqrtPriceLimitX96)) +} - // ensure that we do not overshoot the min/max tick, as the tick bitmap is not aware of these bounds - if step.tickNext < consts.MIN_TICK { - step.tickNext = consts.MIN_TICK - } else if step.tickNext > consts.MAX_TICK { - step.tickNext = consts.MAX_TICK +// computeSwapStep executes a single step of swap and returns new state +func computeSwapStep( + state SwapState, + pool *Pool, + zeroForOne bool, + sqrtPriceLimitX96 *u256.Uint, + exactInput bool, + cache SwapCache, + swapFee *u256.Uint, +) (SwapState, *u256.Uint, error) { + step := computeSwapStepInit(state, pool, zeroForOne) + + // determining the price target for this step + sqrtRatioTargetX96 := computeTargetSqrtRatio(step, sqrtPriceLimitX96, zeroForOne) + + // computing the amounts to be swapped at this step + var newState SwapState + var err error + + newState, step = computeAmounts(state, sqrtRatioTargetX96, pool, step) + newState = updateAmounts(step, newState, exactInput) + + // if the protocol fee is on, calculate how much is owed, + // decrement fee amount, and increment protocol fee + if cache.feeProtocol > 0 { + newState, err = updateFeeProtocol(step, cache.feeProtocol, newState) + if err != nil { + return state, nil, err } + } - // get the price for the next tick - step.sqrtPriceNextX96 = common.TickMathGetSqrtRatioAtTick(step.tickNext) + // update global fee tracker + if newState.liquidity.Gt(u256.Zero()) { + update := u256.MulDiv(step.feeAmount, u256.MustFromDecimal(consts.Q128), newState.liquidity) + newState.SetFeeGrowthGlobalX128(new(u256.Uint).Add(newState.feeGrowthGlobalX128, update)) + } - isLower := step.sqrtPriceNextX96.Lt(sqrtPriceLimitX96) - isHigher := step.sqrtPriceNextX96.Gt(sqrtPriceLimitX96) + // handling tick transitions + if newState.sqrtPriceX96.Eq(step.sqrtPriceNextX96) { + newState = tickTransition(step, zeroForOne, newState, pool) + } - var sqrtRatioTargetX96 *u256.Uint - if (zeroForOne && isLower) || (!zeroForOne && isHigher) { - sqrtRatioTargetX96 = sqrtPriceLimitX96 - } else { - sqrtRatioTargetX96 = step.sqrtPriceNextX96 - } + if newState.sqrtPriceX96.Neq(step.sqrtPriceStartX96) { + newState.SetTick(common.TickMathGetTickAtSqrtRatio(newState.sqrtPriceX96)) + } - _sqrtPriceX96Str, _amountInStr, _amountOutStr, _feeAmountStr := plp.SwapMathComputeSwapStepStr( - state.sqrtPriceX96, - sqrtRatioTargetX96, - state.liquidity, - state.amountSpecifiedRemaining, - uint64(pool.fee), - ) - state.sqrtPriceX96 = u256.MustFromDecimal(_sqrtPriceX96Str) - step.amountIn = u256.MustFromDecimal(_amountInStr) - step.amountOut = u256.MustFromDecimal(_amountOutStr) - step.feeAmount = u256.MustFromDecimal(_feeAmountStr) - - amountInWithFee := i256.FromUint256(new(u256.Uint).Add(step.amountIn, step.feeAmount)) - if exactInput { - state.amountSpecifiedRemaining = i256.Zero().Sub(state.amountSpecifiedRemaining, amountInWithFee) - state.amountCalculated = i256.Zero().Sub(state.amountCalculated, i256.FromUint256(step.amountOut)) - } else { - state.amountSpecifiedRemaining = i256.Zero().Add(state.amountSpecifiedRemaining, i256.FromUint256(step.amountOut)) - state.amountCalculated = i256.Zero().Add(state.amountCalculated, amountInWithFee) - } + newSwapFee := new(u256.Uint).Add(swapFee, step.feeAmount) - // if the protocol fee is on, calculate how much is owed, decrement feeAmount, and increment protocolFee - if cache.feeProtocol > 0 { - delta := new(u256.Uint).Div(step.feeAmount, u256.NewUint(uint64(cache.feeProtocol))) - step.feeAmount = new(u256.Uint).Sub(step.feeAmount, delta) - state.protocolFee = new(u256.Uint).Add(state.protocolFee, delta) - } + return newState, newSwapFee, nil +} - // update global fee tracker - if state.liquidity.Gt(u256.Zero()) { - update := u256.MulDiv(step.feeAmount, u256.MustFromDecimal(consts.Q128), state.liquidity) - state.feeGrowthGlobalX128 = new(u256.Uint).Add(state.feeGrowthGlobalX128, update) - } - swapFee = new(u256.Uint).Add(swapFee, step.feeAmount) - - // shift tick if we reached the next price - if state.sqrtPriceX96.Eq(step.sqrtPriceNextX96) { - // if the tick is initialized, run the tick transition - if step.initialized { - var fee0, fee1 *u256.Uint - - // check for the placeholder value, which we replace with the actual value the first time the swap crosses an initialized tick - if zeroForOne { - fee0 = state.feeGrowthGlobalX128 - fee1 = pool.feeGrowthGlobal1X128 - } else { - fee0 = pool.feeGrowthGlobal0X128 - fee1 = state.feeGrowthGlobalX128 - } - - liquidityNet := pool.tickCross( - step.tickNext, - fee0, - fee1, - ) - - // if we're moving leftward, we interpret liquidityNet as the opposite sign - if zeroForOne { - liquidityNet = i256.Zero().Neg(liquidityNet) - } - - state.liquidity = liquidityMathAddDelta(state.liquidity, liquidityNet) - } - - if zeroForOne { - state.tick = step.tickNext - 1 - } else { - state.tick = step.tickNext - } - } else if !(state.sqrtPriceX96.Eq(step.sqrtPriceStartX96)) { - // recompute unless we're on a lower tick boundary (i.e. already transitioned ticks), and haven't moved - state.tick = common.TickMathGetTickAtSqrtRatio(state.sqrtPriceX96) - } +// updateFeeProtocol calculates and updates protocol fees for the current step. +func updateFeeProtocol(step StepComputations, feeProtocol uint8, state SwapState) (SwapState, error) { + delta := step.feeAmount + delta.Div(delta, u256.NewUint(uint64(feeProtocol))) + + newFeeAmount, overflow := new(u256.Uint).SubOverflow(step.feeAmount, delta) + if overflow { + return state, errUnderflow } - // END LOOP + step.feeAmount = newFeeAmount + state.protocolFee.Add(state.protocolFee, delta) - // update pool sqrtPrice - pool.slot0.sqrtPriceX96 = state.sqrtPriceX96 + return state, nil +} - // update tick if it changed - if state.tick != slot0Start.tick { - pool.slot0.tick = state.tick - } +// computeSwapStepInit initializes the computation for a single swap step. +func computeSwapStepInit(state SwapState, pool *Pool, zeroForOne bool) StepComputations { + var step StepComputations + step.sqrtPriceStartX96 = state.sqrtPriceX96 + tickNext, initialized := pool.tickBitmapNextInitializedTickWithInOneWord( + state.tick, + pool.tickSpacing, + zeroForOne, + ) + + step.tickNext = tickNext + step.initialized = initialized + + // prevent overshoot the min/max tick + step.clampTickNext() + + // get the price for the next tick + step.sqrtPriceNextX96 = common.TickMathGetSqrtRatioAtTick(step.tickNext) + return step +} - // update liquidity if it changed - if !(cache.liquidityStart.Eq(state.liquidity)) { - pool.liquidity = state.liquidity +// computeTargetSqrtRatio determines the target sqrt price for the current swap step. +func computeTargetSqrtRatio(step StepComputations, sqrtPriceLimitX96 *u256.Uint, zeroForOne bool) *u256.Uint { + if shouldUsePriceLimit(step.sqrtPriceNextX96, sqrtPriceLimitX96, zeroForOne) { + return sqrtPriceLimitX96 } + return step.sqrtPriceNextX96 +} - // update fee growth global and, if necessary, protocol fees - // overflow is acceptable, protocol has to withdraw before it hits MAX_UINT256 fees +// shouldUsePriceLimit returns true if the price limit should be used instead of the next tick price +func shouldUsePriceLimit(sqrtPriceNext, sqrtPriceLimit *u256.Uint, zeroForOne bool) bool { + isLower := sqrtPriceNext.Lt(sqrtPriceLimit) + isHigher := sqrtPriceNext.Gt(sqrtPriceLimit) if zeroForOne { - pool.feeGrowthGlobal0X128 = state.feeGrowthGlobalX128 - if state.protocolFee.Gt(u256.Zero()) { - pool.protocolFees.token0 = new(u256.Uint).Add(pool.protocolFees.token0, state.protocolFee) - } - } else { - pool.feeGrowthGlobal1X128 = state.feeGrowthGlobalX128 - if state.protocolFee.Gt(u256.Zero()) { - pool.protocolFees.token1 = new(u256.Uint).Add(pool.protocolFees.token1, state.protocolFee) - } + return isLower } + return isHigher +} - var amount0, amount1 *i256.Int - if zeroForOne == exactInput { - amount0 = i256.Zero().Sub(amountSpecified, state.amountSpecifiedRemaining) - amount1 = state.amountCalculated - } else { - amount0 = state.amountCalculated - amount1 = i256.Zero().Sub(amountSpecified, state.amountSpecifiedRemaining) +// computeAmounts calculates the input and output amounts for the current swap step. +func computeAmounts(state SwapState, sqrtRatioTargetX96 *u256.Uint, pool *Pool, step StepComputations) (SwapState, StepComputations) { + sqrtPriceX96Str, amountInStr, amountOutStr, feeAmountStr := plp.SwapMathComputeSwapStepStr( + state.sqrtPriceX96, + sqrtRatioTargetX96, + state.liquidity, + state.amountSpecifiedRemaining, + uint64(pool.fee), + ) + + step.amountIn = u256.MustFromDecimal(amountInStr) + step.amountOut = u256.MustFromDecimal(amountOutStr) + step.feeAmount = u256.MustFromDecimal(feeAmountStr) + + state.SetSqrtPriceX96(sqrtPriceX96Str) + + return state, step +} + +// updateAmounts calculates new remaining and calculated amounts based on the swap step +// For exact input swaps: +// - Decrements remaining input amount by (amountIn + feeAmount) +// - Decrements calculated amount by amountOut +// For exact output swaps: +// - Increments remaining output amount by amountOut +// - Increments calculated amount by (amountIn + feeAmount) +func updateAmounts(step StepComputations, state SwapState, exactInput bool) SwapState { + amountInWithFee := i256.FromUint256(new(u256.Uint).Add(step.amountIn, step.feeAmount)) + if exactInput { + state.amountSpecifiedRemaining = i256.Zero().Sub(state.amountSpecifiedRemaining, amountInWithFee) + state.amountCalculated = i256.Zero().Sub(state.amountCalculated, i256.FromUint256(step.amountOut)) + return state } + state.amountSpecifiedRemaining = i256.Zero().Add(state.amountSpecifiedRemaining, i256.FromUint256(step.amountOut)) + state.amountCalculated = i256.Zero().Add(state.amountCalculated, amountInWithFee) - // actual swap - if zeroForOne { - // payer > POOL - pool.transferFromAndVerify(payer, consts.POOL_ADDR, pool.token0Path, amount0, true) + return state +} - // POOL > recipient - pool.transferAndVerify(recipient, pool.token1Path, amount1, false) +// tickTransition handles the transition between price ticks during a swap +func tickTransition(step StepComputations, zeroForOne bool, state SwapState, pool *Pool) SwapState { + // ensure existing state to keep immutability + newState := state - } else { - // payer > POOL - pool.transferFromAndVerify(payer, consts.POOL_ADDR, pool.token1Path, amount1, false) + if step.initialized { + var fee0, fee1 *u256.Uint + + if zeroForOne { + fee0 = state.feeGrowthGlobalX128 + fee1 = pool.feeGrowthGlobal1X128 + } else { + fee0 = pool.feeGrowthGlobal0X128 + fee1 = state.feeGrowthGlobalX128 + } + + liquidityNet := pool.tickCross(step.tickNext, fee0, fee1) - // POOL > recipient - pool.transferAndVerify(recipient, pool.token0Path, amount0, true) + if zeroForOne { + liquidityNet = i256.Zero().Neg(liquidityNet) + } + newState.liquidity = liquidityMathAddDelta(state.liquidity, liquidityNet) } - prevAddr, prevRealm := getPrev() + if zeroForOne { + newState.tick = step.tickNext - 1 + } else { + newState.tick = step.tickNext + } - std.Emit( - "Swap", - "prevAddr", prevAddr, - "prevRealm", prevRealm, - "poolPath", GetPoolPath(token0Path, token1Path, fee), - "zeroForOne", ufmt.Sprintf("%t", zeroForOne), - "amountSpecified", _amountSpecified, - "sqrtPriceLimitX96", _sqrtPriceLimitX96, - "payer", payer.String(), - "recipient", recipient.String(), - "internal_amount0", amount0.ToString(), - "internal_amount1", amount1.ToString(), - "internal_protocolFee0", pool.protocolFees.token0.ToString(), - "internal_protocolFee1", pool.protocolFees.token1.ToString(), - "internal_swapFee", swapFee.ToString(), - "internal_sqrtPriceX96", pool.slot0.sqrtPriceX96.ToString(), - ) + return newState +} - pool.slot0.unlocked = true - return amount0.ToString(), amount1.ToString() +func (pool *Pool) swapTransfers(zeroForOne bool, payer, recipient std.Address, amount0, amount1 *i256.Int) { + var targetTokenPath string + var amount *i256.Int + + if zeroForOne { + targetTokenPath = pool.token0Path + amount = amount0 + } else { + targetTokenPath = pool.token1Path + amount = amount1 + } + + // payer -> POOL -> recipient + pool.transferFromAndVerify(payer, consts.POOL_ADDR, targetTokenPath, amount.Abs(), zeroForOne) + pool.transferAndVerify(recipient, targetTokenPath, amount, !zeroForOne) } // SetFeeProtocolByAdmin sets the fee protocol for all pools @@ -476,10 +657,7 @@ func SetFeeProtocolByAdmin( // Only governance contract can execute this function via proposal // Also it will be applied to new created pools // ref: https://docs.gnoswap.io/contracts/pool/pool.gno#setfeeprotocol -func SetFeeProtocol( - feeProtocol0 uint8, - feeProtocol1 uint8, -) { +func SetFeeProtocol(feeProtocol0, feeProtocol1 uint8) { caller := std.PrevRealm().Addr() if err := common.GovernanceOnly(caller); err != nil { panic(err) @@ -498,17 +676,12 @@ func SetFeeProtocol( ) } -func setFeeProtocol( - feeProtocol0 uint8, - feeProtocol1 uint8, -) uint8 { +func setFeeProtocol(feeProtocol0, feeProtocol1 uint8) uint8 { common.IsHalted() - fee0Cond := feeProtocol0 == 0 || (feeProtocol0 >= 4 && feeProtocol0 <= 10) - fee1Cond := feeProtocol1 == 0 || (feeProtocol1 >= 4 && feeProtocol1 <= 10) - if !(fee0Cond && fee1Cond) { + if err := validateFeeProtocol(feeProtocol0, feeProtocol1); err != nil { panic(addDetailToError( - errInvalidProtocolFeePct, + err, ufmt.Sprintf("pool.gno__setFeeProtocol() || expected (feeProtocol0(%d) == 0 || (feeProtocol0(%d) >= 4 && feeProtocol0(%d) <= 10)) && (feeProtocol1(%d) == 0 || (feeProtocol1(%d) >= 4 && feeProtocol1(%d) <= 10))", feeProtocol0, feeProtocol0, feeProtocol0, feeProtocol1, feeProtocol1, feeProtocol1), )) } @@ -526,6 +699,17 @@ func setFeeProtocol( return newFee } +func validateFeeProtocol(feeProtocol0, feeProtocol1 uint8) error { + if !isValidFeeProtocolValue(feeProtocol0) || !isValidFeeProtocolValue(feeProtocol1) { + return errInvalidProtocolFeePct + } + return nil +} + +func isValidFeeProtocolValue(value uint8) bool { + return value == 0 || (value >= 4 && value <= 10) +} + // CollectProtocolByAdmin collects protocol fees for the given pool that accumulated while it was being used for swap // Returns collected amount0, amount1 in string func CollectProtocolByAdmin( @@ -533,9 +717,9 @@ func CollectProtocolByAdmin( token1Path string, fee uint32, recipient std.Address, - _amount0Requested string, // uint128 - _amount1Requested string, // uint128 -) (string, string) { // uint128 x2 + amount0Requested string, + amount1Requested string, +) (string, string) { caller := std.PrevRealm().Addr() if err := common.AdminOnly(caller); err != nil { panic(err) @@ -546,8 +730,8 @@ func CollectProtocolByAdmin( token1Path, fee, recipient, - _amount0Requested, - _amount1Requested, + amount0Requested, + amount1Requested, ) prevAddr, prevRealm := getPrev() @@ -575,9 +759,9 @@ func CollectProtocol( token1Path string, fee uint32, recipient std.Address, - _amount0Requested string, // uint128 - _amount1Requested string, // uint128 -) (string, string) { // uint128 x2 + amount0Requested string, + amount1Requested string, +) (string, string) { caller := std.PrevRealm().Addr() if err := common.GovernanceOnly(caller); err != nil { panic(err) @@ -588,8 +772,8 @@ func CollectProtocol( token1Path, fee, recipient, - _amount0Requested, - _amount1Requested, + amount0Requested, + amount1Requested, ) prevAddr, prevRealm := getPrev() @@ -613,18 +797,18 @@ func collectProtocol( token1Path string, fee uint32, recipient std.Address, - _amount0Requested string, // uint128 - _amount1Requested string, // uint128 -) (string, string) { // uint128 x2 + amount0Requested string, + amount1Requested string, +) (string, string) { common.IsHalted() - amount0Requested := u256.MustFromDecimal(_amount0Requested) - amount1Requested := u256.MustFromDecimal(_amount1Requested) - pool := GetPool(token0Path, token1Path, fee) - amount0 := u256Min(amount0Requested, pool.protocolFees.token0) - amount1 := u256Min(amount1Requested, pool.protocolFees.token1) + amount0Req := u256.MustFromDecimal(amount0Requested) + amount1Req := u256.MustFromDecimal(amount1Requested) + + amount0 := u256Min(amount0Req, pool.protocolFees.token0) + amount1 := u256Min(amount1Req, pool.protocolFees.token1) amount0, amount1 = pool.saveProtocolFees(amount0, amount1) uAmount0 := amount0.Uint64() @@ -662,72 +846,102 @@ func (pool *Pool) transferAndVerify( amount *i256.Int, isToken0 bool, ) { - if amount.IsZero() { - return - } - - // must be negative to send token from pool to user - // as point of view from pool, it is negative - if !amount.IsNeg() { + if amount.Sign() != -1 { panic(addDetailToError( errMustBeNegative, ufmt.Sprintf("pool.gno__transferAndVerify() || amount(%s) must be negative", amount.ToString()), )) } - // check pool.balances + absAmount := amount.Abs() + + token0 := pool.balances.token0 + token1 := pool.balances.token1 + + if err := validatePoolBalance(token0, token1, absAmount, isToken0); err != nil { + panic(err) + } + amountUint64, err := checkAmountRange(absAmount) + if err != nil { + panic(err) + } + + transferByRegisterCall(tokenPath, to, amountUint64) + + newBalance, err := updatePoolBalance(token0, token1, absAmount, isToken0) + if err != nil { + panic(err) + } + if isToken0 { - if pool.balances.token0.Lt(amount.Abs()) { - panic(addDetailToError( - errTransferFailed, - ufmt.Sprintf("pool.gno__transferAndVerify() || pool.balances.token0(%s) >= amount.Abs(%s)", pool.balances.token0.ToString(), amount.Abs().ToString()), - )) - } + pool.balances.token0 = newBalance } else { - if pool.balances.token1.Lt(amount.Abs()) { - panic(addDetailToError( - errTransferFailed, - ufmt.Sprintf("pool.gno__transferAndVerify() || pool.balances.token1(%s) >= amount.Abs(%s)", pool.balances.token1.ToString(), amount.Abs().ToString()), - )) - } + pool.balances.token1 = newBalance } +} - amountUint64 := checkAmountRange(amount) - - // try sending - // will panic if following conditions are met: - // - POOL does not have enough balance - // - token is not registered - transferByRegisterCall(tokenPath, to, amountUint64) +func validatePoolBalance(token0, token1, amount *u256.Uint, isToken0 bool) error { + if isToken0 { + if token0.Lt(amount) { + return ufmt.Errorf( + "%s || token0(%s) >= amount(%s)", + errTransferFailed.Error(), token0.ToString(), amount.ToString(), + ) + } + return nil + } + if token1.Lt(amount) { + return ufmt.Errorf( + "%s || token1(%s) >= amount(%s)", + errTransferFailed.Error(), token1.ToString(), amount.ToString(), + ) + } + return nil +} - // update pool.balances +func updatePoolBalance( + token0, token1, amount *u256.Uint, + isToken0 bool, +) (*u256.Uint, error) { var overflow bool + var newBalance *u256.Uint + if isToken0 { - pool.balances.token0, overflow = new(u256.Uint).SubOverflow(pool.balances.token0, amount.Abs()) - if overflow { - panic(addDetailToError( - errTransferFailed, - ufmt.Sprintf("pool.gno__transferAndVerify() || cannot decrease, pool.balances.token0(%s) - amount(%s)", pool.balances.token0.ToString(), amount.Abs().ToString()), - )) - } - } else { - pool.balances.token1, overflow = new(u256.Uint).SubOverflow(pool.balances.token1, amount.Abs()) - if pool.balances.token1.Lt(u256.Zero()) { - panic(addDetailToError( - errTransferFailed, - ufmt.Sprintf("pool.gno__transferAndVerify() || cannot decrease, pool.balances.token1(%s) - amount(%s)", pool.balances.token1.ToString(), amount.Abs().ToString()), - )) + newBalance, overflow = new(u256.Uint).SubOverflow(token0, amount) + if isBalanceOverflowOrNegative(overflow, newBalance) { + return nil, ufmt.Errorf( + "%s || cannot decrease, token0(%s) - amount(%s)", + errTransferFailed.Error(), token0.ToString(), amount.ToString(), + ) } + return newBalance, nil } + + newBalance, overflow = new(u256.Uint).SubOverflow(token1, amount) + if isBalanceOverflowOrNegative(overflow, newBalance) { + return nil, ufmt.Errorf( + "%s || cannot decrease, token1(%s) - amount(%s)", + errTransferFailed.Error(), token1.ToString(), amount.ToString(), + ) + } + return newBalance, nil +} + +func isBalanceOverflowOrNegative(overflow bool, newBalance *u256.Uint) bool { + return overflow || newBalance.Lt(u256.Zero()) } func (pool *Pool) transferFromAndVerify( from, to std.Address, tokenPath string, - amount *i256.Int, + amount *u256.Uint, isToken0 bool, ) { - amountUint64 := checkAmountRange(amount) + absAmount := amount + amountUint64, err := checkAmountRange(absAmount) + if err != nil { + panic(err) + } // try sending // will panic if following conditions are met: @@ -736,26 +950,24 @@ func (pool *Pool) transferFromAndVerify( // - token is not registered transferFromByRegisterCall(tokenPath, from, to, amountUint64) - // update pool.balances + // update pool balances if isToken0 { - pool.balances.token0 = new(u256.Uint).Add(pool.balances.token0, amount.Abs()) + pool.balances.token0 = new(u256.Uint).Add(pool.balances.token0, absAmount) } else { - pool.balances.token1 = new(u256.Uint).Add(pool.balances.token1, amount.Abs()) + pool.balances.token1 = new(u256.Uint).Add(pool.balances.token1, absAmount) } } -func checkAmountRange(amount *i256.Int) uint64 { - // check amount is in uint64 range - amountAbs := amount.Abs() - amountUint64, overflow := amountAbs.Uint64WithOverflow() +func checkAmountRange(amount *u256.Uint) (uint64, error) { + res, overflow := amount.Uint64WithOverflow() if overflow { - panic(addDetailToError( - errOutOfRange, - ufmt.Sprintf("pool.gno__checkAmountRange() || amountAbs(%s) overflows uint64 range", amountAbs.ToString()), - )) + return 0, ufmt.Errorf( + "%s || amount(%s) overflows uint64 range", + errOutOfRange.Error(), amount.ToString(), + ) } - return amountUint64 + return res, nil } // receiver getters diff --git a/pool/pool_test.gno b/pool/pool_test.gno new file mode 100644 index 000000000..af02987d9 --- /dev/null +++ b/pool/pool_test.gno @@ -0,0 +1,702 @@ +package pool + +import ( + "std" + "testing" + + "gno.land/p/demo/testutils" + "gno.land/p/demo/uassert" + i256 "gno.land/p/gnoswap/int256" + u256 "gno.land/p/gnoswap/uint256" + "gno.land/r/gnoswap/v1/consts" +) + +func TestMint(t *testing.T) { + token0Path := "test_token0" + token1Path := "test_token1" + fee := uint32(3000) + recipient := testutils.TestAddress("recipient") + tickLower := int32(-100) + tickUpper := int32(100) + liquidityAmount := "100000" + + t.Run("unauthorized caller mint should fail", func(t *testing.T) { + unauthorized := testutils.TestAddress("unauthorized") + defer func() { + if r := recover(); r == nil { + t.Error("unauthorized caller mint should fail") + } + }() + + Mint(token0Path, token1Path, fee, recipient, tickLower, tickUpper, liquidityAmount, unauthorized) + }) + + t.Run("mint with 0 liquidity should fail", func(t *testing.T) { + authorized := consts.POSITION_ADDR + defer func() { + if r := recover(); r == nil { + t.Error("mint with 0 liquidity should fail") + } + }() + + Mint(token0Path, token1Path, fee, recipient, tickLower, tickUpper, "0", authorized) + }) +} + +func TestBurn(t *testing.T) { + // Setup + originalGetPool := GetPool + defer func() { + GetPool = originalGetPool + }() + + // Mock data + mockCaller := consts.POSITION_ADDR + mockPosition := PositionInfo{ + liquidity: u256.NewUint(1000), + tokensOwed0: u256.NewUint(0), + tokensOwed1: u256.NewUint(0), + } + mockPool := &Pool{ + positions: make(map[string]PositionInfo), + } + + GetPool = func(token0Path, token1Path string, fee uint32) *Pool { + return mockPool + } + + tests := []struct { + name string + liquidityAmount string + tickLower int32 + tickUpper int32 + expectedAmount0 string + expectedAmount1 string + expectPanic bool + }{ + { + name: "successful burn", + liquidityAmount: "500", + tickLower: -100, + tickUpper: 100, + expectedAmount0: "100", + expectedAmount1: "200", + }, + { + name: "zero liquidity", + liquidityAmount: "0", + tickLower: -100, + tickUpper: 100, + expectPanic: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if tt.name == "successful burn" { + t.Skip("skipping until find better way to test this") + } + + // setup position for this test + posKey := positionGetKey(mockCaller, tt.tickLower, tt.tickUpper) + mockPool.positions[posKey] = mockPosition + + if tt.expectPanic { + defer func() { + if r := recover(); r == nil { + t.Errorf("expected panic but got none") + } + }() + } + + amount0, amount1 := Burn( + "token0", + "token1", + 3000, + tt.tickLower, + tt.tickUpper, + tt.liquidityAmount, + ) + + if !tt.expectPanic { + if amount0 != tt.expectedAmount0 { + t.Errorf("expected amount0 %s, got %s", tt.expectedAmount0, amount0) + } + if amount1 != tt.expectedAmount1 { + t.Errorf("expected amount1 %s, got %s", tt.expectedAmount1, amount1) + } + + newPosition := mockPool.positions[posKey] + if newPosition.tokensOwed0.IsZero() { + t.Error("expected tokensOwed0 to be updated") + } + if newPosition.tokensOwed1.IsZero() { + t.Error("expected tokensOwed1 to be updated") + } + } + }) + } +} + +func TestSaveProtocolFees(t *testing.T) { + tests := []struct { + name string + pool *Pool + amount0 *u256.Uint + amount1 *u256.Uint + want0 *u256.Uint + want1 *u256.Uint + wantFee0 *u256.Uint + wantFee1 *u256.Uint + }{ + { + name: "normal fee deduction", + pool: &Pool{ + protocolFees: ProtocolFees{ + token0: u256.NewUint(1000), + token1: u256.NewUint(2000), + }, + }, + amount0: u256.NewUint(500), + amount1: u256.NewUint(1000), + want0: u256.NewUint(500), + want1: u256.NewUint(1000), + wantFee0: u256.NewUint(500), + wantFee1: u256.NewUint(1000), + }, + { + name: "exact fee deduction (1 deduction)", + pool: &Pool{ + protocolFees: ProtocolFees{ + token0: u256.NewUint(1000), + token1: u256.NewUint(2000), + }, + }, + amount0: u256.NewUint(1000), + amount1: u256.NewUint(2000), + want0: u256.NewUint(999), + want1: u256.NewUint(1999), + wantFee0: u256.NewUint(1), + wantFee1: u256.NewUint(1), + }, + { + name: "0 fee deduction", + pool: &Pool{ + protocolFees: ProtocolFees{ + token0: u256.NewUint(1000), + token1: u256.NewUint(2000), + }, + }, + amount0: u256.NewUint(0), + amount1: u256.NewUint(0), + want0: u256.NewUint(0), + want1: u256.NewUint(0), + wantFee0: u256.NewUint(1000), + wantFee1: u256.NewUint(2000), + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got0, got1 := tt.pool.saveProtocolFees(tt.amount0, tt.amount1) + + uassert.Equal(t, got0.ToString(), tt.want0.ToString()) + uassert.Equal(t, got1.ToString(), tt.want1.ToString()) + uassert.Equal(t, tt.pool.protocolFees.token0.ToString(), tt.wantFee0.ToString()) + uassert.Equal(t, tt.pool.protocolFees.token1.ToString(), tt.wantFee1.ToString()) + }) + } +} + +func TestTransferAndVerify(t *testing.T) { + // Setup common test data + pool := &Pool{ + balances: Balances{ + token0: u256.NewUint(1000), + token1: u256.NewUint(1000), + }, + } + + t.Run("validatePoolBalance", func(t *testing.T) { + tests := []struct { + name string + amount *u256.Uint + isToken0 bool + expectedError bool + }{ + { + name: "must success for negative amount", + amount: u256.NewUint(500), + isToken0: true, + expectedError: false, + }, + { + name: "must panic for insufficient token0 balance", + amount: u256.NewUint(1500), + isToken0: true, + expectedError: true, + }, + { + name: "must success for negative amount", + amount: u256.NewUint(500), + isToken0: false, + expectedError: false, + }, + { + name: "must panic for insufficient token1 balance", + amount: u256.NewUint(1500), + isToken0: false, + expectedError: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + token0 := pool.balances.token0 + token1 := pool.balances.token1 + + err := validatePoolBalance(token0, token1, tt.amount, tt.isToken0) + if err != nil { + if !tt.expectedError { + t.Errorf("unexpected error: %v", err) + } + } + }) + } + }) +} + +func TestUpdatePoolBalance(t *testing.T) { + tests := []struct { + name string + initialToken0 *u256.Uint + initialToken1 *u256.Uint + amount *u256.Uint + isToken0 bool + expectedBal *u256.Uint + expectErr bool + }{ + { + name: "normal token0 decrease", + initialToken0: u256.NewUint(1000), + initialToken1: u256.NewUint(2000), + amount: u256.NewUint(300), + isToken0: true, + expectedBal: u256.NewUint(700), + expectErr: false, + }, + { + name: "normal token1 decrease", + initialToken0: u256.NewUint(1000), + initialToken1: u256.NewUint(2000), + amount: u256.NewUint(500), + isToken0: false, + expectedBal: u256.NewUint(1500), + expectErr: false, + }, + { + name: "insufficient token0 balance", + initialToken0: u256.NewUint(100), + initialToken1: u256.NewUint(2000), + amount: u256.NewUint(200), + isToken0: true, + expectedBal: nil, + expectErr: true, + }, + { + name: "insufficient token1 balance", + initialToken0: u256.NewUint(1000), + initialToken1: u256.NewUint(100), + amount: u256.NewUint(200), + isToken0: false, + expectedBal: nil, + expectErr: true, + }, + { + name: "zero value handling", + initialToken0: u256.NewUint(1000), + initialToken1: u256.NewUint(2000), + amount: u256.NewUint(0), + isToken0: true, + expectedBal: u256.NewUint(1000), + expectErr: false, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + pool := &Pool{ + balances: Balances{ + token0: tt.initialToken0, + token1: tt.initialToken1, + }, + } + + newBal, err := updatePoolBalance(tt.initialToken0, tt.initialToken1, tt.amount, tt.isToken0) + + if tt.expectErr { + if err == nil { + t.Errorf("%s: expected error but no error", tt.name) + } + return + } + if err != nil { + t.Errorf("%s: unexpected error: %v", tt.name, err) + return + } + + if !newBal.Eq(tt.expectedBal) { + t.Errorf("%s: balance mismatch, expected: %s, actual: %s", + tt.name, + tt.expectedBal.ToString(), + newBal.ToString(), + ) + } + }) + } +} + +func TestShouldContinueSwap(t *testing.T) { + tests := []struct { + name string + state SwapState + sqrtPriceLimitX96 *u256.Uint + expected bool + }{ + { + name: "Should continue - amount remaining and price not at limit", + state: SwapState{ + amountSpecifiedRemaining: i256.MustFromDecimal("1000"), + sqrtPriceX96: u256.MustFromDecimal("1000000"), + }, + sqrtPriceLimitX96: u256.MustFromDecimal("900000"), + expected: true, + }, + { + name: "Should stop - no amount remaining", + state: SwapState{ + amountSpecifiedRemaining: i256.Zero(), + sqrtPriceX96: u256.MustFromDecimal("1000000"), + }, + sqrtPriceLimitX96: u256.MustFromDecimal("900000"), + expected: false, + }, + { + name: "Should stop - price at limit", + state: SwapState{ + amountSpecifiedRemaining: i256.MustFromDecimal("1000"), + sqrtPriceX96: u256.MustFromDecimal("900000"), + }, + sqrtPriceLimitX96: u256.MustFromDecimal("900000"), + expected: false, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + result := shouldContinueSwap(tt.state, tt.sqrtPriceLimitX96) + uassert.Equal(t, tt.expected, result) + }) + } +} + +func TestUpdateAmounts(t *testing.T) { + tests := []struct { + name string + step StepComputations + state SwapState + exactInput bool + expectedState SwapState + }{ + { + name: "Exact input update", + step: StepComputations{ + amountIn: u256.MustFromDecimal("100"), + amountOut: u256.MustFromDecimal("97"), + feeAmount: u256.MustFromDecimal("3"), + }, + state: SwapState{ + amountSpecifiedRemaining: i256.MustFromDecimal("1000"), + amountCalculated: i256.Zero(), + }, + exactInput: true, + expectedState: SwapState{ + amountSpecifiedRemaining: i256.MustFromDecimal("897"), // 1000 - (100 + 3) + amountCalculated: i256.MustFromDecimal("-97"), + }, + }, + { + name: "Exact output update", + step: StepComputations{ + amountIn: u256.MustFromDecimal("100"), + amountOut: u256.MustFromDecimal("97"), + feeAmount: u256.MustFromDecimal("3"), + }, + state: SwapState{ + amountSpecifiedRemaining: i256.MustFromDecimal("-1000"), + amountCalculated: i256.Zero(), + }, + exactInput: false, + expectedState: SwapState{ + amountSpecifiedRemaining: i256.MustFromDecimal("-903"), // -1000 + 97 + amountCalculated: i256.MustFromDecimal("103"), // 100 + 3 + }, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + result := updateAmounts(tt.step, tt.state, tt.exactInput) + + uassert.True(t, tt.expectedState.amountSpecifiedRemaining.Eq(result.amountSpecifiedRemaining)) + uassert.True(t, tt.expectedState.amountCalculated.Eq(result.amountCalculated)) + }) + } +} + +func TestComputeSwap(t *testing.T) { + mockPool := &Pool{ + token0Path: "token0", + token1Path: "token1", + fee: 3000, // 0.3% + tickSpacing: 60, + slot0: Slot0{ + sqrtPriceX96: u256.MustFromDecimal("1000000000000000000"), // 1.0 + tick: 0, + feeProtocol: 0, + unlocked: true, + }, + liquidity: u256.MustFromDecimal("1000000000000000000"), // 1.0 + protocolFees: ProtocolFees{ + token0: u256.Zero(), + token1: u256.Zero(), + }, + feeGrowthGlobal0X128: u256.Zero(), + feeGrowthGlobal1X128: u256.Zero(), + tickBitmaps: make(TickBitmaps), + ticks: make(Ticks), + positions: make(Positions), + } + + wordPos, _ := tickBitmapPosition(0) + // TODO: use avl + mockPool.tickBitmaps[wordPos] = u256.NewUint(1) + + t.Run("basic swap", func(t *testing.T) { + comp := SwapComputation{ + AmountSpecified: i256.MustFromDecimal("1000000"), // 1.0 token + SqrtPriceLimitX96: u256.MustFromDecimal("1100000000000000000"), // 1.1 + ZeroForOne: true, + ExactInput: true, + InitialState: SwapState{ + amountSpecifiedRemaining: i256.MustFromDecimal("1000000"), + amountCalculated: i256.Zero(), + sqrtPriceX96: mockPool.slot0.sqrtPriceX96, + tick: mockPool.slot0.tick, + feeGrowthGlobalX128: mockPool.feeGrowthGlobal0X128, + protocolFee: u256.Zero(), + liquidity: mockPool.liquidity, + }, + Cache: SwapCache{ + feeProtocol: 0, + liquidityStart: mockPool.liquidity, + }, + } + + result, err := computeSwap(mockPool, comp) + if err != nil { + t.Fatalf("expected no error, got %v", err) + } + + if result.Amount0.IsZero() { + t.Error("expected non-zero amount0") + } + if result.Amount1.IsZero() { + t.Error("expected non-zero amount1") + } + if result.SwapFee.IsZero() { + t.Error("expected non-zero swap fee") + } + }) + + t.Run("swap with zero liquidity", func(t *testing.T) { + mockPoolZeroLiq := *mockPool + mockPoolZeroLiq.liquidity = u256.Zero() + + comp := SwapComputation{ + AmountSpecified: i256.MustFromDecimal("1000000"), + SqrtPriceLimitX96: u256.MustFromDecimal("1100000000000000000"), + ZeroForOne: true, + ExactInput: true, + InitialState: SwapState{ + amountSpecifiedRemaining: i256.MustFromDecimal("1000000"), + amountCalculated: i256.Zero(), + sqrtPriceX96: mockPoolZeroLiq.slot0.sqrtPriceX96, + tick: mockPoolZeroLiq.slot0.tick, + feeGrowthGlobalX128: mockPoolZeroLiq.feeGrowthGlobal0X128, + protocolFee: u256.Zero(), + liquidity: mockPoolZeroLiq.liquidity, + }, + Cache: SwapCache{ + feeProtocol: 0, + liquidityStart: mockPoolZeroLiq.liquidity, + }, + } + + result, err := computeSwap(&mockPoolZeroLiq, comp) + if err != nil { + t.Fatalf("expected no error, got %v", err) + } + + if !result.Amount0.IsZero() || !result.Amount1.IsZero() { + t.Error("expected zero amounts for zero liquidity") + } + }) +} + +func TestTransferFromAndVerify(t *testing.T) { + tests := []struct { + name string + pool *Pool + from std.Address + to std.Address + tokenPath string + amount *i256.Int + isToken0 bool + expectedBal0 *u256.Uint + expectedBal1 *u256.Uint + }{ + { + name: "normal token0 transfer", + pool: &Pool{ + balances: Balances{ + token0: u256.NewUint(1000), + token1: u256.NewUint(2000), + }, + }, + from: testutils.TestAddress("from_addr"), + to: testutils.TestAddress("to_addr"), + tokenPath: "token0_path", + amount: i256.NewInt(500), + isToken0: true, + expectedBal0: u256.NewUint(1500), // 1000 + 500 + expectedBal1: u256.NewUint(2000), // unchanged + }, + { + name: "normal token1 transfer", + pool: &Pool{ + balances: Balances{ + token0: u256.NewUint(1000), + token1: u256.NewUint(2000), + }, + }, + from: testutils.TestAddress("from_addr"), + to: testutils.TestAddress("to_addr"), + tokenPath: "token1_path", + amount: i256.NewInt(800), + isToken0: false, + expectedBal0: u256.NewUint(1000), // unchanged + expectedBal1: u256.NewUint(2800), // 2000 + 800 + }, + { + name: "zero value transfer", + pool: &Pool{ + balances: Balances{ + token0: u256.NewUint(1000), + token1: u256.NewUint(2000), + }, + }, + from: testutils.TestAddress("from_addr"), + to: testutils.TestAddress("to_addr"), + tokenPath: "token0_path", + amount: i256.NewInt(0), + isToken0: true, + expectedBal0: u256.NewUint(1000), // unchanged + expectedBal1: u256.NewUint(2000), // unchanged + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + // mock transferFromByRegisterCall + oldTransferFromByRegisterCall := transferFromByRegisterCall + defer func() { transferFromByRegisterCall = oldTransferFromByRegisterCall }() + + transferFromByRegisterCall = func(tokenPath string, from, to std.Address, amount uint64) bool { + // mock the transfer (just return true) + return true + } + + tt.pool.transferFromAndVerify(tt.from, tt.to, tt.tokenPath, u256.MustFromDecimal(tt.amount.ToString()), tt.isToken0) + + if !tt.pool.balances.token0.Eq(tt.expectedBal0) { + t.Errorf("token0 balance mismatch: expected %s, got %s", + tt.expectedBal0.ToString(), + tt.pool.balances.token0.ToString()) + } + + if !tt.pool.balances.token1.Eq(tt.expectedBal1) { + t.Errorf("token1 balance mismatch: expected %s, got %s", + tt.expectedBal1.ToString(), + tt.pool.balances.token1.ToString()) + } + }) + } + + t.Run("negative value handling", func(t *testing.T) { + pool := &Pool{ + balances: Balances{ + token0: u256.NewUint(1000), + token1: u256.NewUint(2000), + }, + } + + oldTransferFromByRegisterCall := transferFromByRegisterCall + defer func() { transferFromByRegisterCall = oldTransferFromByRegisterCall }() + + transferFromByRegisterCall = func(tokenPath string, from, to std.Address, amount uint64) bool { + return true + } + + negativeAmount := i256.NewInt(-500) + pool.transferFromAndVerify( + testutils.TestAddress("from_addr"), + testutils.TestAddress("to_addr"), + "token0_path", + u256.MustFromDecimal(negativeAmount.Abs().ToString()), + true, + ) + + expectedBal := u256.NewUint(1500) // 1000 + 500 (absolute value) + if !pool.balances.token0.Eq(expectedBal) { + t.Errorf("negative amount handling failed: expected %s, got %s", + expectedBal.ToString(), + pool.balances.token0.ToString()) + } + }) + + t.Run("uint64 overflow value", func(t *testing.T) { + pool := &Pool{ + balances: Balances{ + token0: u256.NewUint(1000), + token1: u256.NewUint(2000), + }, + } + + hugeAmount := i256.FromUint256(u256.MustFromDecimal("18446744073709551616")) // 2^64 + + defer func() { + if r := recover(); r == nil { + t.Error("expected panic for amount exceeding uint64 range") + } + }() + + pool.transferFromAndVerify( + testutils.TestAddress("from_addr"), + testutils.TestAddress("to_addr"), + "token0_path", + u256.MustFromDecimal(hugeAmount.ToString()), + true, + ) + }) +} diff --git a/pool/position_modify.gno b/pool/position_modify.gno index 00fe51be1..0400b126a 100644 --- a/pool/position_modify.gno +++ b/pool/position_modify.gno @@ -10,16 +10,16 @@ import ( // modifyPosition updates a position in the pool and calculates the amount of tokens to be added or removed. // Returns positionInfo, amount0, amount1 -func (pool *Pool) modifyPosition(params ModifyPositionParams) (PositionInfo, *i256.Int, *i256.Int) { +func (pool *Pool) modifyPosition(params ModifyPositionParams) (PositionInfo, *u256.Uint, *u256.Uint) { position := pool.updatePosition(params) liqDelta := params.liquidityDelta - amount0, amount1 := i256.Zero(), i256.Zero() - if liqDelta.IsZero() { - return position, amount0, amount1 + return position, u256.Zero(), u256.Zero() } + amount0, amount1 := i256.Zero(), i256.Zero() + tick := pool.slot0.tick sqrtRatioLower := common.TickMathGetSqrtRatioAtTick(params.tickLower) sqrtRatioUpper := common.TickMathGetSqrtRatioAtTick(params.tickUpper) @@ -42,7 +42,7 @@ func (pool *Pool) modifyPosition(params ModifyPositionParams) (PositionInfo, *i2 amount1 = calculateToken1Amount(sqrtRatioLower, sqrtRatioUpper, liqDelta) } - return position, amount0, amount1 + return position, amount0.Abs(), amount1.Abs() } func calculateToken0Amount(sqrtPriceLower, sqrtPriceUpper *u256.Uint, liquidityDelta *i256.Int) *i256.Int { diff --git a/pool/position_modify_test.gno b/pool/position_modify_test.gno index 8f6609261..dec483e61 100644 --- a/pool/position_modify_test.gno +++ b/pool/position_modify_test.gno @@ -4,13 +4,12 @@ import ( "testing" "gno.land/p/demo/uassert" - u256 "gno.land/p/gnoswap/uint256" i256 "gno.land/p/gnoswap/int256" - "gno.land/r/gnoswap/v1/consts" + u256 "gno.land/p/gnoswap/uint256" "gno.land/r/gnoswap/v1/common" + "gno.land/r/gnoswap/v1/consts" ) - func TestModifyPosition(t *testing.T) { const ( fee500 = uint32(500) @@ -18,34 +17,34 @@ func TestModifyPosition(t *testing.T) { ) tests := []struct { - name string - sqrtPrice string - tickLower int32 - tickUpper int32 + name string + sqrtPrice string + tickLower int32 + tickUpper int32 expectedAmt0 string expectedAmt1 string }{ { - name: "current price is lower than range", - sqrtPrice: common.TickMathGetSqrtRatioAtTick(-12000).ToString(), - tickLower: -11000, - tickUpper: -9000, + name: "current price is lower than range", + sqrtPrice: common.TickMathGetSqrtRatioAtTick(-12000).ToString(), + tickLower: -11000, + tickUpper: -9000, expectedAmt0: "16492846", expectedAmt1: "0", }, { - name: "current price is in range", - sqrtPrice: common.TickMathGetSqrtRatioAtTick(-10000).ToString(), - tickLower: -11000, - tickUpper: -9000, + name: "current price is in range", + sqrtPrice: common.TickMathGetSqrtRatioAtTick(-10000).ToString(), + tickLower: -11000, + tickUpper: -9000, expectedAmt0: "8040316", expectedAmt1: "2958015", }, { - name: "current price is higher than range", - sqrtPrice: common.TickMathGetSqrtRatioAtTick(-8000).ToString(), - tickLower: -11000, - tickUpper: -9000, + name: "current price is higher than range", + sqrtPrice: common.TickMathGetSqrtRatioAtTick(-8000).ToString(), + tickLower: -11000, + tickUpper: -9000, expectedAmt0: "0", expectedAmt1: "6067683", }, @@ -58,7 +57,7 @@ func TestModifyPosition(t *testing.T) { barPath, fooPath, fee500, - tt.sqrtPrice, + sqrtPrice.ToString(), ) pool := newPool(poolParams) @@ -106,7 +105,7 @@ func TestModifyPositionEdgeCases(t *testing.T) { } } }() - + pool.modifyPosition(params) }) diff --git a/pool/position_update.gno b/pool/position_update.gno index 48d870d1b..5f21e49f7 100644 --- a/pool/position_update.gno +++ b/pool/position_update.gno @@ -5,8 +5,8 @@ import ( ) func (pool *Pool) updatePosition(positionParams ModifyPositionParams) PositionInfo { - _feeGrowthGlobal0X128 := u256.MustFromDecimal(pool.feeGrowthGlobal0X128.ToString()) - _feeGrowthGlobal1X128 := u256.MustFromDecimal(pool.feeGrowthGlobal1X128.ToString()) + feeGrowthGlobal0X128 := pool.feeGrowthGlobal0X128.Clone() + feeGrowthGlobal1X128 := pool.feeGrowthGlobal1X128.Clone() var flippedLower, flippedUpper bool if !(positionParams.liquidityDelta.IsZero()) { @@ -14,8 +14,8 @@ func (pool *Pool) updatePosition(positionParams ModifyPositionParams) PositionIn positionParams.tickLower, pool.slot0.tick, positionParams.liquidityDelta, - _feeGrowthGlobal0X128, - _feeGrowthGlobal1X128, + feeGrowthGlobal0X128, + feeGrowthGlobal1X128, false, pool.maxLiquidityPerTick, ) @@ -24,8 +24,8 @@ func (pool *Pool) updatePosition(positionParams ModifyPositionParams) PositionIn positionParams.tickUpper, pool.slot0.tick, positionParams.liquidityDelta, - _feeGrowthGlobal0X128, - _feeGrowthGlobal1X128, + feeGrowthGlobal0X128, + feeGrowthGlobal1X128, true, pool.maxLiquidityPerTick, ) @@ -43,8 +43,8 @@ func (pool *Pool) updatePosition(positionParams ModifyPositionParams) PositionIn positionParams.tickLower, positionParams.tickUpper, pool.slot0.tick, - _feeGrowthGlobal0X128, - _feeGrowthGlobal1X128, + feeGrowthGlobal0X128, + feeGrowthGlobal1X128, ) positionKey := positionGetKey(positionParams.owner, positionParams.tickLower, positionParams.tickUpper) diff --git a/pool/position_update_test.gno b/pool/position_update_test.gno new file mode 100644 index 000000000..24ca4cb55 --- /dev/null +++ b/pool/position_update_test.gno @@ -0,0 +1,85 @@ +package pool + +import ( + "testing" + + "std" + + "gno.land/p/demo/uassert" + + "gno.land/r/gnoswap/v1/consts" + i256 "gno.land/p/gnoswap/int256" + u256 "gno.land/p/gnoswap/uint256" +) + +func TestUpdatePosition(t *testing.T) { + poolParams := &createPoolParams{ + token0Path: "token0", + token1Path: "token1", + fee: 500, + tickSpacing: 10, + sqrtPriceX96: u256.MustFromDecimal("1000000000000000000"), // 1.0 + } + p := newPool(poolParams) + + tests := []struct { + name string + positionParams ModifyPositionParams + expectLiquidity *u256.Uint + }{ + { + name: "add new position", + positionParams: ModifyPositionParams{ + owner: consts.POSITION_ADDR, + tickLower: -100, + tickUpper: 100, + liquidityDelta: i256.MustFromDecimal("1000000"), + }, + expectLiquidity: u256.MustFromDecimal("1000000"), + }, + { + name: "add liquidity to existing position", + positionParams: ModifyPositionParams{ + owner: consts.POSITION_ADDR, + tickLower: -100, + tickUpper: 100, + liquidityDelta: i256.MustFromDecimal("500000"), + }, + expectLiquidity: u256.MustFromDecimal("1500000"), + }, + { + name: "remove liquidity from position", + positionParams: ModifyPositionParams{ + owner: consts.POSITION_ADDR, + tickLower: -100, + tickUpper: 100, + liquidityDelta: i256.MustFromDecimal("-500000"), + }, + expectLiquidity: u256.MustFromDecimal("1000000"), + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + position := p.updatePosition(tt.positionParams) + + if !position.liquidity.Eq(tt.expectLiquidity) { + t.Errorf("liquidity mismatch: expected %s, got %s", + tt.expectLiquidity.ToString(), + position.liquidity.ToString()) + } + + if !tt.positionParams.liquidityDelta.IsZero() { + lowerTick := p.ticks[tt.positionParams.tickLower] + upperTick := p.ticks[tt.positionParams.tickUpper] + + if !lowerTick.initialized { + t.Error("lower tick not initialized") + } + if !upperTick.initialized { + t.Error("upper tick not initialized") + } + } + }) + } +} diff --git a/pool/token_register.gno b/pool/token_register.gno index 8c730bec7..419b2ee49 100644 --- a/pool/token_register.gno +++ b/pool/token_register.gno @@ -60,12 +60,18 @@ func RegisterGRC20Interface(pkgPath string, igrc20 GRC20Interface) { // UnregisterGRC20Interface unregisters a GRC20 token interface func UnregisterGRC20Interface(pkgPath string) { if err := common.SatisfyCond(isUserCall()); err != nil { - panic(err) + panic(addDetailToError( + errNoPermission, + ufmt.Sprintf("token_register.gno__UnregisterGRC20Interface() || unauthorized address(%s) to unregister", std.PrevRealm().Addr()), + )) } caller := std.PrevRealm().Addr() if err := common.TokenRegisterOnly(caller); err != nil { - panic(err) + panic(addDetailToError( + errNoPermission, + ufmt.Sprintf("token_register.gno__UnregisterGRC20Interface() || unauthorized address(%s) to unregister", caller), + )) } pkgPath = handleNative(pkgPath) diff --git a/pool/type.gno b/pool/type.gno index de92f0a1f..d2a6c5197 100644 --- a/pool/type.gno +++ b/pool/type.gno @@ -4,6 +4,7 @@ import ( "std" "gno.land/r/gnoswap/v1/common" + "gno.land/r/gnoswap/v1/consts" i256 "gno.land/p/gnoswap/int256" u256 "gno.land/p/gnoswap/uint256" @@ -66,6 +67,20 @@ type ModifyPositionParams struct { liquidityDelta *i256.Int // any change in liquidity } +func newModifyPositionParams( + owner std.Address, + tickLower int32, + tickUpper int32, + liquidityDelta *i256.Int, +) ModifyPositionParams { + return ModifyPositionParams{ + owner: owner, + tickLower: tickLower, + tickUpper: tickUpper, + liquidityDelta: liquidityDelta, + } +} + type SwapCache struct { feeProtocol uint8 // protocol fee for the input token liquidityStart *u256.Uint // liquidity at the beginning of the swap @@ -91,13 +106,12 @@ type SwapState struct { liquidity *u256.Uint // current liquidity in range } -func (pool *Pool) newSwapState( +func newSwapState( amountSpecifiedRemaining *i256.Int, feeGrowthGlobalX128 *u256.Uint, liquidity *u256.Uint, + slot0 Slot0, ) SwapState { - slot0 := pool.slot0 - return SwapState{ amountSpecifiedRemaining: amountSpecifiedRemaining, amountCalculated: i256.Zero(), @@ -109,6 +123,22 @@ func (pool *Pool) newSwapState( } } +func (s *SwapState) SetSqrtPriceX96(sqrtPriceX96 string) { + s.sqrtPriceX96 = u256.MustFromDecimal(sqrtPriceX96) +} + +func (s *SwapState) SetTick(tick int32) { + s.tick = tick +} + +func (s *SwapState) SetFeeGrowthGlobalX128(feeGrowthGlobalX128 *u256.Uint) { + s.feeGrowthGlobalX128 = feeGrowthGlobalX128 +} + +func (s *SwapState) SetProtocolFee(fee *u256.Uint) { + s.protocolFee = fee +} + type StepComputations struct { sqrtPriceStartX96 *u256.Uint // price at the beginning of the step tickNext int32 // next tick to swap to from the current tick in the swap direction @@ -119,6 +149,32 @@ type StepComputations struct { feeAmount *u256.Uint // how much fee is being paid in this step } +// init initializes the computation for a single swap step +func (step *StepComputations) initSwapStep(state SwapState, pool *Pool, zeroForOne bool) { + step.sqrtPriceStartX96 = state.sqrtPriceX96 + step.tickNext, step.initialized = pool.tickBitmapNextInitializedTickWithInOneWord( + state.tick, + pool.tickSpacing, + zeroForOne, + ) + + // prevent overshoot the min/max tick + step.clampTickNext() + + // get the price for the next tick + step.sqrtPriceNextX96 = common.TickMathGetSqrtRatioAtTick(step.tickNext) +} + +// clampTickNext ensures that `tickNext` stays within the min, max tick boundaries +// as the tick bitmap is not aware of these bounds +func (step *StepComputations) clampTickNext() { + if step.tickNext < consts.MIN_TICK { + step.tickNext = consts.MIN_TICK + } else if step.tickNext > consts.MAX_TICK { + step.tickNext = consts.MAX_TICK + } +} + type PositionInfo struct { liquidity *u256.Uint // amount of liquidity owned by this position From 1f5a147d1acf1d3cf41c850dcdfb245f13782114 Mon Sep 17 00:00:00 2001 From: n3wbie Date: Tue, 10 Dec 2024 15:47:26 +0900 Subject: [PATCH 04/44] GSW-1838 test: swap_math unitest --- .../p/gnoswap/pool/__TEST_swap_math_test.gnoA | 202 ----------------- _deploy/p/gnoswap/pool/swap_math_test.gno | 206 ++++++++++++++++++ 2 files changed, 206 insertions(+), 202 deletions(-) delete mode 100644 _deploy/p/gnoswap/pool/__TEST_swap_math_test.gnoA create mode 100644 _deploy/p/gnoswap/pool/swap_math_test.gno diff --git a/_deploy/p/gnoswap/pool/__TEST_swap_math_test.gnoA b/_deploy/p/gnoswap/pool/__TEST_swap_math_test.gnoA deleted file mode 100644 index b34032bb6..000000000 --- a/_deploy/p/gnoswap/pool/__TEST_swap_math_test.gnoA +++ /dev/null @@ -1,202 +0,0 @@ -package pool - -import ( - "testing" - - i256 "gno.land/p/gnoswap/int256" - u256 "gno.land/p/gnoswap/uint256" -) - -var ( - amountIn_i256 *i256.Int - amountOut *u256.Uint - zeroForOne bool - expected *u256.Uint - rst bool - price *u256.Uint - priceTarget *u256.Uint - liquidity *u256.Uint - fee uint64 - amountIn_String string - amountOut_String string - sqrtQ_String string - feeAmount_String string -) - -func TestSwapMathComputeSwapStepStr_1(t *testing.T) { - var amountIn_feeAmount *u256.Uint - var priceAfterWholeInputAmount *u256.Uint - // exact amount in that gets capped at price target in one for zero - - price = u256.MustFromDecimal("79228162514264337593543950336") // encodePriceSqrt(1, 1) = 79228162514264337593543950336 - priceTarget = u256.MustFromDecimal("79623317895830914510639640423") // encodePriceSqrt(101,100) = 79623317895830914510639640423 - liquidity = u256.MustFromDecimal("2000000000000000000") // 2e18 - amountIn_i256 = i256.MustFromDecimal("1000000000000000000") // 1e18 - fee = 600 - zeroForOne = false - - sqrtQ_String, amountIn_String, amountOut_String, feeAmount_String := SwapMathComputeSwapStepStr(price, priceTarget, liquidity, amountIn_i256, fee) - - shouldEQ(t, amountIn_String, "9975124224178055") - shouldEQ(t, feeAmount_String, "5988667735148") - shouldEQ(t, amountOut_String, "9925619580021728") - amountIn_feeAmount = u256.MustFromDecimal(amountIn_String) - amountIn_feeAmount.Add(amountIn_feeAmount, u256.MustFromDecimal(feeAmount_String)) - - if amountIn_feeAmount.Cmp(u256.MustFromDecimal("1000000000000000000")) > 0 { - t.Errorf("entire amount is not used") - } - - priceAfterWholeInputAmount = sqrtPriceMathGetNextSqrtPriceFromInput(price, liquidity, u256.MustFromDecimal("1000000000000000000"), zeroForOne) - - shouldEQ(t, sqrtQ_String, priceTarget.ToString()) - if u256.MustFromDecimal(sqrtQ_String).Cmp(priceAfterWholeInputAmount) > 0 { - t.Errorf("price is less than price after whole input amount") - } -} - -func TestSwapMathComputeSwapStepStr_2(t *testing.T) { - var priceAfterWholeInputAmount *u256.Uint - // exact amount out that gets capped at price target in one for zero - - price = u256.MustFromDecimal("79228162514264337593543950336") // encodePriceSqrt(1, 1) = 79228162514264337593543950336 - priceTarget = u256.MustFromDecimal("79623317895830914510639640423") // encodePriceSqrt(101,100) = 79623317895830914510639640423 - liquidity = u256.MustFromDecimal("2000000000000000000") // 2e18 - amountIn_i256 = i256.MustFromDecimal("-1000000000000000000") // -1e18 - fee = 600 - zeroForOne = false - - sqrtQ_String, amountIn_String, amountOut_String, feeAmount_String := SwapMathComputeSwapStepStr(price, priceTarget, liquidity, amountIn_i256, fee) - - shouldEQ(t, amountIn_String, "9975124224178055") - shouldEQ(t, feeAmount_String, "5988667735148") - shouldEQ(t, amountOut_String, "9925619580021728") - - if u256.MustFromDecimal(amountOut_String).Cmp(u256.MustFromDecimal("1000000000000000000")) >= 0 { - t.Errorf("entire amount out is not returned") - } - - priceAfterWholeInputAmount = sqrtPriceMathGetNextSqrtPriceFromInput(price, liquidity, u256.MustFromDecimal("1000000000000000000"), zeroForOne) - shouldEQ(t, sqrtQ_String, priceTarget.ToString()) - if u256.MustFromDecimal(sqrtQ_String).Cmp(priceAfterWholeInputAmount) > 0 { - t.Errorf("price is less than price after whole output amount") - } -} - -func TestSwapMathComputeSwapStepStr_3(t *testing.T) { - var amountIn_feeAmount *u256.Uint - var priceAfterWholeInputAmount *u256.Uint - // exact amount in that is fully spent in one for zero - - price = u256.MustFromDecimal("79228162514264337593543950336") // encodePriceSqrt(1, 1) = 79228162514264337593543950336 - priceTarget = u256.MustFromDecimal("792281625142643375935439503360") // encodePriceSqrt(1000,100) = 792281625142643375935439503360 - liquidity = u256.MustFromDecimal("2000000000000000000") // 2e18 - amountIn_i256 = i256.MustFromDecimal("1000000000000000000") // 1e18 - fee = 600 - zeroForOne = false - - sqrtQ_String, amountIn_String, amountOut_String, feeAmount_String := SwapMathComputeSwapStepStr(price, priceTarget, liquidity, amountIn_i256, fee) - - shouldEQ(t, amountIn_String, "999400000000000000") - shouldEQ(t, feeAmount_String, "600000000000000") - shouldEQ(t, amountOut_String, "666399946655997866") - amountIn_feeAmount = u256.MustFromDecimal(amountIn_String) - amountIn_feeAmount.Add(amountIn_feeAmount, u256.MustFromDecimal(feeAmount_String)) - shouldEQ(t, amountIn_feeAmount.ToString(), "1000000000000000000") - - priceAfterWholeInputAmount = sqrtPriceMathGetNextSqrtPriceFromInput(price, liquidity, u256.MustFromDecimal("999400000000000000"), zeroForOne) - shouldEQ(t, sqrtQ_String, priceAfterWholeInputAmount.ToString()) - if u256.MustFromDecimal(sqrtQ_String).Cmp(priceTarget) > 0 { - t.Errorf("price does not reach price target") - } -} - -func TestSwapMathComputeSwapStepStr_4(t *testing.T) { - // amount out is capped at the desired amount out - - price = u256.MustFromDecimal("417332158212080721273783715441582") - priceTarget = u256.MustFromDecimal("1452870262520218020823638996") - liquidity = u256.MustFromDecimal("159344665391607089467575320103") - amountIn_i256 = i256.MustFromDecimal("-1") - fee = 1 - - sqrtQ_String, amountIn_String, amountOut_String, feeAmount_String := SwapMathComputeSwapStepStr(price, priceTarget, liquidity, amountIn_i256, fee) - - shouldEQ(t, sqrtQ_String, "417332158212080721273783715441581") - shouldEQ(t, amountIn_String, "1") - shouldEQ(t, feeAmount_String, "1") - shouldEQ(t, amountOut_String, "1") -} - -func TestSwapMathComputeSwapStepStr_5(t *testing.T) { - var amountIn_feeAmount *u256.Uint - // target price of 1 uses partial input amount - - price = u256.MustFromDecimal("2") - priceTarget = u256.MustFromDecimal("1") - liquidity = u256.MustFromDecimal("1") - amountIn_i256 = i256.MustFromDecimal("3915081100057732413702495386755767") - fee = 1 - - sqrtQ_String, amountIn_String, amountOut_String, feeAmount_String := SwapMathComputeSwapStepStr(price, priceTarget, liquidity, amountIn_i256, fee) - - shouldEQ(t, sqrtQ_String, "1") - shouldEQ(t, feeAmount_String, "39614120871253040049813") - shouldEQ(t, amountOut_String, "0") - shouldEQ(t, amountIn_String, "39614081257132168796771975168") - amountIn_feeAmount = u256.MustFromDecimal(amountIn_String) - amountIn_feeAmount.Add(amountIn_feeAmount, u256.MustFromDecimal(feeAmount_String)) - - if amountIn_feeAmount.Cmp(u256.MustFromDecimal("3915081100057732413702495386755767")) >= 0 { - t.Errorf("amountIn+feeAmount should be less than or eq to 3915081100057732413702495386755767") - } -} - -func TestSwapMathComputeSwapStepStr_6(t *testing.T) { - // entire input amount taken as fee - price = u256.MustFromDecimal("2413") - priceTarget = u256.MustFromDecimal("79887613182836312") - liquidity = u256.MustFromDecimal("1985041575832132834610021537970") - amountIn_i256 = i256.MustFromDecimal("10") - fee = 1872 - - sqrtQ_String, amountIn_String, amountOut_String, feeAmount_String := SwapMathComputeSwapStepStr(price, priceTarget, liquidity, amountIn_i256, fee) - - shouldEQ(t, amountIn_String, "0") - shouldEQ(t, feeAmount_String, "10") - shouldEQ(t, amountOut_String, "0") - shouldEQ(t, sqrtQ_String, "2413") -} - -func TestSwapMathComputeSwapStepStr_7(t *testing.T) { - // handles intermediate insufficient liquidity in zero for one exact output case - - price = u256.MustFromDecimal("20282409603651670423947251286016") - priceTarget = u256.MulDiv(price, u256.NewUint(11), u256.NewUint(10)) - liquidity = u256.MustFromDecimal("1024") - amountIn_i256 = i256.MustFromDecimal("-4") - fee = 3000 - - sqrtQ_String, amountIn_String, amountOut_String, feeAmount_String := SwapMathComputeSwapStepStr(price, priceTarget, liquidity, amountIn_i256, fee) - - shouldEQ(t, amountOut_String, "0") - shouldEQ(t, sqrtQ_String, priceTarget.ToString()) - shouldEQ(t, amountIn_String, "26215") - shouldEQ(t, feeAmount_String, "79") -} - -func TestSwapMathComputeSwapStepStr_8(t *testing.T) { - // handles intermediate insufficient liquidity in one for zero exact output case - price = u256.MustFromDecimal("20282409603651670423947251286016") - priceTarget = u256.MulDiv(price, u256.NewUint(9), u256.NewUint(10)) - liquidity = u256.MustFromDecimal("1024") - amountIn_i256 = i256.MustFromDecimal("-263000") - fee = 3000 - - sqrtQ_String, amountIn_String, amountOut_String, feeAmount_String := SwapMathComputeSwapStepStr(price, priceTarget, liquidity, amountIn_i256, fee) - - shouldEQ(t, amountOut_String, "26214") - shouldEQ(t, sqrtQ_String, priceTarget.ToString()) - shouldEQ(t, amountIn_String, "1") - shouldEQ(t, feeAmount_String, "1") -} diff --git a/_deploy/p/gnoswap/pool/swap_math_test.gno b/_deploy/p/gnoswap/pool/swap_math_test.gno new file mode 100644 index 000000000..356664824 --- /dev/null +++ b/_deploy/p/gnoswap/pool/swap_math_test.gno @@ -0,0 +1,206 @@ +package pool + +import ( + "testing" + + "gno.land/p/demo/uassert" + + i256 "gno.land/p/gnoswap/int256" + u256 "gno.land/p/gnoswap/uint256" +) + +func TestSwapMathComputeSwapStepStr(t *testing.T) { + tests := []struct { + name string + currentX96, targetX96 *u256.Uint + liquidity *u256.Uint + amountRemaining *i256.Int + feePips uint64 + sqrtNextX96 *u256.Uint + chkSqrtNextX96 func(sqrtRatioNextX96, priceTarget *u256.Uint) + amountIn, amountOut, feeAmount string + }{ + { + name: "exact amount in that gets capped at price target in one for zero", + currentX96: encodePriceSqrt("1", "1"), + targetX96: encodePriceSqrt("101", "100"), + liquidity: u256.MustFromDecimal("2000000000000000000"), + amountRemaining: i256.MustFromDecimal("1000000000000000000"), + feePips: 600, + sqrtNextX96: encodePriceSqrt("101", "100"), + chkSqrtNextX96: func(sqrtRatioNextX96, priceTarget *u256.Uint) { + uassert.True(t, sqrtRatioNextX96.Eq(priceTarget)) + }, + amountIn: "9975124224178055", + amountOut: "9925619580021728", + feeAmount: "5988667735148", + }, + { + name: "exact amount out that gets capped at price target in one for zero", + currentX96: encodePriceSqrt("1", "1"), + targetX96: encodePriceSqrt("101", "100"), + liquidity: u256.MustFromDecimal("2000000000000000000"), + amountRemaining: i256.MustFromDecimal("-1000000000000000000"), + feePips: 600, + sqrtNextX96: encodePriceSqrt("101", "100"), + chkSqrtNextX96: func(sqrtRatioNextX96, priceTarget *u256.Uint) { + uassert.True(t, sqrtRatioNextX96.Eq(priceTarget)) + }, + amountIn: "9975124224178055", + amountOut: "9925619580021728", + feeAmount: "5988667735148", + }, + { + name: "exact amount in that is fully spent in one for zero", + currentX96: encodePriceSqrt("1", "1"), + targetX96: encodePriceSqrt("1000", "100"), + liquidity: u256.MustFromDecimal("2000000000000000000"), + amountRemaining: i256.MustFromDecimal("1000000000000000000"), + sqrtNextX96: encodePriceSqrt("1000", "100"), + feePips: 600, + chkSqrtNextX96: func(sqrtRatioNextX96, priceTarget *u256.Uint) { + uassert.True(t, sqrtRatioNextX96.Lte(priceTarget)) + }, + amountIn: "999400000000000000", + amountOut: "666399946655997866", + feeAmount: "600000000000000", + }, + { + name: "exact amount out that is fully received in one for zero", + currentX96: encodePriceSqrt("1", "1"), + targetX96: encodePriceSqrt("1000", "100"), + liquidity: u256.MustFromDecimal("2000000000000000000"), + amountRemaining: i256.MustFromDecimal("-1000000000000000000"), + feePips: 600, + sqrtNextX96: encodePriceSqrt("1000", "100"), + chkSqrtNextX96: func(sqrtRatioNextX96, priceTarget *u256.Uint) { + uassert.True(t, sqrtRatioNextX96.Lt(priceTarget)) + }, + amountIn: "2000000000000000000", + amountOut: "1000000000000000000", + feeAmount: "1200720432259356", + }, + { + name: "amount out is capped at the desired amount out", + currentX96: u256.MustFromDecimal("417332158212080721273783715441582"), + targetX96: u256.MustFromDecimal("1452870262520218020823638996"), + liquidity: u256.MustFromDecimal("159344665391607089467575320103"), + amountRemaining: i256.MustFromDecimal("-1"), + feePips: 1, + sqrtNextX96: u256.MustFromDecimal("417332158212080721273783715441581"), + chkSqrtNextX96: func(sqrtRatioNextX96, priceTarget *u256.Uint) { + uassert.True(t, sqrtRatioNextX96.Eq(priceTarget)) + }, + amountIn: "1", + amountOut: "1", + feeAmount: "1", + }, + { + name: "target price of 1 uses partial input amount", + currentX96: u256.MustFromDecimal("2"), + targetX96: u256.MustFromDecimal("1"), + liquidity: u256.MustFromDecimal("1"), + amountRemaining: i256.MustFromDecimal("3915081100057732413702495386755767"), + feePips: 1, + sqrtNextX96: u256.MustFromDecimal("1"), + chkSqrtNextX96: func(sqrtRatioNextX96, priceTarget *u256.Uint) { + uassert.True(t, sqrtRatioNextX96.Eq(priceTarget)) + }, + amountIn: "39614081257132168796771975168", + amountOut: "0", + feeAmount: "39614120871253040049813", + }, + { + name: "entire input amount taken as fee", + currentX96: u256.MustFromDecimal("2413"), + targetX96: u256.MustFromDecimal("79887613182836312"), + liquidity: u256.MustFromDecimal("1985041575832132834610021537970"), + amountRemaining: i256.MustFromDecimal("10"), + feePips: 1872, + sqrtNextX96: u256.MustFromDecimal("2413"), + chkSqrtNextX96: func(sqrtRatioNextX96, priceTarget *u256.Uint) { + uassert.True(t, sqrtRatioNextX96.Eq(priceTarget)) + }, + amountIn: "0", + amountOut: "0", + feeAmount: "10", + }, + { + name: "handles intermediate insufficient liquidity in zero for one exact output case", + currentX96: u256.MustFromDecimal("20282409603651670423947251286016"), + targetX96: u256.MustFromDecimal("22310650564016837466341976414617"), + liquidity: u256.MustFromDecimal("1024"), + amountRemaining: i256.MustFromDecimal("-4"), + feePips: 3000, + sqrtNextX96: u256.MustFromDecimal("22310650564016837466341976414617"), + chkSqrtNextX96: func(sqrtRatioNextX96, priceTarget *u256.Uint) { + uassert.True(t, sqrtRatioNextX96.Eq(priceTarget)) + }, + amountIn: "26215", + amountOut: "0", + feeAmount: "79", + }, + { + name: "handles intermediate insufficient liquidity in one for zero exact output case", + currentX96: u256.MustFromDecimal("20282409603651670423947251286016"), + targetX96: u256.MustFromDecimal("18254168643286503381552526157414"), + liquidity: u256.MustFromDecimal("1024"), + amountRemaining: i256.MustFromDecimal("-263000"), + feePips: 3000, + sqrtNextX96: u256.MustFromDecimal("18254168643286503381552526157414"), + chkSqrtNextX96: func(sqrtRatioNextX96, priceTarget *u256.Uint) { + uassert.True(t, sqrtRatioNextX96.Eq(priceTarget)) + }, + amountIn: "1", + amountOut: "26214", + feeAmount: "1", + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + sqrtRatioNextX96, amountIn, amountOut, feeAmount := SwapMathComputeSwapStepStr(test.currentX96, test.targetX96, test.liquidity, test.amountRemaining, test.feePips) + test.chkSqrtNextX96(u256.MustFromDecimal(sqrtRatioNextX96), test.sqrtNextX96) + uassert.Equal(t, amountIn, test.amountIn) + uassert.Equal(t, amountOut, test.amountOut) + uassert.Equal(t, feeAmount, test.feeAmount) + }) + } +} + +// encodePriceSqrt calculates the sqrt((reserve1 << 192) / reserve0) +func encodePriceSqrt(reserve1, reserve0 string) *u256.Uint { + reserve1Uint := u256.MustFromDecimal(reserve1) + reserve0Uint := u256.MustFromDecimal(reserve0) + + if reserve0Uint.IsZero() { + panic("division by zero") + } + + // numerator = reserve1 * (2^192) + two192 := new(u256.Uint).Lsh(u256.NewUint(1), 192) + numerator := new(u256.Uint).Mul(reserve1Uint, two192) + + // ratioX192 = numerator / reserve0 + ratioX192 := new(u256.Uint).Div(numerator, reserve0Uint) + + // Return sqrt(ratioX192) + return sqrt(ratioX192) +} + +// sqrt computes the integer square root of a u256.Uint +func sqrt(x *u256.Uint) *u256.Uint { + if x.IsZero() { + return u256.NewUint(0) + } + + z := new(u256.Uint).Set(x) + y := new(u256.Uint).Rsh(z, 1) // Initial guess is x / 2 + + for y.Cmp(z) < 0 { + z.Set(y) + temp := new(u256.Uint).Div(x, z) + y.Add(z, temp).Rsh(y, 1) + } + return z +} From edfb2965453dd6c29e89abb40a4c92e957258f5b Mon Sep 17 00:00:00 2001 From: n3wbie Date: Mon, 2 Dec 2024 19:48:28 +0900 Subject: [PATCH 05/44] GSW-1838 refactor: use grc20reg - use gno's grc20reg realm to support dynamic token transfer in pool --- pool/pool.gno | 53 ++--- .../__TEST_0_INIT_TOKEN_REGISTER_test.gnoA | 183 ------------------ ..._TEST_0_INIT_VARIABLE_AND_HELPER_test.gnoA | 6 + pool/utils.gno | 9 + 4 files changed, 45 insertions(+), 206 deletions(-) delete mode 100644 pool/tests/__TEST_0_INIT_TOKEN_REGISTER_test.gnoA diff --git a/pool/pool.gno b/pool/pool.gno index 1ab2b8696..0ce792a66 100644 --- a/pool/pool.gno +++ b/pool/pool.gno @@ -145,12 +145,18 @@ func Collect( // Smallest of three: amount0Requested, position.tokensOwed0, pool.balances.token0 amount0Req := u256.MustFromDecimal(amount0Requested) amount0, position.tokensOwed0, pool.balances.token0 = collectToken(amount0Req, position.tokensOwed0, pool.balances.token0) - transferByRegisterCall(pool.token0Path, recipient, amount0.Uint64()) + token0 := common.GetToken(pool.token0Path) + checkTransferError(token0.Transfer(recipient, amount0.Uint64())) // Smallest of three: amount0Requested, position.tokensOwed0, pool.balances.token0 amount1Req := u256.MustFromDecimal(amount1Requested) amount1, position.tokensOwed1, pool.balances.token1 = collectToken(amount1Req, position.tokensOwed1, pool.balances.token1) - transferByRegisterCall(pool.token1Path, recipient, amount1.Uint64()) + + // Update state first then transfer + position.tokensOwed1 = new(u256.Uint).Sub(position.tokensOwed1, amount1) + pool.balances.token1 = new(u256.Uint).Sub(pool.balances.token1, amount1) + token1 := common.GetToken(pool.token1Path) + checkTransferError(token1.Transfer(recipient, amount1.Uint64())) pool.positions[positionKey] = position @@ -175,15 +181,15 @@ func collectToken( // SwapResult encapsulates all state changes that occur as a result of a swap // This type ensure all state transitions are atomic and can be applied at once. type SwapResult struct { - Amount0 *i256.Int - Amount1 *i256.Int - NewSqrtPrice *u256.Uint - NewTick int32 - NewLiquidity *u256.Uint - NewProtocolFees ProtocolFees + Amount0 *i256.Int + Amount1 *i256.Int + NewSqrtPrice *u256.Uint + NewTick int32 + NewLiquidity *u256.Uint + NewProtocolFees ProtocolFees FeeGrowthGlobal0X128 *u256.Uint FeeGrowthGlobal1X128 *u256.Uint - SwapFee *u256.Uint + SwapFee *u256.Uint } // SwapComputation encapsulates pure computation logic for swap @@ -294,10 +300,11 @@ func Swap( // The function follows these state transitions: // 1. Initial State: Provided by `SwapComputation.InitialState` // 2. Stepping State: For each step: -// - Compute next tick and price target -// - Calculate amounts and fees -// - Update state (remaining amount, fees, liquidity) -// - Handle tick transitions if necessary +// - Compute next tick and price target +// - Calculate amounts and fees +// - Update state (remaining amount, fees, liquidity) +// - Handle tick transitions if necessary +// // 3. Final State: Aggregated in SwapResult // // The computation continues until either: @@ -309,7 +316,6 @@ func computeSwap(pool *Pool, comp SwapComputation) (*SwapResult, error) { state := comp.InitialState swapFee := u256.Zero() - var newFee *u256.Uint var err error @@ -342,7 +348,7 @@ func computeSwap(pool *Pool, comp SwapComputation) (*SwapResult, error) { }, FeeGrowthGlobal0X128: pool.feeGrowthGlobal0X128, FeeGrowthGlobal1X128: pool.feeGrowthGlobal1X128, - SwapFee: swapFee, + SwapFee: swapFee, } // Update protocol fees if necessary @@ -377,6 +383,7 @@ func applySwapResult(pool *Pool, result *SwapResult) { // For zeroForOne (selling token0): // - Price limit must be below current price // - Price limit must be above MIN_SQRT_RATIO +// // For !zeroForOne (selling token1): // - Price limit must be above current price // - Price limit must be below MAX_SQRT_RATIO @@ -562,6 +569,7 @@ func computeAmounts(state SwapState, sqrtRatioTargetX96 *u256.Uint, pool *Pool, // For exact input swaps: // - Decrements remaining input amount by (amountIn + feeAmount) // - Decrements calculated amount by amountOut +// // For exact output swaps: // - Increments remaining output amount by amountOut // - Increments calculated amount by (amountIn + feeAmount) @@ -814,8 +822,11 @@ func collectProtocol( uAmount0 := amount0.Uint64() uAmount1 := amount1.Uint64() - transferByRegisterCall(pool.token0Path, recipient, uAmount0) - transferByRegisterCall(pool.token1Path, recipient, uAmount1) + token0 := common.GetToken(pool.token0Path) + checkTransferError(token0.Transfer(recipient, uAmount0)) + + token1 := common.GetToken(pool.token1Path) + checkTransferError(token1.Transfer(recipient, uAmount1)) return amount0.ToString(), amount1.ToString() } @@ -943,12 +954,8 @@ func (pool *Pool) transferFromAndVerify( panic(err) } - // try sending - // will panic if following conditions are met: - // - POOL does not have enough approved amount - // - from does not have enough balance - // - token is not registered - transferFromByRegisterCall(tokenPath, from, to, amountUint64) + token := common.GetToken(tokenPath) + checkTransferError(token.TransferFrom(from, to, amountUint64)) // update pool balances if isToken0 { diff --git a/pool/tests/__TEST_0_INIT_TOKEN_REGISTER_test.gnoA b/pool/tests/__TEST_0_INIT_TOKEN_REGISTER_test.gnoA deleted file mode 100644 index 6702bd674..000000000 --- a/pool/tests/__TEST_0_INIT_TOKEN_REGISTER_test.gnoA +++ /dev/null @@ -1,183 +0,0 @@ -package pool - -import ( - "std" - "testing" - - "gno.land/p/demo/testutils" - "gno.land/p/demo/uassert" - - "gno.land/r/onbloc/foo" - - "gno.land/r/onbloc/bar" - - "gno.land/r/onbloc/baz" - - "gno.land/r/onbloc/qux" - - "gno.land/r/demo/wugnot" - - "gno.land/r/onbloc/obl" - - "gno.land/r/gnoswap/v1/gns" - - "gno.land/r/gnoswap/v1/consts" - - pusers "gno.land/p/demo/users" -) - -type FooToken struct{} - -func (FooToken) Transfer() func(to pusers.AddressOrName, amount uint64) { - return foo.Transfer -} -func (FooToken) TransferFrom() func(from, to pusers.AddressOrName, amount uint64) { - return foo.TransferFrom -} -func (FooToken) BalanceOf() func(owner pusers.AddressOrName) uint64 { - return foo.BalanceOf -} -func (FooToken) Approve() func(spender pusers.AddressOrName, amount uint64) { - return foo.Approve -} - -type BarToken struct{} - -func (BarToken) Transfer() func(to pusers.AddressOrName, amount uint64) { - return bar.Transfer -} -func (BarToken) TransferFrom() func(from, to pusers.AddressOrName, amount uint64) { - return bar.TransferFrom -} -func (BarToken) BalanceOf() func(owner pusers.AddressOrName) uint64 { - return bar.BalanceOf -} -func (BarToken) Approve() func(spender pusers.AddressOrName, amount uint64) { - return bar.Approve -} - -type BazToken struct{} - -func (BazToken) Transfer() func(to pusers.AddressOrName, amount uint64) { - return baz.Transfer -} -func (BazToken) TransferFrom() func(from, to pusers.AddressOrName, amount uint64) { - return baz.TransferFrom -} -func (BazToken) BalanceOf() func(owner pusers.AddressOrName) uint64 { - return baz.BalanceOf -} -func (BazToken) Approve() func(spender pusers.AddressOrName, amount uint64) { - return baz.Approve -} - -type QuxToken struct{} - -func (QuxToken) Transfer() func(to pusers.AddressOrName, amount uint64) { - return qux.Transfer -} -func (QuxToken) TransferFrom() func(from, to pusers.AddressOrName, amount uint64) { - return qux.TransferFrom -} -func (QuxToken) BalanceOf() func(owner pusers.AddressOrName) uint64 { - return qux.BalanceOf -} -func (QuxToken) Approve() func(spender pusers.AddressOrName, amount uint64) { - return qux.Approve -} - -type WugnotToken struct{} - -func (WugnotToken) Transfer() func(to pusers.AddressOrName, amount uint64) { - return wugnot.Transfer -} -func (WugnotToken) TransferFrom() func(from, to pusers.AddressOrName, amount uint64) { - return wugnot.TransferFrom -} -func (WugnotToken) BalanceOf() func(owner pusers.AddressOrName) uint64 { - return wugnot.BalanceOf -} -func (WugnotToken) Approve() func(spender pusers.AddressOrName, amount uint64) { - return wugnot.Approve -} - -type OBLToken struct{} - -func (OBLToken) Transfer() func(to pusers.AddressOrName, amount uint64) { - return obl.Transfer -} -func (OBLToken) TransferFrom() func(from, to pusers.AddressOrName, amount uint64) { - return obl.TransferFrom -} -func (OBLToken) BalanceOf() func(owner pusers.AddressOrName) uint64 { - return obl.BalanceOf -} -func (OBLToken) Approve() func(spender pusers.AddressOrName, amount uint64) { - return obl.Approve -} - -type GNSToken struct{} - -func (GNSToken) Transfer() func(to pusers.AddressOrName, amount uint64) { - return gns.Transfer -} - -func (GNSToken) TransferFrom() func(from, to pusers.AddressOrName, amount uint64) { - return gns.TransferFrom -} - -func (GNSToken) BalanceOf() func(owner pusers.AddressOrName) uint64 { - return gns.BalanceOf -} - -func (GNSToken) Approve() func(spender pusers.AddressOrName, amount uint64) { - return gns.Approve -} - -func init() { - std.TestSetRealm(std.NewUserRealm(consts.TOKEN_REGISTER)) - - RegisterGRC20Interface("gno.land/r/onbloc/bar", BarToken{}) - RegisterGRC20Interface("gno.land/r/onbloc/foo", FooToken{}) - RegisterGRC20Interface("gno.land/r/onbloc/baz", BazToken{}) - RegisterGRC20Interface("gno.land/r/onbloc/qux", QuxToken{}) - RegisterGRC20Interface("gno.land/r/demo/wugnot", WugnotToken{}) - RegisterGRC20Interface("gno.land/r/onbloc/obl", OBLToken{}) - RegisterGRC20Interface("gno.land/r/gnoswap/v1/gns", GNSToken{}) -} - -func TestGetRegisteredTokens(t *testing.T) { - uassert.Equal(t, len(GetRegisteredTokens()), 7) -} - -func TestRegisterGRC20Interface(t *testing.T) { - uassert.PanicsWithMessage(t, - `[GNOSWAP-POOL-001] caller has no permission || token_register.gno__RegisterGRC20Interface() || only register(g1er355fkjksqpdtwmhf5penwa82p0rhqxkkyhk5) can register token, called from g1wymu47drhr0kuq2098m792lytgtj2nyx77yrsm`, - func() { - RegisterGRC20Interface("gno.land/r/onbloc/bar", BarToken{}) - }, - ) -} - -func TestUnregisterGRC20Interface(t *testing.T) { - dummy := testutils.TestAddress("dummy") - std.TestSetRealm(std.NewUserRealm(dummy)) - - uassert.PanicsWithMessage(t, - `[GNOSWAP-POOL-001] caller has no permission || token_register.gno__UnregisterGRC20Interface() || unauthorized address(g1v36k6mteta047h6lta047h6lta047h6lz7gmv8) to unregister`, - func() { - UnregisterGRC20Interface("gno.land/r/onbloc/bar") - }, - ) - - uassert.Equal(t, len(GetRegisteredTokens()), 7) - - std.TestSetRealm(std.NewUserRealm(consts.TOKEN_REGISTER)) - UnregisterGRC20Interface("gno.land/r/onbloc/bar") - uassert.Equal(t, len(GetRegisteredTokens()), 6) - - // re-register to avoid panic in other tests - RegisterGRC20Interface("gno.land/r/onbloc/bar", BarToken{}) - - std.TestSetRealm(adminRealm) -} diff --git a/pool/tests/__TEST_0_INIT_VARIABLE_AND_HELPER_test.gnoA b/pool/tests/__TEST_0_INIT_VARIABLE_AND_HELPER_test.gnoA index 4b6ba0436..e92df9720 100644 --- a/pool/tests/__TEST_0_INIT_VARIABLE_AND_HELPER_test.gnoA +++ b/pool/tests/__TEST_0_INIT_VARIABLE_AND_HELPER_test.gnoA @@ -3,6 +3,7 @@ package pool import ( "std" + "gno.land/r/gnoswap/v1/common" "gno.land/r/gnoswap/v1/consts" ) @@ -43,3 +44,8 @@ func ugnotBalanceOf(addr std.Address) uint64 { return uint64(coins.AmountOf("ugnot")) } + +func balanceOfByRegisterCall(tokenPath string, caller std.Address) uint64 { + token := common.GetToken(tokenPath) + return token.BalanceOf(caller) +} diff --git a/pool/utils.gno b/pool/utils.gno index 0ad4f4e63..b8d627a9d 100644 --- a/pool/utils.gno +++ b/pool/utils.gno @@ -34,3 +34,12 @@ func getPrev() (string, string) { prev := std.PrevRealm() return prev.Addr().String(), prev.PkgPath() } + +func checkTransferError(err error) { + if err != nil { + panic(addDetailToError( + errTransferFailed, + err.Error(), + )) + } +} From 0411dd187e007119368d60ef8adfa4009d24eb40 Mon Sep 17 00:00:00 2001 From: n3wbie Date: Mon, 2 Dec 2024 17:16:50 +0900 Subject: [PATCH 06/44] GSW-1838 feat: grc20reg - replace previous token_register to latest grc20reg --- .../grc20reg/approve_and_transferfrom.txtar | 53 +++++++++++++ __local/grc20_tokens/grc20reg/gno.mod | 9 +++ __local/grc20_tokens/grc20reg/grc20reg.gno | 76 +++++++++++++++++++ .../grc20_tokens/grc20reg/grc20reg_test.gno | 59 ++++++++++++++ __local/grc20_tokens/onbloc/bar/bar.gno | 9 ++- __local/grc20_tokens/onbloc/bar/gno.mod | 1 + __local/grc20_tokens/onbloc/baz/baz.gno | 9 ++- __local/grc20_tokens/onbloc/baz/gno.mod | 1 + __local/grc20_tokens/onbloc/foo/foo.gno | 9 ++- __local/grc20_tokens/onbloc/foo/gno.mod | 1 + __local/grc20_tokens/onbloc/obl/gno.mod | 1 + __local/grc20_tokens/onbloc/obl/obl.gno | 9 ++- __local/grc20_tokens/onbloc/qux/gno.mod | 1 + __local/grc20_tokens/onbloc/qux/qux.gno | 9 ++- __local/grc20_tokens/onbloc/usdc/gno.mod | 1 + __local/grc20_tokens/onbloc/usdc/usdc.gno | 9 ++- _deploy/r/gnoswap/gns/gno.mod | 1 + _deploy/r/gnoswap/gns/gns.gno | 8 +- 18 files changed, 252 insertions(+), 14 deletions(-) create mode 100644 __local/grc20_tokens/grc20reg/approve_and_transferfrom.txtar create mode 100644 __local/grc20_tokens/grc20reg/gno.mod create mode 100644 __local/grc20_tokens/grc20reg/grc20reg.gno create mode 100644 __local/grc20_tokens/grc20reg/grc20reg_test.gno diff --git a/__local/grc20_tokens/grc20reg/approve_and_transferfrom.txtar b/__local/grc20_tokens/grc20reg/approve_and_transferfrom.txtar new file mode 100644 index 000000000..03b160b92 --- /dev/null +++ b/__local/grc20_tokens/grc20reg/approve_and_transferfrom.txtar @@ -0,0 +1,53 @@ +loadpkg gno.land/p/demo/users + +loadpkg gno.land/r/demo/foo20 +loadpkg gno.land/r/demo/grc20reg + +loadpkg gno.land/r/demo/echo $WORK/echo + +## start a new node +gnoland start + +## faucet +# gnokey maketx call -pkgpath gno.land/r/demo/foo20 -func Faucet -gas-fee 1ugnot -gas-wanted 4000000 -broadcast -chainid=tendermint_test test1 + +## print reg addr +gnokey maketx call -pkgpath gno.land/r/demo/reg -func RelamAddr -gas-fee 1ugnot -gas-wanted 4000000 -broadcast -chainid=tendermint_test test1 +stdout 'g19tlskvga928es8ug2empargp0teul03apzjud9' + +## approve +gnokey maketx call -pkgpath gno.land/r/demo/foo20 -func Approve -args 'g19tlskvga928es8ug2empargp0teul03apzjud9' -args '100' -gas-fee 1ugnot -gas-wanted 4000000 -broadcast -chainid=tendermint_test test1 + +## transfer from +gnokey maketx call -pkgpath gno.land/r/demo/reg -func TransferFromWithReg -gas-fee 1ugnot -gas-wanted 4000000 -broadcast -chainid=tendermint_test test1 +stdout '' + +-- reg/reg.gno -- +package reg + +import ( + "std" + + "gno.land/r/demo/grc20reg" +) + +func RelamAddr() string { + addr := std.CurrentRealm().Addr().String() + return addr +} + +func TransferFromWithReg() { + caller := std.PrevRealm().Addr() + curr := std.CurrentRealm().Addr() + + + // using import + // foo20.TransferFrom(uCaller, uCurr, uint64(100)) + + // using grc20reg + fooTokenGetter := grc20reg.Get("gno.land/r/demo/foo20") + fooToken := fooTokenGetter() + userTeller := fooToken.CallerTeller() + + userTeller.TransferFrom(caller, curr, uint64(100)) +} \ No newline at end of file diff --git a/__local/grc20_tokens/grc20reg/gno.mod b/__local/grc20_tokens/grc20reg/gno.mod new file mode 100644 index 000000000..450e21bea --- /dev/null +++ b/__local/grc20_tokens/grc20reg/gno.mod @@ -0,0 +1,9 @@ +module gno.land/r/gnoswap/v1/grc20reg + +require ( + gno.land/p/demo/avl v0.0.0-latest + gno.land/p/demo/fqname v0.0.0-latest + gno.land/p/demo/grc/grc20 v0.0.0-latest + gno.land/p/demo/ufmt v0.0.0-latest + gno.land/p/demo/urequire v0.0.0-latest +) \ No newline at end of file diff --git a/__local/grc20_tokens/grc20reg/grc20reg.gno b/__local/grc20_tokens/grc20reg/grc20reg.gno new file mode 100644 index 000000000..ff46ec948 --- /dev/null +++ b/__local/grc20_tokens/grc20reg/grc20reg.gno @@ -0,0 +1,76 @@ +package grc20reg + +import ( + "std" + + "gno.land/p/demo/avl" + "gno.land/p/demo/fqname" + "gno.land/p/demo/grc/grc20" + "gno.land/p/demo/ufmt" +) + +var registry = avl.NewTree() // rlmPath[.slug] -> TokenGetter (slug is optional) + +func Register(tokenGetter grc20.TokenGetter, slug string) { + rlmPath := std.PrevRealm().PkgPath() + key := fqname.Construct(rlmPath, slug) + registry.Set(key, tokenGetter) + std.Emit( + registerEvent, + "pkgpath", rlmPath, + "slug", slug, + ) +} + +func Get(key string) grc20.TokenGetter { + tokenGetter, ok := registry.Get(key) + if !ok { + return nil + } + return tokenGetter.(grc20.TokenGetter) +} + +func MustGet(key string) grc20.TokenGetter { + tokenGetter := Get(key) + if tokenGetter == nil { + panic("unknown token: " + key) + } + return tokenGetter +} + +func Render(path string) string { + switch { + case path == "": // home + // TODO: add pagination + s := "" + count := 0 + registry.Iterate("", "", func(key string, tokenI interface{}) bool { + count++ + tokenGetter := tokenI.(grc20.TokenGetter) + token := tokenGetter() + rlmPath, slug := fqname.Parse(key) + rlmLink := fqname.RenderLink(rlmPath, slug) + infoLink := "/r/demo/grc20reg:" + key + s += ufmt.Sprintf("- **%s** - %s - [info](%s)\n", token.GetName(), rlmLink, infoLink) + return false + }) + if count == 0 { + return "No registered token." + } + return s + default: // specific token + key := path + tokenGetter := MustGet(key) + token := tokenGetter() + rlmPath, slug := fqname.Parse(key) + rlmLink := fqname.RenderLink(rlmPath, slug) + s := ufmt.Sprintf("# %s\n", token.GetName()) + s += ufmt.Sprintf("- symbol: **%s**\n", token.GetSymbol()) + s += ufmt.Sprintf("- realm: %s\n", rlmLink) + s += ufmt.Sprintf("- decimals: %d\n", token.GetDecimals()) + s += ufmt.Sprintf("- total supply: %d\n", token.TotalSupply()) + return s + } +} + +const registerEvent = "register" diff --git a/__local/grc20_tokens/grc20reg/grc20reg_test.gno b/__local/grc20_tokens/grc20reg/grc20reg_test.gno new file mode 100644 index 000000000..c93365ff7 --- /dev/null +++ b/__local/grc20_tokens/grc20reg/grc20reg_test.gno @@ -0,0 +1,59 @@ +package grc20reg + +import ( + "std" + "strings" + "testing" + + "gno.land/p/demo/grc/grc20" + "gno.land/p/demo/urequire" +) + +func TestRegistry(t *testing.T) { + std.TestSetRealm(std.NewCodeRealm("gno.land/r/demo/foo")) + realmAddr := std.CurrentRealm().PkgPath() + token, ledger := grc20.NewToken("TestToken", "TST", 4) + ledger.Mint(std.CurrentRealm().Addr(), 1234567) + tokenGetter := func() *grc20.Token { return token } + // register + Register(tokenGetter, "") + regTokenGetter := Get(realmAddr) + regToken := regTokenGetter() + urequire.True(t, regToken != nil, "expected to find a token") // fixme: use urequire.NotNil + urequire.Equal(t, regToken.GetSymbol(), "TST") + + expected := `- **TestToken** - [gno.land/r/demo/foo](/r/demo/foo) - [info](/r/demo/grc20reg:gno.land/r/demo/foo) +` + got := Render("") + urequire.True(t, strings.Contains(got, expected)) + // 404 + invalidToken := Get("0xdeadbeef") + urequire.True(t, invalidToken == nil) + + // register with a slug + Register(tokenGetter, "mySlug") + regTokenGetter = Get(realmAddr + ".mySlug") + regToken = regTokenGetter() + urequire.True(t, regToken != nil, "expected to find a token") // fixme: use urequire.NotNil + urequire.Equal(t, regToken.GetSymbol(), "TST") + + // override + Register(tokenGetter, "") + regTokenGetter = Get(realmAddr + "") + regToken = regTokenGetter() + urequire.True(t, regToken != nil, "expected to find a token") // fixme: use urequire.NotNil + urequire.Equal(t, regToken.GetSymbol(), "TST") + + got = Render("") + urequire.True(t, strings.Contains(got, `- **TestToken** - [gno.land/r/demo/foo](/r/demo/foo) - [info](/r/demo/grc20reg:gno.land/r/demo/foo)`)) + urequire.True(t, strings.Contains(got, `- **TestToken** - [gno.land/r/demo/foo](/r/demo/foo).mySlug - [info](/r/demo/grc20reg:gno.land/r/demo/foo.mySlug)`)) + + expected = `# TestToken +- symbol: **TST** +- realm: [gno.land/r/demo/foo](/r/demo/foo).mySlug +- decimals: 4 +- total supply: 1234567 +` + got = Render("gno.land/r/demo/foo.mySlug") + urequire.Equal(t, expected, got) +} diff --git a/__local/grc20_tokens/onbloc/bar/bar.gno b/__local/grc20_tokens/onbloc/bar/bar.gno index c759395b3..0be389fd5 100644 --- a/__local/grc20_tokens/onbloc/bar/bar.gno +++ b/__local/grc20_tokens/onbloc/bar/bar.gno @@ -7,19 +7,24 @@ import ( "gno.land/p/demo/ownable" "gno.land/p/demo/ufmt" pusers "gno.land/p/demo/users" + + "gno.land/r/demo/grc20reg" "gno.land/r/demo/users" ) var ( admin *ownable.Ownable - token *grc20.Token + Token *grc20.Token ledger *grc20.PrivateLedger ) func init() { admin = ownable.NewWithAddress("g17290cwvmrapvp869xfnhhawa8sm9edpufzat7d") // ADMIN - token, ledger = grc20.NewToken("Bar", "BAR", 6) + Token, ledger = grc20.NewToken("Bar", "BAR", 6) ledger.Mint(admin.Owner(), 100_000_000_000_000) + + getter := func() *grc20.Token { return Token } + grc20reg.Register(getter, "") } func TotalSupply() uint64 { return token.TotalSupply() } diff --git a/__local/grc20_tokens/onbloc/bar/gno.mod b/__local/grc20_tokens/onbloc/bar/gno.mod index 8ed87d516..4e18b2676 100644 --- a/__local/grc20_tokens/onbloc/bar/gno.mod +++ b/__local/grc20_tokens/onbloc/bar/gno.mod @@ -6,4 +6,5 @@ require ( gno.land/p/demo/ufmt v0.0.0-latest gno.land/p/demo/users v0.0.0-latest gno.land/r/demo/users v0.0.0-latest + gno.land/r/demo/grc20reg v0.0.0-latest ) diff --git a/__local/grc20_tokens/onbloc/baz/baz.gno b/__local/grc20_tokens/onbloc/baz/baz.gno index ad003225c..d40f18a74 100644 --- a/__local/grc20_tokens/onbloc/baz/baz.gno +++ b/__local/grc20_tokens/onbloc/baz/baz.gno @@ -7,19 +7,24 @@ import ( "gno.land/p/demo/ownable" "gno.land/p/demo/ufmt" pusers "gno.land/p/demo/users" + + "gno.land/r/demo/grc20reg" "gno.land/r/demo/users" ) var ( admin *ownable.Ownable - token *grc20.Token + Token *grc20.Token ledger *grc20.PrivateLedger ) func init() { admin = ownable.NewWithAddress("g17290cwvmrapvp869xfnhhawa8sm9edpufzat7d") // ADMIN - token, ledger = grc20.NewToken("Baz", "BAZ", 6) + Token, ledger = grc20.NewToken("Baz", "BAZ", 6) ledger.Mint(admin.Owner(), 100_000_000_000_000) + + getter := func() *grc20.Token { return Token } + grc20reg.Register(getter, "") } func TotalSupply() uint64 { return token.TotalSupply() } diff --git a/__local/grc20_tokens/onbloc/baz/gno.mod b/__local/grc20_tokens/onbloc/baz/gno.mod index 6d1b8cd2c..05230b44d 100644 --- a/__local/grc20_tokens/onbloc/baz/gno.mod +++ b/__local/grc20_tokens/onbloc/baz/gno.mod @@ -6,4 +6,5 @@ require ( gno.land/p/demo/ufmt v0.0.0-latest gno.land/p/demo/users v0.0.0-latest gno.land/r/demo/users v0.0.0-latest + gno.land/r/demo/grc20reg v0.0.0-latest ) diff --git a/__local/grc20_tokens/onbloc/foo/foo.gno b/__local/grc20_tokens/onbloc/foo/foo.gno index 54451f3d4..87dcbe32d 100644 --- a/__local/grc20_tokens/onbloc/foo/foo.gno +++ b/__local/grc20_tokens/onbloc/foo/foo.gno @@ -7,19 +7,24 @@ import ( "gno.land/p/demo/ownable" "gno.land/p/demo/ufmt" pusers "gno.land/p/demo/users" + + "gno.land/r/demo/grc20reg" "gno.land/r/demo/users" ) var ( admin *ownable.Ownable - token *grc20.Token + Token *grc20.Token ledger *grc20.PrivateLedger ) func init() { admin = ownable.NewWithAddress("g17290cwvmrapvp869xfnhhawa8sm9edpufzat7d") // ADMIN - token, ledger = grc20.NewToken("Foo", "FOO", 6) + Token, ledger = grc20.NewToken("Foo", "FOO", 6) ledger.Mint(admin.Owner(), 100_000_000_000_000) + + getter := func() *grc20.Token { return Token } + grc20reg.Register(getter, "") } func TotalSupply() uint64 { return token.TotalSupply() } diff --git a/__local/grc20_tokens/onbloc/foo/gno.mod b/__local/grc20_tokens/onbloc/foo/gno.mod index bdd7de698..42e302af2 100644 --- a/__local/grc20_tokens/onbloc/foo/gno.mod +++ b/__local/grc20_tokens/onbloc/foo/gno.mod @@ -6,4 +6,5 @@ require ( gno.land/p/demo/ufmt v0.0.0-latest gno.land/p/demo/users v0.0.0-latest gno.land/r/demo/users v0.0.0-latest + gno.land/r/demo/grc20reg v0.0.0-latest ) diff --git a/__local/grc20_tokens/onbloc/obl/gno.mod b/__local/grc20_tokens/onbloc/obl/gno.mod index a45fb79bb..ffdeb5df8 100644 --- a/__local/grc20_tokens/onbloc/obl/gno.mod +++ b/__local/grc20_tokens/onbloc/obl/gno.mod @@ -6,4 +6,5 @@ require ( gno.land/p/demo/ufmt v0.0.0-latest gno.land/p/demo/users v0.0.0-latest gno.land/r/demo/users v0.0.0-latest + gno.land/r/demo/grc20reg v0.0.0-latest ) diff --git a/__local/grc20_tokens/onbloc/obl/obl.gno b/__local/grc20_tokens/onbloc/obl/obl.gno index 890debb65..0815952c1 100644 --- a/__local/grc20_tokens/onbloc/obl/obl.gno +++ b/__local/grc20_tokens/onbloc/obl/obl.gno @@ -7,19 +7,24 @@ import ( "gno.land/p/demo/ownable" "gno.land/p/demo/ufmt" pusers "gno.land/p/demo/users" + + "gno.land/r/demo/grc20reg" "gno.land/r/demo/users" ) var ( admin *ownable.Ownable - token *grc20.Token + Token *grc20.Token ledger *grc20.PrivateLedger ) func init() { admin = ownable.NewWithAddress("g17290cwvmrapvp869xfnhhawa8sm9edpufzat7d") // ADMIN - token, ledger = grc20.NewToken("Obl", "OBL", 6) + Token, ledger = grc20.NewToken("Obl", "OBL", 6) ledger.Mint(admin.Owner(), 100_000_000_000_000) + + getter := func() *grc20.Token { return Token } + grc20reg.Register(getter, "") } func TotalSupply() uint64 { return token.TotalSupply() } diff --git a/__local/grc20_tokens/onbloc/qux/gno.mod b/__local/grc20_tokens/onbloc/qux/gno.mod index 8e74cede7..1528a0331 100644 --- a/__local/grc20_tokens/onbloc/qux/gno.mod +++ b/__local/grc20_tokens/onbloc/qux/gno.mod @@ -6,4 +6,5 @@ require ( gno.land/p/demo/ufmt v0.0.0-latest gno.land/p/demo/users v0.0.0-latest gno.land/r/demo/users v0.0.0-latest + gno.land/r/demo/grc20reg v0.0.0-latest ) diff --git a/__local/grc20_tokens/onbloc/qux/qux.gno b/__local/grc20_tokens/onbloc/qux/qux.gno index 49585a9c5..747ce978c 100644 --- a/__local/grc20_tokens/onbloc/qux/qux.gno +++ b/__local/grc20_tokens/onbloc/qux/qux.gno @@ -7,19 +7,24 @@ import ( "gno.land/p/demo/ownable" "gno.land/p/demo/ufmt" pusers "gno.land/p/demo/users" + + "gno.land/r/demo/grc20reg" "gno.land/r/demo/users" ) var ( admin *ownable.Ownable - token *grc20.Token + Token *grc20.Token ledger *grc20.PrivateLedger ) func init() { admin = ownable.NewWithAddress("g17290cwvmrapvp869xfnhhawa8sm9edpufzat7d") // ADMIN - token, ledger = grc20.NewToken("Qux", "QUX", 6) + Token, ledger = grc20.NewToken("Qux", "QUX", 6) ledger.Mint(admin.Owner(), 100_000_000_000_000) + + getter := func() *grc20.Token { return Token } + grc20reg.Register(getter, "") } func TotalSupply() uint64 { return token.TotalSupply() } diff --git a/__local/grc20_tokens/onbloc/usdc/gno.mod b/__local/grc20_tokens/onbloc/usdc/gno.mod index 25836f4de..13d6b5cbe 100644 --- a/__local/grc20_tokens/onbloc/usdc/gno.mod +++ b/__local/grc20_tokens/onbloc/usdc/gno.mod @@ -6,4 +6,5 @@ require ( gno.land/p/demo/ufmt v0.0.0-latest gno.land/p/demo/users v0.0.0-latest gno.land/r/demo/users v0.0.0-latest + gno.land/r/demo/grc20reg v0.0.0-latest ) diff --git a/__local/grc20_tokens/onbloc/usdc/usdc.gno b/__local/grc20_tokens/onbloc/usdc/usdc.gno index cd1db0df2..9477bf0bc 100644 --- a/__local/grc20_tokens/onbloc/usdc/usdc.gno +++ b/__local/grc20_tokens/onbloc/usdc/usdc.gno @@ -7,19 +7,24 @@ import ( "gno.land/p/demo/ownable" "gno.land/p/demo/ufmt" pusers "gno.land/p/demo/users" + + "gno.land/r/demo/grc20reg" "gno.land/r/demo/users" ) var ( admin *ownable.Ownable - token *grc20.Token + Token *grc20.Token ledger *grc20.PrivateLedger ) func init() { admin = ownable.NewWithAddress("g17290cwvmrapvp869xfnhhawa8sm9edpufzat7d") // ADMIN - token, ledger = grc20.NewToken("Usd Coin", "USDC", 6) + Token, ledger = grc20.NewToken("Usd Coin", "USDC", 6) ledger.Mint(admin.Owner(), 100_000_000_000_000) + + getter := func() *grc20.Token { return Token } + grc20reg.Register(getter, "") } func TotalSupply() uint64 { return token.TotalSupply() } diff --git a/_deploy/r/gnoswap/gns/gno.mod b/_deploy/r/gnoswap/gns/gno.mod index 7963234e4..8c541067d 100644 --- a/_deploy/r/gnoswap/gns/gno.mod +++ b/_deploy/r/gnoswap/gns/gno.mod @@ -7,6 +7,7 @@ require ( gno.land/p/demo/ufmt v0.0.0-latest gno.land/p/demo/users v0.0.0-latest gno.land/r/demo/users v0.0.0-latest + gno.land/r/demo/grc20reg v0.0.0-latest gno.land/r/gnoswap/v1/common v0.0.0-latest gno.land/r/gnoswap/v1/consts v0.0.0-latest ) diff --git a/_deploy/r/gnoswap/gns/gns.gno b/_deploy/r/gnoswap/gns/gns.gno index cfe319271..20584c5ef 100644 --- a/_deploy/r/gnoswap/gns/gns.gno +++ b/_deploy/r/gnoswap/gns/gns.gno @@ -9,6 +9,7 @@ import ( "gno.land/p/demo/ufmt" pusers "gno.land/p/demo/users" + "gno.land/r/demo/grc20reg" "gno.land/r/demo/users" "gno.land/r/gnoswap/v1/common" @@ -20,7 +21,7 @@ const MAXIMUM_SUPPLY = uint64(1_000_000_000_000_000) // 1B var ( banker *grc20.Teller admin *ownable.Ownable - token *grc20.Token + Token *grc20.Token ledger *grc20.PrivateLedger ) @@ -31,10 +32,13 @@ var ( func init() { admin = ownable.NewWithAddress("g17290cwvmrapvp869xfnhhawa8sm9edpufzat7d") // ADMIN - token, ledger = grc20.NewToken("Gnoswap", "GNS", 6) + Token, ledger = grc20.NewToken("Gnoswap", "GNS", 6) ledger.Mint(admin.Owner(), 100_000_000_000_000) // 100_000_000 GNS ≈ 0.1B + getter := func() *grc20.Token { return Token } + grc20reg.Register(getter, "") + amountToEmission = MAXIMUM_SUPPLY - uint64(100_000_000_000_000) lastMintedHeight = std.GetHeight() From 2fe48bf4f0f094943b1cea9e32a5a81019234834 Mon Sep 17 00:00:00 2001 From: n3wbie Date: Mon, 2 Dec 2024 18:15:38 +0900 Subject: [PATCH 07/44] feat: update grc20 spec for grc20reg --- __local/grc20_tokens/onbloc/bar/bar.gno | 4 +- __local/grc20_tokens/onbloc/baz/baz.gno | 33 +++++++------- __local/grc20_tokens/onbloc/foo/foo.gno | 33 +++++++------- __local/grc20_tokens/onbloc/obl/obl.gno | 33 +++++++------- __local/grc20_tokens/onbloc/qux/qux.gno | 33 +++++++------- __local/grc20_tokens/onbloc/usdc/usdc.gno | 33 +++++++------- _deploy/r/gnoswap/gns/gns.gno | 53 +++++++++++------------ 7 files changed, 108 insertions(+), 114 deletions(-) diff --git a/__local/grc20_tokens/onbloc/bar/bar.gno b/__local/grc20_tokens/onbloc/bar/bar.gno index 0be389fd5..05f9086b4 100644 --- a/__local/grc20_tokens/onbloc/bar/bar.gno +++ b/__local/grc20_tokens/onbloc/bar/bar.gno @@ -68,11 +68,11 @@ func Render(path string) string { switch { case path == "": - return token.RenderHome() + return Token.RenderHome() case c == 2 && parts[0] == "balance": owner := pusers.AddressOrName(parts[1]) ownerAddr := users.Resolve(owner) - balance := token.BalanceOf(ownerAddr) + balance := UserTeller.BalanceOf(ownerAddr) return ufmt.Sprintf("%d\n", balance) default: return "404\n" diff --git a/__local/grc20_tokens/onbloc/baz/baz.gno b/__local/grc20_tokens/onbloc/baz/baz.gno index d40f18a74..c1dbdca1e 100644 --- a/__local/grc20_tokens/onbloc/baz/baz.gno +++ b/__local/grc20_tokens/onbloc/baz/baz.gno @@ -13,53 +13,52 @@ import ( ) var ( - admin *ownable.Ownable - Token *grc20.Token - ledger *grc20.PrivateLedger + Token, privateLedger = grc20.NewToken("Baz", "BAZ", 6) + UserTeller = Token.CallerTeller() + owner = ownable.NewWithAddress("g17290cwvmrapvp869xfnhhawa8sm9edpufzat7d") // ADMIN ) func init() { - admin = ownable.NewWithAddress("g17290cwvmrapvp869xfnhhawa8sm9edpufzat7d") // ADMIN - Token, ledger = grc20.NewToken("Baz", "BAZ", 6) - ledger.Mint(admin.Owner(), 100_000_000_000_000) - + privateLedger.Mint(owner.Owner(), 100_000_000_000_000) getter := func() *grc20.Token { return Token } grc20reg.Register(getter, "") } -func TotalSupply() uint64 { return token.TotalSupply() } +func TotalSupply() uint64 { + return UserTeller.TotalSupply() +} func BalanceOf(owner pusers.AddressOrName) uint64 { ownerAddr := users.Resolve(owner) - return token.BalanceOf(ownerAddr) + return UserTeller.BalanceOf(ownerAddr) } func Allowance(owner, spender pusers.AddressOrName) uint64 { ownerAddr := users.Resolve(owner) spenderAddr := users.Resolve(spender) - return token.Allowance(ownerAddr, spenderAddr) + return UserTeller.Allowance(ownerAddr, spenderAddr) } func Transfer(to pusers.AddressOrName, amount uint64) { toAddr := users.Resolve(to) - checkErr(token.CallerTeller().Transfer(toAddr, amount)) + checkErr(UserTeller.Transfer(toAddr, amount)) } func Approve(spender pusers.AddressOrName, amount uint64) { spenderAddr := users.Resolve(spender) - checkErr(token.CallerTeller().Approve(spenderAddr, amount)) + checkErr(UserTeller.Approve(spenderAddr, amount)) } func TransferFrom(from, to pusers.AddressOrName, amount uint64) { fromAddr := users.Resolve(from) toAddr := users.Resolve(to) - checkErr(token.CallerTeller().TransferFrom(fromAddr, toAddr, amount)) + checkErr(UserTeller.TransferFrom(fromAddr, toAddr, amount)) } func Burn(from pusers.AddressOrName, amount uint64) { - admin.AssertCallerIsOwner() + owner.AssertCallerIsOwner() fromAddr := users.Resolve(from) - checkErr(ledger.Burn(fromAddr, amount)) + checkErr(privateLedger.Burn(fromAddr, amount)) } func Render(path string) string { @@ -68,11 +67,11 @@ func Render(path string) string { switch { case path == "": - return token.RenderHome() + return Token.RenderHome() case c == 2 && parts[0] == "balance": owner := pusers.AddressOrName(parts[1]) ownerAddr := users.Resolve(owner) - balance := token.BalanceOf(ownerAddr) + balance := UserTeller.BalanceOf(ownerAddr) return ufmt.Sprintf("%d\n", balance) default: return "404\n" diff --git a/__local/grc20_tokens/onbloc/foo/foo.gno b/__local/grc20_tokens/onbloc/foo/foo.gno index 87dcbe32d..e99ee1d95 100644 --- a/__local/grc20_tokens/onbloc/foo/foo.gno +++ b/__local/grc20_tokens/onbloc/foo/foo.gno @@ -13,53 +13,52 @@ import ( ) var ( - admin *ownable.Ownable - Token *grc20.Token - ledger *grc20.PrivateLedger + Token, privateLedger = grc20.NewToken("Baz", "BAZ", 6) + UserTeller = Token.CallerTeller() + owner = ownable.NewWithAddress("g17290cwvmrapvp869xfnhhawa8sm9edpufzat7d") // ADMIN ) func init() { - admin = ownable.NewWithAddress("g17290cwvmrapvp869xfnhhawa8sm9edpufzat7d") // ADMIN - Token, ledger = grc20.NewToken("Foo", "FOO", 6) - ledger.Mint(admin.Owner(), 100_000_000_000_000) - + privateLedger.Mint(owner.Owner(), 100_000_000_000_000) getter := func() *grc20.Token { return Token } grc20reg.Register(getter, "") } -func TotalSupply() uint64 { return token.TotalSupply() } +func TotalSupply() uint64 { + return UserTeller.TotalSupply() +} func BalanceOf(owner pusers.AddressOrName) uint64 { ownerAddr := users.Resolve(owner) - return token.BalanceOf(ownerAddr) + return UserTeller.BalanceOf(ownerAddr) } func Allowance(owner, spender pusers.AddressOrName) uint64 { ownerAddr := users.Resolve(owner) spenderAddr := users.Resolve(spender) - return token.Allowance(ownerAddr, spenderAddr) + return UserTeller.Allowance(ownerAddr, spenderAddr) } func Transfer(to pusers.AddressOrName, amount uint64) { toAddr := users.Resolve(to) - checkErr(token.CallerTeller().Transfer(toAddr, amount)) + checkErr(UserTeller.Transfer(toAddr, amount)) } func Approve(spender pusers.AddressOrName, amount uint64) { spenderAddr := users.Resolve(spender) - checkErr(token.CallerTeller().Approve(spenderAddr, amount)) + checkErr(UserTeller.Approve(spenderAddr, amount)) } func TransferFrom(from, to pusers.AddressOrName, amount uint64) { fromAddr := users.Resolve(from) toAddr := users.Resolve(to) - checkErr(token.CallerTeller().TransferFrom(fromAddr, toAddr, amount)) + checkErr(UserTeller.TransferFrom(fromAddr, toAddr, amount)) } func Burn(from pusers.AddressOrName, amount uint64) { - admin.AssertCallerIsOwner() + owner.AssertCallerIsOwner() fromAddr := users.Resolve(from) - checkErr(ledger.Burn(fromAddr, amount)) + checkErr(privateLedger.Burn(fromAddr, amount)) } func Render(path string) string { @@ -68,11 +67,11 @@ func Render(path string) string { switch { case path == "": - return token.RenderHome() + return Token.RenderHome() case c == 2 && parts[0] == "balance": owner := pusers.AddressOrName(parts[1]) ownerAddr := users.Resolve(owner) - balance := token.BalanceOf(ownerAddr) + balance := UserTeller.BalanceOf(ownerAddr) return ufmt.Sprintf("%d\n", balance) default: return "404\n" diff --git a/__local/grc20_tokens/onbloc/obl/obl.gno b/__local/grc20_tokens/onbloc/obl/obl.gno index 0815952c1..976cc088a 100644 --- a/__local/grc20_tokens/onbloc/obl/obl.gno +++ b/__local/grc20_tokens/onbloc/obl/obl.gno @@ -13,53 +13,52 @@ import ( ) var ( - admin *ownable.Ownable - Token *grc20.Token - ledger *grc20.PrivateLedger + Token, privateLedger = grc20.NewToken("Obl", "OBL", 6) + UserTeller = Token.CallerTeller() + owner = ownable.NewWithAddress("g17290cwvmrapvp869xfnhhawa8sm9edpufzat7d") // ADMIN ) func init() { - admin = ownable.NewWithAddress("g17290cwvmrapvp869xfnhhawa8sm9edpufzat7d") // ADMIN - Token, ledger = grc20.NewToken("Obl", "OBL", 6) - ledger.Mint(admin.Owner(), 100_000_000_000_000) - + privateLedger.Mint(owner.Owner(), 100_000_000_000_000) getter := func() *grc20.Token { return Token } grc20reg.Register(getter, "") } -func TotalSupply() uint64 { return token.TotalSupply() } +func TotalSupply() uint64 { + return UserTeller.TotalSupply() +} func BalanceOf(owner pusers.AddressOrName) uint64 { ownerAddr := users.Resolve(owner) - return token.BalanceOf(ownerAddr) + return UserTeller.BalanceOf(ownerAddr) } func Allowance(owner, spender pusers.AddressOrName) uint64 { ownerAddr := users.Resolve(owner) spenderAddr := users.Resolve(spender) - return token.Allowance(ownerAddr, spenderAddr) + return UserTeller.Allowance(ownerAddr, spenderAddr) } func Transfer(to pusers.AddressOrName, amount uint64) { toAddr := users.Resolve(to) - checkErr(token.CallerTeller().Transfer(toAddr, amount)) + checkErr(UserTeller.Transfer(toAddr, amount)) } func Approve(spender pusers.AddressOrName, amount uint64) { spenderAddr := users.Resolve(spender) - checkErr(token.CallerTeller().Approve(spenderAddr, amount)) + checkErr(UserTeller.Approve(spenderAddr, amount)) } func TransferFrom(from, to pusers.AddressOrName, amount uint64) { fromAddr := users.Resolve(from) toAddr := users.Resolve(to) - checkErr(token.CallerTeller().TransferFrom(fromAddr, toAddr, amount)) + checkErr(UserTeller.TransferFrom(fromAddr, toAddr, amount)) } func Burn(from pusers.AddressOrName, amount uint64) { - admin.AssertCallerIsOwner() + owner.AssertCallerIsOwner() fromAddr := users.Resolve(from) - checkErr(ledger.Burn(fromAddr, amount)) + checkErr(privateLedger.Burn(fromAddr, amount)) } func Render(path string) string { @@ -68,11 +67,11 @@ func Render(path string) string { switch { case path == "": - return token.RenderHome() + return Token.RenderHome() case c == 2 && parts[0] == "balance": owner := pusers.AddressOrName(parts[1]) ownerAddr := users.Resolve(owner) - balance := token.BalanceOf(ownerAddr) + balance := UserTeller.BalanceOf(ownerAddr) return ufmt.Sprintf("%d\n", balance) default: return "404\n" diff --git a/__local/grc20_tokens/onbloc/qux/qux.gno b/__local/grc20_tokens/onbloc/qux/qux.gno index 747ce978c..29aaec1b6 100644 --- a/__local/grc20_tokens/onbloc/qux/qux.gno +++ b/__local/grc20_tokens/onbloc/qux/qux.gno @@ -13,53 +13,52 @@ import ( ) var ( - admin *ownable.Ownable - Token *grc20.Token - ledger *grc20.PrivateLedger + Token, privateLedger = grc20.NewToken("Qux", "QUX", 6) + UserTeller = Token.CallerTeller() + owner = ownable.NewWithAddress("g17290cwvmrapvp869xfnhhawa8sm9edpufzat7d") // ADMIN ) func init() { - admin = ownable.NewWithAddress("g17290cwvmrapvp869xfnhhawa8sm9edpufzat7d") // ADMIN - Token, ledger = grc20.NewToken("Qux", "QUX", 6) - ledger.Mint(admin.Owner(), 100_000_000_000_000) - + privateLedger.Mint(owner.Owner(), 100_000_000_000_000) getter := func() *grc20.Token { return Token } grc20reg.Register(getter, "") } -func TotalSupply() uint64 { return token.TotalSupply() } +func TotalSupply() uint64 { + return UserTeller.TotalSupply() +} func BalanceOf(owner pusers.AddressOrName) uint64 { ownerAddr := users.Resolve(owner) - return token.BalanceOf(ownerAddr) + return UserTeller.BalanceOf(ownerAddr) } func Allowance(owner, spender pusers.AddressOrName) uint64 { ownerAddr := users.Resolve(owner) spenderAddr := users.Resolve(spender) - return token.Allowance(ownerAddr, spenderAddr) + return UserTeller.Allowance(ownerAddr, spenderAddr) } func Transfer(to pusers.AddressOrName, amount uint64) { toAddr := users.Resolve(to) - checkErr(token.CallerTeller().Transfer(toAddr, amount)) + checkErr(UserTeller.Transfer(toAddr, amount)) } func Approve(spender pusers.AddressOrName, amount uint64) { spenderAddr := users.Resolve(spender) - checkErr(token.CallerTeller().Approve(spenderAddr, amount)) + checkErr(UserTeller.Approve(spenderAddr, amount)) } func TransferFrom(from, to pusers.AddressOrName, amount uint64) { fromAddr := users.Resolve(from) toAddr := users.Resolve(to) - checkErr(token.CallerTeller().TransferFrom(fromAddr, toAddr, amount)) + checkErr(UserTeller.TransferFrom(fromAddr, toAddr, amount)) } func Burn(from pusers.AddressOrName, amount uint64) { - admin.AssertCallerIsOwner() + owner.AssertCallerIsOwner() fromAddr := users.Resolve(from) - checkErr(ledger.Burn(fromAddr, amount)) + checkErr(privateLedger.Burn(fromAddr, amount)) } func Render(path string) string { @@ -68,11 +67,11 @@ func Render(path string) string { switch { case path == "": - return token.RenderHome() + return Token.RenderHome() case c == 2 && parts[0] == "balance": owner := pusers.AddressOrName(parts[1]) ownerAddr := users.Resolve(owner) - balance := token.BalanceOf(ownerAddr) + balance := UserTeller.BalanceOf(ownerAddr) return ufmt.Sprintf("%d\n", balance) default: return "404\n" diff --git a/__local/grc20_tokens/onbloc/usdc/usdc.gno b/__local/grc20_tokens/onbloc/usdc/usdc.gno index 9477bf0bc..c0469bd1f 100644 --- a/__local/grc20_tokens/onbloc/usdc/usdc.gno +++ b/__local/grc20_tokens/onbloc/usdc/usdc.gno @@ -13,53 +13,52 @@ import ( ) var ( - admin *ownable.Ownable - Token *grc20.Token - ledger *grc20.PrivateLedger + Token, privateLedger = grc20.NewToken("Usd Coin", "USDC", 6) + UserTeller = Token.CallerTeller() + owner = ownable.NewWithAddress("g17290cwvmrapvp869xfnhhawa8sm9edpufzat7d") // ADMIN ) func init() { - admin = ownable.NewWithAddress("g17290cwvmrapvp869xfnhhawa8sm9edpufzat7d") // ADMIN - Token, ledger = grc20.NewToken("Usd Coin", "USDC", 6) - ledger.Mint(admin.Owner(), 100_000_000_000_000) - + privateLedger.Mint(owner.Owner(), 100_000_000_000_000) getter := func() *grc20.Token { return Token } grc20reg.Register(getter, "") } -func TotalSupply() uint64 { return token.TotalSupply() } +func TotalSupply() uint64 { + return UserTeller.TotalSupply() +} func BalanceOf(owner pusers.AddressOrName) uint64 { ownerAddr := users.Resolve(owner) - return token.BalanceOf(ownerAddr) + return UserTeller.BalanceOf(ownerAddr) } func Allowance(owner, spender pusers.AddressOrName) uint64 { ownerAddr := users.Resolve(owner) spenderAddr := users.Resolve(spender) - return token.Allowance(ownerAddr, spenderAddr) + return UserTeller.Allowance(ownerAddr, spenderAddr) } func Transfer(to pusers.AddressOrName, amount uint64) { toAddr := users.Resolve(to) - checkErr(token.CallerTeller().Transfer(toAddr, amount)) + checkErr(UserTeller.Transfer(toAddr, amount)) } func Approve(spender pusers.AddressOrName, amount uint64) { spenderAddr := users.Resolve(spender) - checkErr(token.CallerTeller().Approve(spenderAddr, amount)) + checkErr(UserTeller.Approve(spenderAddr, amount)) } func TransferFrom(from, to pusers.AddressOrName, amount uint64) { fromAddr := users.Resolve(from) toAddr := users.Resolve(to) - checkErr(token.CallerTeller().TransferFrom(fromAddr, toAddr, amount)) + checkErr(UserTeller.TransferFrom(fromAddr, toAddr, amount)) } func Burn(from pusers.AddressOrName, amount uint64) { - admin.AssertCallerIsOwner() + owner.AssertCallerIsOwner() fromAddr := users.Resolve(from) - checkErr(ledger.Burn(fromAddr, amount)) + checkErr(privateLedger.Burn(fromAddr, amount)) } func Render(path string) string { @@ -68,11 +67,11 @@ func Render(path string) string { switch { case path == "": - return token.RenderHome() + return Token.RenderHome() case c == 2 && parts[0] == "balance": owner := pusers.AddressOrName(parts[1]) ownerAddr := users.Resolve(owner) - balance := token.BalanceOf(ownerAddr) + balance := UserTeller.BalanceOf(ownerAddr) return ufmt.Sprintf("%d\n", balance) default: return "404\n" diff --git a/_deploy/r/gnoswap/gns/gns.gno b/_deploy/r/gnoswap/gns/gns.gno index 20584c5ef..327ffb448 100644 --- a/_deploy/r/gnoswap/gns/gns.gno +++ b/_deploy/r/gnoswap/gns/gns.gno @@ -19,23 +19,18 @@ import ( const MAXIMUM_SUPPLY = uint64(1_000_000_000_000_000) // 1B var ( - banker *grc20.Teller - admin *ownable.Ownable - Token *grc20.Token - ledger *grc20.PrivateLedger + lastMintedHeight int64 + amountToEmission uint64 ) var ( - lastMintedHeight int64 - amountToEmission uint64 + Token, privateLedger = grc20.NewToken("Gnoswap", "GNS", 6) + UserTeller = Token.CallerTeller() + owner = ownable.NewWithAddress("g17290cwvmrapvp869xfnhhawa8sm9edpufzat7d") // ADMIN ) func init() { - admin = ownable.NewWithAddress("g17290cwvmrapvp869xfnhhawa8sm9edpufzat7d") // ADMIN - Token, ledger = grc20.NewToken("Gnoswap", "GNS", 6) - - ledger.Mint(admin.Owner(), 100_000_000_000_000) // 100_000_000 GNS ≈ 0.1B - + privateLedger.Mint(owner.Owner(), 100_000_000_000_000) // 100_000_000 GNS ≈ 0.1B getter := func() *grc20.Token { return Token } grc20reg.Register(getter, "") @@ -46,41 +41,39 @@ func init() { func GetAmountToEmission() uint64 { return amountToEmission } -func TotalSupply() uint64 { return token.TotalSupply() } +func TotalSupply() uint64 { + return UserTeller.TotalSupply() +} -func TotalMinted() uint64 { return token.TotalSupply() - uint64(100_000_000_000_000) } +func TotalMinted() uint64 { + return UserTeller.TotalSupply() - uint64(100_000_000_000_000) +} func BalanceOf(owner pusers.AddressOrName) uint64 { ownerAddr := users.Resolve(owner) - return token.BalanceOf(ownerAddr) + return UserTeller.BalanceOf(ownerAddr) } func Allowance(owner, spender pusers.AddressOrName) uint64 { ownerAddr := users.Resolve(owner) spenderAddr := users.Resolve(spender) - return token.Allowance(ownerAddr, spenderAddr) + return UserTeller.Allowance(ownerAddr, spenderAddr) } func Transfer(to pusers.AddressOrName, amount uint64) { - common.IsHalted() - toAddr := users.Resolve(to) - checkErr(token.CallerTeller().Transfer(toAddr, amount)) + checkErr(UserTeller.Transfer(toAddr, amount)) } func Approve(spender pusers.AddressOrName, amount uint64) { - common.IsHalted() - spenderAddr := users.Resolve(spender) - checkErr(token.CallerTeller().Approve(spenderAddr, amount)) + checkErr(UserTeller.Approve(spenderAddr, amount)) } func TransferFrom(from, to pusers.AddressOrName, amount uint64) { - common.IsHalted() - fromAddr := users.Resolve(from) toAddr := users.Resolve(to) - checkErr(token.CallerTeller().TransferFrom(fromAddr, toAddr, amount)) + checkErr(UserTeller.TransferFrom(fromAddr, toAddr, amount)) } func Render(path string) string { @@ -89,11 +82,11 @@ func Render(path string) string { switch { case path == "": - return token.RenderHome() + return Token.RenderHome() case c == 2 && parts[0] == "balance": owner := pusers.AddressOrName(parts[1]) ownerAddr := users.Resolve(owner) - balance := token.BalanceOf(ownerAddr) + balance := UserTeller.BalanceOf(ownerAddr) return ufmt.Sprintf("%d\n", balance) default: return "404\n" @@ -150,7 +143,7 @@ func Mint(address pusers.AddressOrName) uint64 { } } - err := ledger.Mint(users.Resolve(address), amountToMint) + err := privateLedger.Mint(users.Resolve(address), amountToMint) if err != nil { panic(err.Error()) } @@ -160,6 +153,12 @@ func Mint(address pusers.AddressOrName) uint64 { return amountToMint } +func Burn(from pusers.AddressOrName, amount uint64) { + owner.AssertCallerIsOwner() + fromAddr := users.Resolve(from) + checkErr(privateLedger.Burn(fromAddr, amount)) +} + func checkAndHandleIfLastBlockOfHalvingYear(height int64, amount uint64) uint64 { year := GetHalvingYearByHeight(height) lastBlock := halvingYearBlock[year] From a5f0a56a6a4d35c65210e327c555aa3593dac905 Mon Sep 17 00:00:00 2001 From: n3wbie Date: Mon, 2 Dec 2024 18:25:37 +0900 Subject: [PATCH 08/44] hotfix: update bar token spec --- __local/grc20_tokens/onbloc/bar/bar.gno | 29 ++++++++++++------------- 1 file changed, 14 insertions(+), 15 deletions(-) diff --git a/__local/grc20_tokens/onbloc/bar/bar.gno b/__local/grc20_tokens/onbloc/bar/bar.gno index 05f9086b4..d3d998e8a 100644 --- a/__local/grc20_tokens/onbloc/bar/bar.gno +++ b/__local/grc20_tokens/onbloc/bar/bar.gno @@ -13,53 +13,52 @@ import ( ) var ( - admin *ownable.Ownable - Token *grc20.Token - ledger *grc20.PrivateLedger + Token, privateLedger = grc20.NewToken("Bar", "BAR", 6) + UserTeller = Token.CallerTeller() + owner = ownable.NewWithAddress("g17290cwvmrapvp869xfnhhawa8sm9edpufzat7d") // ADMIN ) func init() { - admin = ownable.NewWithAddress("g17290cwvmrapvp869xfnhhawa8sm9edpufzat7d") // ADMIN - Token, ledger = grc20.NewToken("Bar", "BAR", 6) - ledger.Mint(admin.Owner(), 100_000_000_000_000) - + privateLedger.Mint(owner.Owner(), 100_000_000_000_000) getter := func() *grc20.Token { return Token } grc20reg.Register(getter, "") } -func TotalSupply() uint64 { return token.TotalSupply() } +func TotalSupply() uint64 { + return UserTeller.TotalSupply() +} func BalanceOf(owner pusers.AddressOrName) uint64 { ownerAddr := users.Resolve(owner) - return token.BalanceOf(ownerAddr) + return UserTeller.BalanceOf(ownerAddr) } func Allowance(owner, spender pusers.AddressOrName) uint64 { ownerAddr := users.Resolve(owner) spenderAddr := users.Resolve(spender) - return token.Allowance(ownerAddr, spenderAddr) + return UserTeller.Allowance(ownerAddr, spenderAddr) } func Transfer(to pusers.AddressOrName, amount uint64) { toAddr := users.Resolve(to) - checkErr(token.CallerTeller().Transfer(toAddr, amount)) + checkErr(UserTeller.Transfer(toAddr, amount)) } func Approve(spender pusers.AddressOrName, amount uint64) { spenderAddr := users.Resolve(spender) - checkErr(token.CallerTeller().Approve(spenderAddr, amount)) + checkErr(UserTeller.Approve(spenderAddr, amount)) } func TransferFrom(from, to pusers.AddressOrName, amount uint64) { fromAddr := users.Resolve(from) toAddr := users.Resolve(to) - checkErr(token.CallerTeller().TransferFrom(fromAddr, toAddr, amount)) + checkErr(UserTeller.TransferFrom(fromAddr, toAddr, amount)) } func Burn(from pusers.AddressOrName, amount uint64) { - admin.AssertCallerIsOwner() + owner.AssertCallerIsOwner() fromAddr := users.Resolve(from) - checkErr(ledger.Burn(fromAddr, amount)) + checkErr(privateLedger.Burn(fromAddr, amount)) } func Render(path string) string { From 497b6580a64369bdeaedcbd693b417ced602fea4 Mon Sep 17 00:00:00 2001 From: n3wbie Date: Mon, 2 Dec 2024 18:59:57 +0900 Subject: [PATCH 09/44] feat: get token object from `grc20-reg` in `common` --- _deploy/r/gnoswap/common/grc20reg_helper.gno | 12 +++++++++ .../r/gnoswap/common/grc20reg_helper_test.gno | 25 +++++++++++++++++++ 2 files changed, 37 insertions(+) create mode 100644 _deploy/r/gnoswap/common/grc20reg_helper.gno create mode 100644 _deploy/r/gnoswap/common/grc20reg_helper_test.gno diff --git a/_deploy/r/gnoswap/common/grc20reg_helper.gno b/_deploy/r/gnoswap/common/grc20reg_helper.gno new file mode 100644 index 000000000..f61c551ef --- /dev/null +++ b/_deploy/r/gnoswap/common/grc20reg_helper.gno @@ -0,0 +1,12 @@ +package common + +import ( + "gno.land/p/demo/grc/grc20" + "gno.land/r/demo/grc20reg" +) + +func GetToken(path string) grc20.Teller { + tokenGetter := grc20reg.MustGet(path) // if token is not registered, it will panic + token := tokenGetter() + return token.CallerTeller() +} diff --git a/_deploy/r/gnoswap/common/grc20reg_helper_test.gno b/_deploy/r/gnoswap/common/grc20reg_helper_test.gno new file mode 100644 index 000000000..46687a59d --- /dev/null +++ b/_deploy/r/gnoswap/common/grc20reg_helper_test.gno @@ -0,0 +1,25 @@ +package common + +import ( + "testing" + + _ "gno.land/r/demo/wugnot" +) + +func TestGetToken(t *testing.T) { + t.Run("registered(by default)", func(t *testing.T) { + token := GetToken("gno.land/r/demo/wugnot") + if token == nil { + t.Error("Expected non-nil teller for WUGNOT") + } + }) + + t.Run("not registered", func(t *testing.T) { + defer func() { + if r := recover(); r == nil { + t.Errorf("Expected panic for non-registered token") + } + }() + GetToken("not_registered") + }) +} From 735ac70a7f0460598e8906795ddf6e655d008b4c Mon Sep 17 00:00:00 2001 From: n3wbie Date: Thu, 5 Dec 2024 10:58:00 +0900 Subject: [PATCH 10/44] fix: typo --- __local/grc20_tokens/grc20reg/approve_and_transferfrom.txtar | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/__local/grc20_tokens/grc20reg/approve_and_transferfrom.txtar b/__local/grc20_tokens/grc20reg/approve_and_transferfrom.txtar index 03b160b92..ff43ac448 100644 --- a/__local/grc20_tokens/grc20reg/approve_and_transferfrom.txtar +++ b/__local/grc20_tokens/grc20reg/approve_and_transferfrom.txtar @@ -3,7 +3,7 @@ loadpkg gno.land/p/demo/users loadpkg gno.land/r/demo/foo20 loadpkg gno.land/r/demo/grc20reg -loadpkg gno.land/r/demo/echo $WORK/echo +loadpkg gno.land/r/demo/reg $WORK/reg ## start a new node gnoland start From 52caa14e0783803ef725624dfc635acf46491ea1 Mon Sep 17 00:00:00 2001 From: n3wbie Date: Fri, 6 Dec 2024 10:32:16 +0900 Subject: [PATCH 11/44] chore: remove grc20reg - it's been merged into master of gnolang/gno --- .../grc20reg/approve_and_transferfrom.txtar | 53 ------------- __local/grc20_tokens/grc20reg/gno.mod | 9 --- __local/grc20_tokens/grc20reg/grc20reg.gno | 76 ------------------- .../grc20_tokens/grc20reg/grc20reg_test.gno | 59 -------------- 4 files changed, 197 deletions(-) delete mode 100644 __local/grc20_tokens/grc20reg/approve_and_transferfrom.txtar delete mode 100644 __local/grc20_tokens/grc20reg/gno.mod delete mode 100644 __local/grc20_tokens/grc20reg/grc20reg.gno delete mode 100644 __local/grc20_tokens/grc20reg/grc20reg_test.gno diff --git a/__local/grc20_tokens/grc20reg/approve_and_transferfrom.txtar b/__local/grc20_tokens/grc20reg/approve_and_transferfrom.txtar deleted file mode 100644 index ff43ac448..000000000 --- a/__local/grc20_tokens/grc20reg/approve_and_transferfrom.txtar +++ /dev/null @@ -1,53 +0,0 @@ -loadpkg gno.land/p/demo/users - -loadpkg gno.land/r/demo/foo20 -loadpkg gno.land/r/demo/grc20reg - -loadpkg gno.land/r/demo/reg $WORK/reg - -## start a new node -gnoland start - -## faucet -# gnokey maketx call -pkgpath gno.land/r/demo/foo20 -func Faucet -gas-fee 1ugnot -gas-wanted 4000000 -broadcast -chainid=tendermint_test test1 - -## print reg addr -gnokey maketx call -pkgpath gno.land/r/demo/reg -func RelamAddr -gas-fee 1ugnot -gas-wanted 4000000 -broadcast -chainid=tendermint_test test1 -stdout 'g19tlskvga928es8ug2empargp0teul03apzjud9' - -## approve -gnokey maketx call -pkgpath gno.land/r/demo/foo20 -func Approve -args 'g19tlskvga928es8ug2empargp0teul03apzjud9' -args '100' -gas-fee 1ugnot -gas-wanted 4000000 -broadcast -chainid=tendermint_test test1 - -## transfer from -gnokey maketx call -pkgpath gno.land/r/demo/reg -func TransferFromWithReg -gas-fee 1ugnot -gas-wanted 4000000 -broadcast -chainid=tendermint_test test1 -stdout '' - --- reg/reg.gno -- -package reg - -import ( - "std" - - "gno.land/r/demo/grc20reg" -) - -func RelamAddr() string { - addr := std.CurrentRealm().Addr().String() - return addr -} - -func TransferFromWithReg() { - caller := std.PrevRealm().Addr() - curr := std.CurrentRealm().Addr() - - - // using import - // foo20.TransferFrom(uCaller, uCurr, uint64(100)) - - // using grc20reg - fooTokenGetter := grc20reg.Get("gno.land/r/demo/foo20") - fooToken := fooTokenGetter() - userTeller := fooToken.CallerTeller() - - userTeller.TransferFrom(caller, curr, uint64(100)) -} \ No newline at end of file diff --git a/__local/grc20_tokens/grc20reg/gno.mod b/__local/grc20_tokens/grc20reg/gno.mod deleted file mode 100644 index 450e21bea..000000000 --- a/__local/grc20_tokens/grc20reg/gno.mod +++ /dev/null @@ -1,9 +0,0 @@ -module gno.land/r/gnoswap/v1/grc20reg - -require ( - gno.land/p/demo/avl v0.0.0-latest - gno.land/p/demo/fqname v0.0.0-latest - gno.land/p/demo/grc/grc20 v0.0.0-latest - gno.land/p/demo/ufmt v0.0.0-latest - gno.land/p/demo/urequire v0.0.0-latest -) \ No newline at end of file diff --git a/__local/grc20_tokens/grc20reg/grc20reg.gno b/__local/grc20_tokens/grc20reg/grc20reg.gno deleted file mode 100644 index ff46ec948..000000000 --- a/__local/grc20_tokens/grc20reg/grc20reg.gno +++ /dev/null @@ -1,76 +0,0 @@ -package grc20reg - -import ( - "std" - - "gno.land/p/demo/avl" - "gno.land/p/demo/fqname" - "gno.land/p/demo/grc/grc20" - "gno.land/p/demo/ufmt" -) - -var registry = avl.NewTree() // rlmPath[.slug] -> TokenGetter (slug is optional) - -func Register(tokenGetter grc20.TokenGetter, slug string) { - rlmPath := std.PrevRealm().PkgPath() - key := fqname.Construct(rlmPath, slug) - registry.Set(key, tokenGetter) - std.Emit( - registerEvent, - "pkgpath", rlmPath, - "slug", slug, - ) -} - -func Get(key string) grc20.TokenGetter { - tokenGetter, ok := registry.Get(key) - if !ok { - return nil - } - return tokenGetter.(grc20.TokenGetter) -} - -func MustGet(key string) grc20.TokenGetter { - tokenGetter := Get(key) - if tokenGetter == nil { - panic("unknown token: " + key) - } - return tokenGetter -} - -func Render(path string) string { - switch { - case path == "": // home - // TODO: add pagination - s := "" - count := 0 - registry.Iterate("", "", func(key string, tokenI interface{}) bool { - count++ - tokenGetter := tokenI.(grc20.TokenGetter) - token := tokenGetter() - rlmPath, slug := fqname.Parse(key) - rlmLink := fqname.RenderLink(rlmPath, slug) - infoLink := "/r/demo/grc20reg:" + key - s += ufmt.Sprintf("- **%s** - %s - [info](%s)\n", token.GetName(), rlmLink, infoLink) - return false - }) - if count == 0 { - return "No registered token." - } - return s - default: // specific token - key := path - tokenGetter := MustGet(key) - token := tokenGetter() - rlmPath, slug := fqname.Parse(key) - rlmLink := fqname.RenderLink(rlmPath, slug) - s := ufmt.Sprintf("# %s\n", token.GetName()) - s += ufmt.Sprintf("- symbol: **%s**\n", token.GetSymbol()) - s += ufmt.Sprintf("- realm: %s\n", rlmLink) - s += ufmt.Sprintf("- decimals: %d\n", token.GetDecimals()) - s += ufmt.Sprintf("- total supply: %d\n", token.TotalSupply()) - return s - } -} - -const registerEvent = "register" diff --git a/__local/grc20_tokens/grc20reg/grc20reg_test.gno b/__local/grc20_tokens/grc20reg/grc20reg_test.gno deleted file mode 100644 index c93365ff7..000000000 --- a/__local/grc20_tokens/grc20reg/grc20reg_test.gno +++ /dev/null @@ -1,59 +0,0 @@ -package grc20reg - -import ( - "std" - "strings" - "testing" - - "gno.land/p/demo/grc/grc20" - "gno.land/p/demo/urequire" -) - -func TestRegistry(t *testing.T) { - std.TestSetRealm(std.NewCodeRealm("gno.land/r/demo/foo")) - realmAddr := std.CurrentRealm().PkgPath() - token, ledger := grc20.NewToken("TestToken", "TST", 4) - ledger.Mint(std.CurrentRealm().Addr(), 1234567) - tokenGetter := func() *grc20.Token { return token } - // register - Register(tokenGetter, "") - regTokenGetter := Get(realmAddr) - regToken := regTokenGetter() - urequire.True(t, regToken != nil, "expected to find a token") // fixme: use urequire.NotNil - urequire.Equal(t, regToken.GetSymbol(), "TST") - - expected := `- **TestToken** - [gno.land/r/demo/foo](/r/demo/foo) - [info](/r/demo/grc20reg:gno.land/r/demo/foo) -` - got := Render("") - urequire.True(t, strings.Contains(got, expected)) - // 404 - invalidToken := Get("0xdeadbeef") - urequire.True(t, invalidToken == nil) - - // register with a slug - Register(tokenGetter, "mySlug") - regTokenGetter = Get(realmAddr + ".mySlug") - regToken = regTokenGetter() - urequire.True(t, regToken != nil, "expected to find a token") // fixme: use urequire.NotNil - urequire.Equal(t, regToken.GetSymbol(), "TST") - - // override - Register(tokenGetter, "") - regTokenGetter = Get(realmAddr + "") - regToken = regTokenGetter() - urequire.True(t, regToken != nil, "expected to find a token") // fixme: use urequire.NotNil - urequire.Equal(t, regToken.GetSymbol(), "TST") - - got = Render("") - urequire.True(t, strings.Contains(got, `- **TestToken** - [gno.land/r/demo/foo](/r/demo/foo) - [info](/r/demo/grc20reg:gno.land/r/demo/foo)`)) - urequire.True(t, strings.Contains(got, `- **TestToken** - [gno.land/r/demo/foo](/r/demo/foo).mySlug - [info](/r/demo/grc20reg:gno.land/r/demo/foo.mySlug)`)) - - expected = `# TestToken -- symbol: **TST** -- realm: [gno.land/r/demo/foo](/r/demo/foo).mySlug -- decimals: 4 -- total supply: 1234567 -` - got = Render("gno.land/r/demo/foo.mySlug") - urequire.Equal(t, expected, got) -} From 309a4b226cc271221a32002511708f0bd602a9dc Mon Sep 17 00:00:00 2001 From: n3wbie Date: Fri, 6 Dec 2024 11:19:07 +0900 Subject: [PATCH 12/44] feat: get `token` and `teller` object from grc20reg --- _deploy/r/gnoswap/common/grc20reg_helper.gno | 26 ++++- .../r/gnoswap/common/grc20reg_helper_test.gno | 104 +++++++++++++++++- 2 files changed, 127 insertions(+), 3 deletions(-) diff --git a/_deploy/r/gnoswap/common/grc20reg_helper.gno b/_deploy/r/gnoswap/common/grc20reg_helper.gno index f61c551ef..e8c17e3ab 100644 --- a/_deploy/r/gnoswap/common/grc20reg_helper.gno +++ b/_deploy/r/gnoswap/common/grc20reg_helper.gno @@ -2,10 +2,34 @@ package common import ( "gno.land/p/demo/grc/grc20" + "gno.land/r/demo/grc20reg" ) -func GetToken(path string) grc20.Teller { +// GetToken returns a grc20.Token instance +// if token is not registered, it will panic +// token instance supports following methods: +// - GetName +// - GetSymbol +// - GetDecimals +// - TotalSupply +// - KnownAccounts +// - BalanceOf +// - Allowance +// - RenderHome +func GetToken(path string) *grc20.Token { + tokenGetter := grc20reg.MustGet(path) // if token is not registered, it will panic + + return tokenGetter() +} + +// GetTokenTeller returns a grc20.Teller instance +// if token is not registered, it will panic +// teller instance supports following methods: +// - Transfer +// - Approve +// - TransferFrom +func GetTokenTeller(path string) grc20.Teller { tokenGetter := grc20reg.MustGet(path) // if token is not registered, it will panic token := tokenGetter() return token.CallerTeller() diff --git a/_deploy/r/gnoswap/common/grc20reg_helper_test.gno b/_deploy/r/gnoswap/common/grc20reg_helper_test.gno index 46687a59d..e5aba65bd 100644 --- a/_deploy/r/gnoswap/common/grc20reg_helper_test.gno +++ b/_deploy/r/gnoswap/common/grc20reg_helper_test.gno @@ -1,14 +1,19 @@ package common import ( + "std" "testing" - _ "gno.land/r/demo/wugnot" + "gno.land/p/demo/testutils" + "gno.land/p/demo/uassert" + "gno.land/p/demo/ufmt" + + _ "gno.land/r/demo/foo20" ) func TestGetToken(t *testing.T) { t.Run("registered(by default)", func(t *testing.T) { - token := GetToken("gno.land/r/demo/wugnot") + token := GetToken("gno.land/r/demo/foo20") if token == nil { t.Error("Expected non-nil teller for WUGNOT") } @@ -23,3 +28,98 @@ func TestGetToken(t *testing.T) { GetToken("not_registered") }) } + +func TestTokenMethod(t *testing.T) { + token := GetToken("gno.land/r/demo/foo20") + + t.Run("GetName()", func(t *testing.T) { + uassert.Equal(t, "Foo", token.GetName()) + }) + + t.Run("GetSymbol()", func(t *testing.T) { + uassert.Equal(t, "FOO", token.GetSymbol()) + }) + + t.Run("GetDecimals()", func(t *testing.T) { + uassert.Equal(t, uint(4), token.GetDecimals()) + }) + + t.Run("TotalSupply()", func(t *testing.T) { + uassert.Equal(t, uint64(10000000000), token.TotalSupply()) + }) + + t.Run("KnownAccounts()", func(t *testing.T) { + uassert.Equal(t, int(1), token.KnownAccounts()) + }) + + t.Run("BalanceOf()", func(t *testing.T) { + uassert.Equal(t, uint64(10000000000), token.BalanceOf(std.Address("g1manfred47kzduec920z88wfr64ylksmdcedlf5"))) + }) + + t.Run("Allowance()", func(t *testing.T) { + uassert.Equal(t, uint64(0), token.Allowance(std.Address("g1manfred47kzduec920z88wfr64ylksmdcedlf5"), std.Address("g1pf6dv9fjk3rn0m4jjcne306ga4he3mzmupfjl6"))) + }) + + t.Run("RenderHome()", func(t *testing.T) { + expected := "" + expected += ufmt.Sprintf("# %s ($%s)\n\n", "Foo", "FOO") + expected += ufmt.Sprintf("* **Decimals**: %d\n", 4) + expected += ufmt.Sprintf("* **Total supply**: %d\n", 10000000000) + expected += ufmt.Sprintf("* **Known accounts**: %d\n", 1) + uassert.Equal(t, expected, token.RenderHome()) + }) +} + +func TestGetTokenTeller(t *testing.T) { + t.Run("registered(by default)", func(t *testing.T) { + teller := GetTokenTeller("gno.land/r/demo/foo20") + if teller == nil { + t.Error("Expected non-nil teller for WUGNOT") + } + }) + + t.Run("not registered", func(t *testing.T) { + defer func() { + if r := recover(); r == nil { + t.Errorf("Expected panic for non-registered token") + } + }() + GetTokenTeller("not_registered") + }) +} + +func TestTellerMethod(t *testing.T) { + teller := GetTokenTeller("gno.land/r/demo/foo20") + token := GetToken("gno.land/r/demo/foo20") + defaultHolder := std.Address("g1manfred47kzduec920z88wfr64ylksmdcedlf5") + addr01 := testutils.TestAddress("addr01") + + t.Run("Transfer()", func(t *testing.T) { + std.TestSetRealm(std.NewUserRealm(defaultHolder)) + + uassert.Equal(t, uint64(10000000000), token.BalanceOf(defaultHolder)) + + uassert.NoError(t, teller.Transfer(addr01, uint64(10000000000))) // transfer all balance to addr01 + + uassert.Equal(t, uint64(0), token.BalanceOf(defaultHolder)) + uassert.Equal(t, uint64(10000000000), token.BalanceOf(addr01)) + + uassert.Error(t, teller.Transfer(addr01, uint64(10000000000))) // not enough balance + }) + + t.Run("Approve()", func(t *testing.T) { + std.TestSetRealm(std.NewUserRealm(addr01)) + uassert.NoError(t, teller.Approve(defaultHolder, uint64(500))) + uassert.Equal(t, uint64(500), token.Allowance(addr01, defaultHolder)) + }) + + t.Run("TransferFrom()", func(t *testing.T) { + std.TestSetRealm(std.NewUserRealm(defaultHolder)) + uassert.NoError(t, teller.TransferFrom(addr01, defaultHolder, uint64(500))) + uassert.Equal(t, uint64(9999999500), token.BalanceOf(addr01)) + uassert.Equal(t, uint64(500), token.BalanceOf(defaultHolder)) + uassert.Equal(t, uint64(0), token.Allowance(addr01, defaultHolder)) + + uassert.Error(t, teller.TransferFrom(addr01, defaultHolder, uint64(500))) // not enough allowance + }) +} From 2244fb43fd94a5c0dc56ce5376b0d137e66118a0 Mon Sep 17 00:00:00 2001 From: n3wbie Date: Fri, 6 Dec 2024 12:00:39 +0900 Subject: [PATCH 13/44] test: txtar for approve & transferfrom using grc20reg --- .../grc20reg/approve_transferfrom.txtar | 53 +++++++++++++++++++ 1 file changed, 53 insertions(+) create mode 100644 __local/grc20_tokens/grc20reg/approve_transferfrom.txtar diff --git a/__local/grc20_tokens/grc20reg/approve_transferfrom.txtar b/__local/grc20_tokens/grc20reg/approve_transferfrom.txtar new file mode 100644 index 000000000..ff43ac448 --- /dev/null +++ b/__local/grc20_tokens/grc20reg/approve_transferfrom.txtar @@ -0,0 +1,53 @@ +loadpkg gno.land/p/demo/users + +loadpkg gno.land/r/demo/foo20 +loadpkg gno.land/r/demo/grc20reg + +loadpkg gno.land/r/demo/reg $WORK/reg + +## start a new node +gnoland start + +## faucet +# gnokey maketx call -pkgpath gno.land/r/demo/foo20 -func Faucet -gas-fee 1ugnot -gas-wanted 4000000 -broadcast -chainid=tendermint_test test1 + +## print reg addr +gnokey maketx call -pkgpath gno.land/r/demo/reg -func RelamAddr -gas-fee 1ugnot -gas-wanted 4000000 -broadcast -chainid=tendermint_test test1 +stdout 'g19tlskvga928es8ug2empargp0teul03apzjud9' + +## approve +gnokey maketx call -pkgpath gno.land/r/demo/foo20 -func Approve -args 'g19tlskvga928es8ug2empargp0teul03apzjud9' -args '100' -gas-fee 1ugnot -gas-wanted 4000000 -broadcast -chainid=tendermint_test test1 + +## transfer from +gnokey maketx call -pkgpath gno.land/r/demo/reg -func TransferFromWithReg -gas-fee 1ugnot -gas-wanted 4000000 -broadcast -chainid=tendermint_test test1 +stdout '' + +-- reg/reg.gno -- +package reg + +import ( + "std" + + "gno.land/r/demo/grc20reg" +) + +func RelamAddr() string { + addr := std.CurrentRealm().Addr().String() + return addr +} + +func TransferFromWithReg() { + caller := std.PrevRealm().Addr() + curr := std.CurrentRealm().Addr() + + + // using import + // foo20.TransferFrom(uCaller, uCurr, uint64(100)) + + // using grc20reg + fooTokenGetter := grc20reg.Get("gno.land/r/demo/foo20") + fooToken := fooTokenGetter() + userTeller := fooToken.CallerTeller() + + userTeller.TransferFrom(caller, curr, uint64(100)) +} \ No newline at end of file From bba3ee4d0bfb485bca9dc4566b0255ca81093fc2 Mon Sep 17 00:00:00 2001 From: n3wbie Date: Fri, 6 Dec 2024 18:15:48 +0900 Subject: [PATCH 14/44] feat: use teller to transfer token --- pool/pool.gno | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/pool/pool.gno b/pool/pool.gno index 0ce792a66..76286a22e 100644 --- a/pool/pool.gno +++ b/pool/pool.gno @@ -142,6 +142,8 @@ func Collect( var amount0, amount1 *u256.Uint + var amount0, amount1 *u256.Uint + // Smallest of three: amount0Requested, position.tokensOwed0, pool.balances.token0 amount0Req := u256.MustFromDecimal(amount0Requested) amount0, position.tokensOwed0, pool.balances.token0 = collectToken(amount0Req, position.tokensOwed0, pool.balances.token0) @@ -822,10 +824,10 @@ func collectProtocol( uAmount0 := amount0.Uint64() uAmount1 := amount1.Uint64() - token0 := common.GetToken(pool.token0Path) + token0 := common.GetTokenTeller(pool.token0Path) checkTransferError(token0.Transfer(recipient, uAmount0)) - token1 := common.GetToken(pool.token1Path) + token1 := common.GetTokenTeller(pool.token1Path) checkTransferError(token1.Transfer(recipient, uAmount1)) return amount0.ToString(), amount1.ToString() @@ -954,7 +956,7 @@ func (pool *Pool) transferFromAndVerify( panic(err) } - token := common.GetToken(tokenPath) + token := common.GetTokenTeller(tokenPath) checkTransferError(token.TransferFrom(from, to, amountUint64)) // update pool balances From 747f9ae0f61d23ce6717909988b9fb912691ab2b Mon Sep 17 00:00:00 2001 From: n3wbie Date: Fri, 6 Dec 2024 19:01:48 +0900 Subject: [PATCH 15/44] feat: IsRegistered() to check if token is registered or not --- _deploy/r/gnoswap/common/grc20reg_helper.gno | 12 +++++++++- .../r/gnoswap/common/grc20reg_helper_test.gno | 23 +++++++++++++------ 2 files changed, 27 insertions(+), 8 deletions(-) diff --git a/_deploy/r/gnoswap/common/grc20reg_helper.gno b/_deploy/r/gnoswap/common/grc20reg_helper.gno index e8c17e3ab..239c42ca8 100644 --- a/_deploy/r/gnoswap/common/grc20reg_helper.gno +++ b/_deploy/r/gnoswap/common/grc20reg_helper.gno @@ -2,7 +2,7 @@ package common import ( "gno.land/p/demo/grc/grc20" - + "gno.land/p/demo/ufmt" "gno.land/r/demo/grc20reg" ) @@ -34,3 +34,13 @@ func GetTokenTeller(path string) grc20.Teller { token := tokenGetter() return token.CallerTeller() } + +// IsRegistered returns nil if token is registered to grc20reg +// otherwise, it returns an error +func IsRegistered(path string) error { + getter := grc20reg.Get(path) + if getter == nil { + return ufmt.Errorf("token(%s) is not registered to grc20reg", path) + } + return nil +} diff --git a/_deploy/r/gnoswap/common/grc20reg_helper_test.gno b/_deploy/r/gnoswap/common/grc20reg_helper_test.gno index e5aba65bd..4287a62db 100644 --- a/_deploy/r/gnoswap/common/grc20reg_helper_test.gno +++ b/_deploy/r/gnoswap/common/grc20reg_helper_test.gno @@ -11,11 +11,15 @@ import ( _ "gno.land/r/demo/foo20" ) +var ( + tokenPath = "gno.land/r/demo/foo20" +) + func TestGetToken(t *testing.T) { t.Run("registered(by default)", func(t *testing.T) { - token := GetToken("gno.land/r/demo/foo20") + token := GetToken(tokenPath) if token == nil { - t.Error("Expected non-nil teller for WUGNOT") + t.Error("Expected non-nil teller for foo20") } }) @@ -30,7 +34,7 @@ func TestGetToken(t *testing.T) { } func TestTokenMethod(t *testing.T) { - token := GetToken("gno.land/r/demo/foo20") + token := GetToken(tokenPath) t.Run("GetName()", func(t *testing.T) { uassert.Equal(t, "Foo", token.GetName()) @@ -72,9 +76,9 @@ func TestTokenMethod(t *testing.T) { func TestGetTokenTeller(t *testing.T) { t.Run("registered(by default)", func(t *testing.T) { - teller := GetTokenTeller("gno.land/r/demo/foo20") + teller := GetTokenTeller(tokenPath) if teller == nil { - t.Error("Expected non-nil teller for WUGNOT") + t.Error("Expected non-nil teller for foo20") } }) @@ -89,8 +93,8 @@ func TestGetTokenTeller(t *testing.T) { } func TestTellerMethod(t *testing.T) { - teller := GetTokenTeller("gno.land/r/demo/foo20") - token := GetToken("gno.land/r/demo/foo20") + teller := GetTokenTeller(tokenPath) + token := GetToken(tokenPath) defaultHolder := std.Address("g1manfred47kzduec920z88wfr64ylksmdcedlf5") addr01 := testutils.TestAddress("addr01") @@ -123,3 +127,8 @@ func TestTellerMethod(t *testing.T) { uassert.Error(t, teller.TransferFrom(addr01, defaultHolder, uint64(500))) // not enough allowance }) } + +func TestIsRegistered(t *testing.T) { + uassert.NoError(t, IsRegistered(tokenPath)) + uassert.Error(t, IsRegistered("not_registered")) +} From 08f69a6b9928ad675991b9f5e9c793bec5387188 Mon Sep 17 00:00:00 2001 From: n3wbie Date: Fri, 6 Dec 2024 19:22:33 +0900 Subject: [PATCH 16/44] refactor: remove old token_register --- pool/token_register.gno | 153 ---------------------------------------- 1 file changed, 153 deletions(-) delete mode 100644 pool/token_register.gno diff --git a/pool/token_register.gno b/pool/token_register.gno deleted file mode 100644 index 419b2ee49..000000000 --- a/pool/token_register.gno +++ /dev/null @@ -1,153 +0,0 @@ -package pool - -import ( - "std" - "strings" - - "gno.land/p/demo/ufmt" - pusers "gno.land/p/demo/users" - - "gno.land/r/gnoswap/v1/common" - "gno.land/r/gnoswap/v1/consts" -) - -// GRC20Interface is the interface for GRC20 tokens -// It is used to interact with the GRC20 tokens without importing but by registering each tokens function -type GRC20Interface interface { - Transfer() func(to pusers.AddressOrName, amount uint64) - TransferFrom() func(from, to pusers.AddressOrName, amount uint64) - BalanceOf() func(owner pusers.AddressOrName) uint64 - Approve() func(spender pusers.AddressOrName, amount uint64) -} - -var ( - registered = make(map[string]GRC20Interface) -) - -// GetRegisteredTokens returns a list of all registered tokens -func GetRegisteredTokens() []string { - tokens := make([]string, 0, len(registered)) - for k := range registered { - tokens = append(tokens, k) - } - return tokens -} - -// RegisterGRC20Interface registers a GRC20 token interface -func RegisterGRC20Interface(pkgPath string, igrc20 GRC20Interface) { - prevAddr := std.PrevRealm().Addr() - prevPath := std.PrevRealm().PkgPath() - if !(prevAddr == consts.TOKEN_REGISTER || prevPath == consts.INIT_REGISTER_PATH || strings.HasPrefix(prevPath, "gno.land/r/g1er355fkjksqpdtwmhf5penwa82p0rhqxkkyhk5")) { - panic(addDetailToError( - errNoPermission, - ufmt.Sprintf("token_register.gno__RegisterGRC20Interface() || only register(%s) can register token, called from %s", consts.TOKEN_REGISTER, prevAddr), - )) - } - - pkgPath = handleNative(pkgPath) - - _, found := registered[pkgPath] - if found { - panic(addDetailToError( - errAlreadyRegistered, - ufmt.Sprintf("token_register.gno__RegisterGRC20Interface() || token(%s) already registered", pkgPath), - )) - } - - registered[pkgPath] = igrc20 -} - -// UnregisterGRC20Interface unregisters a GRC20 token interface -func UnregisterGRC20Interface(pkgPath string) { - if err := common.SatisfyCond(isUserCall()); err != nil { - panic(addDetailToError( - errNoPermission, - ufmt.Sprintf("token_register.gno__UnregisterGRC20Interface() || unauthorized address(%s) to unregister", std.PrevRealm().Addr()), - )) - } - - caller := std.PrevRealm().Addr() - if err := common.TokenRegisterOnly(caller); err != nil { - panic(addDetailToError( - errNoPermission, - ufmt.Sprintf("token_register.gno__UnregisterGRC20Interface() || unauthorized address(%s) to unregister", caller), - )) - } - - pkgPath = handleNative(pkgPath) - - _, found := registered[pkgPath] - if found { - delete(registered, pkgPath) - } -} - -func transferByRegisterCall(pkgPath string, to std.Address, amount uint64) bool { - pkgPath = handleNative(pkgPath) - - _, found := registered[pkgPath] - if !found { - panic(addDetailToError( - errNotRegistered, - ufmt.Sprintf("token_register.gno__transferByRegisterCall() || token(%s) not registered", pkgPath), - )) - } - registered[pkgPath].Transfer()(pusers.AddressOrName(to), amount) - - return true -} - -func transferFromByRegisterCall(pkgPath string, from, to std.Address, amount uint64) bool { - pkgPath = handleNative(pkgPath) - - _, found := registered[pkgPath] - if !found { - panic(addDetailToError( - errNotRegistered, - ufmt.Sprintf("token_register.gno__transferFromByRegisterCall() || token(%s) not registered", pkgPath), - )) - } - - registered[pkgPath].TransferFrom()(pusers.AddressOrName(from), pusers.AddressOrName(to), amount) - - return true -} - -func balanceOfByRegisterCall(pkgPath string, owner std.Address) uint64 { - pkgPath = handleNative(pkgPath) - - _, found := registered[pkgPath] - if !found { - panic(addDetailToError( - errNotRegistered, - ufmt.Sprintf("token_register.gno__balanceOfByRegisterCall() || token(%s) not registered", pkgPath), - )) - } - - balance := registered[pkgPath].BalanceOf()(pusers.AddressOrName(owner)) - return balance -} - -func approveByRegisterCall(pkgPath string, spender std.Address, amount uint64) bool { - pkgPath = handleNative(pkgPath) - - _, found := registered[pkgPath] - if !found { - panic(addDetailToError( - errNotRegistered, - ufmt.Sprintf("token_register.gno__approveByRegisterCall() || token(%s) not registered", pkgPath), - )) - } - - registered[pkgPath].Approve()(pusers.AddressOrName(spender), amount) - - return true -} - -func handleNative(pkgPath string) string { - if pkgPath == consts.GNOT { - return consts.WRAPPED_WUGNOT - } - - return pkgPath -} From 32b18f3784ea433d523c6af5338cd6cef8b72f21 Mon Sep 17 00:00:00 2001 From: n3wbie Date: Sun, 8 Dec 2024 23:35:29 +0900 Subject: [PATCH 17/44] chore: rename teller --- pool/pool.gno | 8 ++++---- pool/protocol_fee_withdrawal.gno | 7 +++++-- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/pool/pool.gno b/pool/pool.gno index 76286a22e..f6f8fa464 100644 --- a/pool/pool.gno +++ b/pool/pool.gno @@ -824,11 +824,11 @@ func collectProtocol( uAmount0 := amount0.Uint64() uAmount1 := amount1.Uint64() - token0 := common.GetTokenTeller(pool.token0Path) - checkTransferError(token0.Transfer(recipient, uAmount0)) + token0Teller := common.GetTokenTeller(pool.token0Path) + checkTransferError(token0Teller.Transfer(recipient, uAmount0)) - token1 := common.GetTokenTeller(pool.token1Path) - checkTransferError(token1.Transfer(recipient, uAmount1)) + token1Teller := common.GetTokenTeller(pool.token1Path) + checkTransferError(token1Teller.Transfer(recipient, uAmount1)) return amount0.ToString(), amount1.ToString() } diff --git a/pool/protocol_fee_withdrawal.gno b/pool/protocol_fee_withdrawal.gno index 4e79388fa..bf55e2c2c 100644 --- a/pool/protocol_fee_withdrawal.gno +++ b/pool/protocol_fee_withdrawal.gno @@ -64,8 +64,11 @@ func HandleWithdrawalFee( feeAmount0, afterAmount0 := calculateAmountWithFee(u256.MustFromDecimal(_amount0), u256.NewUint(fee)) feeAmount1, afterAmount1 := calculateAmountWithFee(u256.MustFromDecimal(_amount1), u256.NewUint(fee)) - transferFromByRegisterCall(token0Path, positionCaller, consts.PROTOCOL_FEE_ADDR, feeAmount0.Uint64()) - transferFromByRegisterCall(token1Path, positionCaller, consts.PROTOCOL_FEE_ADDR, feeAmount1.Uint64()) + token0Teller := common.GetTokenTeller(token0Path) + checkTransferError(token0Teller.TransferFrom(positionCaller, consts.PROTOCOL_FEE_ADDR, feeAmount0.Uint64())) + + token1Teller := common.GetTokenTeller(token1Path) + checkTransferError(token1Teller.TransferFrom(positionCaller, consts.PROTOCOL_FEE_ADDR, feeAmount1.Uint64())) prevAddr, prevPkgPath := getPrev() std.Emit( From be570a24257f7445c057a4bdc7010b107b3feedb Mon Sep 17 00:00:00 2001 From: n3wbie Date: Mon, 9 Dec 2024 00:00:39 +0900 Subject: [PATCH 18/44] feat: guard logic to see if token is registered --- _deploy/r/gnoswap/common/errors.gno | 7 +++-- _deploy/r/gnoswap/common/grc20reg_helper.gno | 9 ++++++ pool/pool.gno | 30 ++++++++++++++++---- pool/pool_manager.gno | 9 ++++++ 4 files changed, 46 insertions(+), 9 deletions(-) diff --git a/_deploy/r/gnoswap/common/errors.gno b/_deploy/r/gnoswap/common/errors.gno index 8cf952f20..43bd31337 100644 --- a/_deploy/r/gnoswap/common/errors.gno +++ b/_deploy/r/gnoswap/common/errors.gno @@ -7,9 +7,10 @@ import ( ) var ( - errNoPermission = errors.New("[GNOSWAP-COMMON-001] caller has no permission") - errHalted = errors.New("[GNOSWAP-COMMON-002] halted") - errOutOfRange = errors.New("[GNOSWAP-COMMON-003] value out of range") + errNoPermission = errors.New("[GNOSWAP-COMMON-001] caller has no permission") + errHalted = errors.New("[GNOSWAP-COMMON-002] halted") + errOutOfRange = errors.New("[GNOSWAP-COMMON-003] value out of range") + errNotRegistered = errors.New("[GNOSWAP-COMMON-004] token is not registered") ) func addDetailToError(err error, detail string) string { diff --git a/_deploy/r/gnoswap/common/grc20reg_helper.gno b/_deploy/r/gnoswap/common/grc20reg_helper.gno index 239c42ca8..785b3f65c 100644 --- a/_deploy/r/gnoswap/common/grc20reg_helper.gno +++ b/_deploy/r/gnoswap/common/grc20reg_helper.gno @@ -44,3 +44,12 @@ func IsRegistered(path string) error { } return nil } + +func MustRegisteredToken(path string) { + if err := IsRegistered(path); err != nil { + panic(addDetailToError( + errNotRegistered, + ufmt.Sprintf("token(%s) is not registered", path), + )) + } +} diff --git a/pool/pool.gno b/pool/pool.gno index f6f8fa464..3f6c59118 100644 --- a/pool/pool.gno +++ b/pool/pool.gno @@ -28,6 +28,9 @@ func Mint( positionCaller std.Address, ) (string, string) { common.IsHalted() + common.MustRegisteredToken(token0Path) + common.MustRegisteredToken(token1Path) + if common.GetLimitCaller() { caller := std.PrevRealm().Addr() if err := common.PositionOnly(caller); err != nil { @@ -74,6 +77,9 @@ func Burn( liquidityAmount string, // uint128 ) (string, string) { // uint256 x2 common.IsHalted() + common.MustRegisteredToken(token0Path) + common.MustRegisteredToken(token1Path) + caller := std.PrevRealm().Addr() if common.GetLimitCaller() { if err := common.PositionOnly(caller); err != nil { @@ -119,6 +125,9 @@ func Collect( amount1Requested string, ) (string, string) { common.IsHalted() + common.MustRegisteredToken(token0Path) + common.MustRegisteredToken(token1Path) + if common.GetLimitCaller() { caller := std.PrevRealm().Addr() if err := common.PositionOnly(caller); err != nil { @@ -218,6 +227,9 @@ func Swap( payer std.Address, // router ) (string, string) { common.IsHalted() + common.MustRegisteredToken(token0Path) + common.MustRegisteredToken(token1Path) + if common.GetLimitCaller() { caller := std.PrevRealm().Addr() if err := common.RouterOnly(caller); err != nil { @@ -727,9 +739,12 @@ func CollectProtocolByAdmin( token1Path string, fee uint32, recipient std.Address, - amount0Requested string, - amount1Requested string, -) (string, string) { + _amount0Requested string, // uint128 + _amount1Requested string, // uint128 +) (string, string) { // uint128 x2 + common.MustRegisteredToken(token0Path) + common.MustRegisteredToken(token1Path) + caller := std.PrevRealm().Addr() if err := common.AdminOnly(caller); err != nil { panic(err) @@ -769,9 +784,12 @@ func CollectProtocol( token1Path string, fee uint32, recipient std.Address, - amount0Requested string, - amount1Requested string, -) (string, string) { + _amount0Requested string, // uint128 + _amount1Requested string, // uint128 +) (string, string) { // uint128 x2 + common.MustRegisteredToken(token0Path) + common.MustRegisteredToken(token1Path) + caller := std.PrevRealm().Addr() if err := common.GovernanceOnly(caller); err != nil { panic(err) diff --git a/pool/pool_manager.gno b/pool/pool_manager.gno index e6652805f..88d48e682 100644 --- a/pool/pool_manager.gno +++ b/pool/pool_manager.gno @@ -150,6 +150,9 @@ func CreatePool( // wrap first token0Path, token1Path = poolInfo.wrap() + common.MustRegisteredToken(token0Path) + common.MustRegisteredToken(token1Path) + // reinitialize poolInfo with wrapped tokens poolInfo = newPoolParams(token0Path, token1Path, fee, _sqrtPriceX96) @@ -224,6 +227,9 @@ func DoesPoolPathExist(poolPath string) bool { // It constructs the poolPath from the given parameters and returns the corresponding pool. // Returns pool struct func GetPool(token0Path, token1Path string, fee uint32) *Pool { + common.MustRegisteredToken(token0Path) + common.MustRegisteredToken(token1Path) + poolPath := GetPoolPath(token0Path, token1Path, fee) pool, exist := pools[poolPath] if !exist { @@ -252,6 +258,9 @@ func GetPoolFromPoolPath(poolPath string) *Pool { // GetPoolPath generates a poolPath from the given token paths and fee. // The poolPath is constructed by joining the token paths and fee with colons. func GetPoolPath(token0Path, token1Path string, fee uint32) string { + common.MustRegisteredToken(token0Path) + common.MustRegisteredToken(token1Path) + // TODO: this check is not unnecessary, if we are sure that // all the token paths in the pool are sorted in alphabetical order. if strings.Compare(token1Path, token0Path) < 0 { From 14b7e71277741b9b94d7314a2e104c4811c0b793 Mon Sep 17 00:00:00 2001 From: n3wbie Date: Mon, 9 Dec 2024 10:53:28 +0900 Subject: [PATCH 19/44] feat: MustRegistered in common - if token is not registered, it will panic. --- _deploy/r/gnoswap/common/grc20reg_helper.gno | 4 +++- .../r/gnoswap/common/grc20reg_helper_test.gno | 24 +++++++++++++++++-- 2 files changed, 25 insertions(+), 3 deletions(-) diff --git a/_deploy/r/gnoswap/common/grc20reg_helper.gno b/_deploy/r/gnoswap/common/grc20reg_helper.gno index 785b3f65c..50f945e02 100644 --- a/_deploy/r/gnoswap/common/grc20reg_helper.gno +++ b/_deploy/r/gnoswap/common/grc20reg_helper.gno @@ -45,7 +45,9 @@ func IsRegistered(path string) error { return nil } -func MustRegisteredToken(path string) { +// MustRegistered is a helper function to check if token is registered to grc20reg +// if token is not registered, it will panic +func MustRegistered(path string) { if err := IsRegistered(path); err != nil { panic(addDetailToError( errNotRegistered, diff --git a/_deploy/r/gnoswap/common/grc20reg_helper_test.gno b/_deploy/r/gnoswap/common/grc20reg_helper_test.gno index 4287a62db..023461f14 100644 --- a/_deploy/r/gnoswap/common/grc20reg_helper_test.gno +++ b/_deploy/r/gnoswap/common/grc20reg_helper_test.gno @@ -129,6 +129,26 @@ func TestTellerMethod(t *testing.T) { } func TestIsRegistered(t *testing.T) { - uassert.NoError(t, IsRegistered(tokenPath)) - uassert.Error(t, IsRegistered("not_registered")) + t.Run("registered(by default)", func(t *testing.T) { + uassert.NoError(t, IsRegistered(tokenPath)) + }) + + t.Run("not registered", func(t *testing.T) { + uassert.Error(t, IsRegistered("not_registered")) + }) +} + +func TestMustRegistered(t *testing.T) { + t.Run("registered(by default)", func(t *testing.T) { + MustRegistered(tokenPath) + }) + + t.Run("not registered", func(t *testing.T) { + defer func() { + if r := recover(); r == nil { + t.Errorf("Expected panic for non-registered token") + } + }() + MustRegistered("not_registered") + }) } From 0cfd449a2ab8fe76c62e95abb7aefc668dbe2c78 Mon Sep 17 00:00:00 2001 From: n3wbie Date: Mon, 9 Dec 2024 10:54:49 +0900 Subject: [PATCH 20/44] chore: rename --- pool/pool.gno | 24 ++++++++++++------------ pool/pool_manager.gno | 12 ++++++------ 2 files changed, 18 insertions(+), 18 deletions(-) diff --git a/pool/pool.gno b/pool/pool.gno index 3f6c59118..3980adcf4 100644 --- a/pool/pool.gno +++ b/pool/pool.gno @@ -28,8 +28,8 @@ func Mint( positionCaller std.Address, ) (string, string) { common.IsHalted() - common.MustRegisteredToken(token0Path) - common.MustRegisteredToken(token1Path) + common.MustRegistered(token0Path) + common.MustRegistered(token1Path) if common.GetLimitCaller() { caller := std.PrevRealm().Addr() @@ -77,8 +77,8 @@ func Burn( liquidityAmount string, // uint128 ) (string, string) { // uint256 x2 common.IsHalted() - common.MustRegisteredToken(token0Path) - common.MustRegisteredToken(token1Path) + common.MustRegistered(token0Path) + common.MustRegistered(token1Path) caller := std.PrevRealm().Addr() if common.GetLimitCaller() { @@ -125,8 +125,8 @@ func Collect( amount1Requested string, ) (string, string) { common.IsHalted() - common.MustRegisteredToken(token0Path) - common.MustRegisteredToken(token1Path) + common.MustRegistered(token0Path) + common.MustRegistered(token1Path) if common.GetLimitCaller() { caller := std.PrevRealm().Addr() @@ -227,8 +227,8 @@ func Swap( payer std.Address, // router ) (string, string) { common.IsHalted() - common.MustRegisteredToken(token0Path) - common.MustRegisteredToken(token1Path) + common.MustRegistered(token0Path) + common.MustRegistered(token1Path) if common.GetLimitCaller() { caller := std.PrevRealm().Addr() @@ -742,8 +742,8 @@ func CollectProtocolByAdmin( _amount0Requested string, // uint128 _amount1Requested string, // uint128 ) (string, string) { // uint128 x2 - common.MustRegisteredToken(token0Path) - common.MustRegisteredToken(token1Path) + common.MustRegistered(token0Path) + common.MustRegistered(token1Path) caller := std.PrevRealm().Addr() if err := common.AdminOnly(caller); err != nil { @@ -787,8 +787,8 @@ func CollectProtocol( _amount0Requested string, // uint128 _amount1Requested string, // uint128 ) (string, string) { // uint128 x2 - common.MustRegisteredToken(token0Path) - common.MustRegisteredToken(token1Path) + common.MustRegistered(token0Path) + common.MustRegistered(token1Path) caller := std.PrevRealm().Addr() if err := common.GovernanceOnly(caller); err != nil { diff --git a/pool/pool_manager.gno b/pool/pool_manager.gno index 88d48e682..cf318736b 100644 --- a/pool/pool_manager.gno +++ b/pool/pool_manager.gno @@ -150,8 +150,8 @@ func CreatePool( // wrap first token0Path, token1Path = poolInfo.wrap() - common.MustRegisteredToken(token0Path) - common.MustRegisteredToken(token1Path) + common.MustRegistered(token0Path) + common.MustRegistered(token1Path) // reinitialize poolInfo with wrapped tokens poolInfo = newPoolParams(token0Path, token1Path, fee, _sqrtPriceX96) @@ -227,8 +227,8 @@ func DoesPoolPathExist(poolPath string) bool { // It constructs the poolPath from the given parameters and returns the corresponding pool. // Returns pool struct func GetPool(token0Path, token1Path string, fee uint32) *Pool { - common.MustRegisteredToken(token0Path) - common.MustRegisteredToken(token1Path) + common.MustRegistered(token0Path) + common.MustRegistered(token1Path) poolPath := GetPoolPath(token0Path, token1Path, fee) pool, exist := pools[poolPath] @@ -258,8 +258,8 @@ func GetPoolFromPoolPath(poolPath string) *Pool { // GetPoolPath generates a poolPath from the given token paths and fee. // The poolPath is constructed by joining the token paths and fee with colons. func GetPoolPath(token0Path, token1Path string, fee uint32) string { - common.MustRegisteredToken(token0Path) - common.MustRegisteredToken(token1Path) + common.MustRegistered(token0Path) + common.MustRegistered(token1Path) // TODO: this check is not unnecessary, if we are sure that // all the token paths in the pool are sorted in alphabetical order. From 16dfe8e459ed90e1dcd05005ad3b4b4d4982ed87 Mon Sep 17 00:00:00 2001 From: 0xTopaz <60733299+onlyhyde@users.noreply.github.com> Date: Mon, 9 Dec 2024 12:32:08 +0900 Subject: [PATCH 21/44] GSW-1838 fix: test errors are fixed (#422) * GSW-1838 fix: test errors are fixed - Integrate helper functions for tests - Change file extensions to prevent test code in the test folder from being executed - Fixing failure errors due to code integration - Known issue : Fixed additional test failure case related to getter * fix: remove time compare in unit test * fix: do not setup data in init * test: pool manger testcase --------- Co-authored-by: n3wbie --- .../__TEST_0_INIT_TOKEN_REGISTER_test.gnoA | 183 ++++++++++++++++++ 1 file changed, 183 insertions(+) create mode 100644 pool/tests/__TEST_0_INIT_TOKEN_REGISTER_test.gnoA diff --git a/pool/tests/__TEST_0_INIT_TOKEN_REGISTER_test.gnoA b/pool/tests/__TEST_0_INIT_TOKEN_REGISTER_test.gnoA new file mode 100644 index 000000000..6702bd674 --- /dev/null +++ b/pool/tests/__TEST_0_INIT_TOKEN_REGISTER_test.gnoA @@ -0,0 +1,183 @@ +package pool + +import ( + "std" + "testing" + + "gno.land/p/demo/testutils" + "gno.land/p/demo/uassert" + + "gno.land/r/onbloc/foo" + + "gno.land/r/onbloc/bar" + + "gno.land/r/onbloc/baz" + + "gno.land/r/onbloc/qux" + + "gno.land/r/demo/wugnot" + + "gno.land/r/onbloc/obl" + + "gno.land/r/gnoswap/v1/gns" + + "gno.land/r/gnoswap/v1/consts" + + pusers "gno.land/p/demo/users" +) + +type FooToken struct{} + +func (FooToken) Transfer() func(to pusers.AddressOrName, amount uint64) { + return foo.Transfer +} +func (FooToken) TransferFrom() func(from, to pusers.AddressOrName, amount uint64) { + return foo.TransferFrom +} +func (FooToken) BalanceOf() func(owner pusers.AddressOrName) uint64 { + return foo.BalanceOf +} +func (FooToken) Approve() func(spender pusers.AddressOrName, amount uint64) { + return foo.Approve +} + +type BarToken struct{} + +func (BarToken) Transfer() func(to pusers.AddressOrName, amount uint64) { + return bar.Transfer +} +func (BarToken) TransferFrom() func(from, to pusers.AddressOrName, amount uint64) { + return bar.TransferFrom +} +func (BarToken) BalanceOf() func(owner pusers.AddressOrName) uint64 { + return bar.BalanceOf +} +func (BarToken) Approve() func(spender pusers.AddressOrName, amount uint64) { + return bar.Approve +} + +type BazToken struct{} + +func (BazToken) Transfer() func(to pusers.AddressOrName, amount uint64) { + return baz.Transfer +} +func (BazToken) TransferFrom() func(from, to pusers.AddressOrName, amount uint64) { + return baz.TransferFrom +} +func (BazToken) BalanceOf() func(owner pusers.AddressOrName) uint64 { + return baz.BalanceOf +} +func (BazToken) Approve() func(spender pusers.AddressOrName, amount uint64) { + return baz.Approve +} + +type QuxToken struct{} + +func (QuxToken) Transfer() func(to pusers.AddressOrName, amount uint64) { + return qux.Transfer +} +func (QuxToken) TransferFrom() func(from, to pusers.AddressOrName, amount uint64) { + return qux.TransferFrom +} +func (QuxToken) BalanceOf() func(owner pusers.AddressOrName) uint64 { + return qux.BalanceOf +} +func (QuxToken) Approve() func(spender pusers.AddressOrName, amount uint64) { + return qux.Approve +} + +type WugnotToken struct{} + +func (WugnotToken) Transfer() func(to pusers.AddressOrName, amount uint64) { + return wugnot.Transfer +} +func (WugnotToken) TransferFrom() func(from, to pusers.AddressOrName, amount uint64) { + return wugnot.TransferFrom +} +func (WugnotToken) BalanceOf() func(owner pusers.AddressOrName) uint64 { + return wugnot.BalanceOf +} +func (WugnotToken) Approve() func(spender pusers.AddressOrName, amount uint64) { + return wugnot.Approve +} + +type OBLToken struct{} + +func (OBLToken) Transfer() func(to pusers.AddressOrName, amount uint64) { + return obl.Transfer +} +func (OBLToken) TransferFrom() func(from, to pusers.AddressOrName, amount uint64) { + return obl.TransferFrom +} +func (OBLToken) BalanceOf() func(owner pusers.AddressOrName) uint64 { + return obl.BalanceOf +} +func (OBLToken) Approve() func(spender pusers.AddressOrName, amount uint64) { + return obl.Approve +} + +type GNSToken struct{} + +func (GNSToken) Transfer() func(to pusers.AddressOrName, amount uint64) { + return gns.Transfer +} + +func (GNSToken) TransferFrom() func(from, to pusers.AddressOrName, amount uint64) { + return gns.TransferFrom +} + +func (GNSToken) BalanceOf() func(owner pusers.AddressOrName) uint64 { + return gns.BalanceOf +} + +func (GNSToken) Approve() func(spender pusers.AddressOrName, amount uint64) { + return gns.Approve +} + +func init() { + std.TestSetRealm(std.NewUserRealm(consts.TOKEN_REGISTER)) + + RegisterGRC20Interface("gno.land/r/onbloc/bar", BarToken{}) + RegisterGRC20Interface("gno.land/r/onbloc/foo", FooToken{}) + RegisterGRC20Interface("gno.land/r/onbloc/baz", BazToken{}) + RegisterGRC20Interface("gno.land/r/onbloc/qux", QuxToken{}) + RegisterGRC20Interface("gno.land/r/demo/wugnot", WugnotToken{}) + RegisterGRC20Interface("gno.land/r/onbloc/obl", OBLToken{}) + RegisterGRC20Interface("gno.land/r/gnoswap/v1/gns", GNSToken{}) +} + +func TestGetRegisteredTokens(t *testing.T) { + uassert.Equal(t, len(GetRegisteredTokens()), 7) +} + +func TestRegisterGRC20Interface(t *testing.T) { + uassert.PanicsWithMessage(t, + `[GNOSWAP-POOL-001] caller has no permission || token_register.gno__RegisterGRC20Interface() || only register(g1er355fkjksqpdtwmhf5penwa82p0rhqxkkyhk5) can register token, called from g1wymu47drhr0kuq2098m792lytgtj2nyx77yrsm`, + func() { + RegisterGRC20Interface("gno.land/r/onbloc/bar", BarToken{}) + }, + ) +} + +func TestUnregisterGRC20Interface(t *testing.T) { + dummy := testutils.TestAddress("dummy") + std.TestSetRealm(std.NewUserRealm(dummy)) + + uassert.PanicsWithMessage(t, + `[GNOSWAP-POOL-001] caller has no permission || token_register.gno__UnregisterGRC20Interface() || unauthorized address(g1v36k6mteta047h6lta047h6lta047h6lz7gmv8) to unregister`, + func() { + UnregisterGRC20Interface("gno.land/r/onbloc/bar") + }, + ) + + uassert.Equal(t, len(GetRegisteredTokens()), 7) + + std.TestSetRealm(std.NewUserRealm(consts.TOKEN_REGISTER)) + UnregisterGRC20Interface("gno.land/r/onbloc/bar") + uassert.Equal(t, len(GetRegisteredTokens()), 6) + + // re-register to avoid panic in other tests + RegisterGRC20Interface("gno.land/r/onbloc/bar", BarToken{}) + + std.TestSetRealm(adminRealm) +} From 880311f3479960629571acbbc8ef57eb05bc6d15 Mon Sep 17 00:00:00 2001 From: n3wbie Date: Mon, 2 Dec 2024 19:48:28 +0900 Subject: [PATCH 22/44] GSW-1838 refactor: use grc20reg - use gno's grc20reg realm to support dynamic token transfer in pool --- .../__TEST_0_INIT_TOKEN_REGISTER_test.gnoA | 183 ------------------ 1 file changed, 183 deletions(-) delete mode 100644 pool/tests/__TEST_0_INIT_TOKEN_REGISTER_test.gnoA diff --git a/pool/tests/__TEST_0_INIT_TOKEN_REGISTER_test.gnoA b/pool/tests/__TEST_0_INIT_TOKEN_REGISTER_test.gnoA deleted file mode 100644 index 6702bd674..000000000 --- a/pool/tests/__TEST_0_INIT_TOKEN_REGISTER_test.gnoA +++ /dev/null @@ -1,183 +0,0 @@ -package pool - -import ( - "std" - "testing" - - "gno.land/p/demo/testutils" - "gno.land/p/demo/uassert" - - "gno.land/r/onbloc/foo" - - "gno.land/r/onbloc/bar" - - "gno.land/r/onbloc/baz" - - "gno.land/r/onbloc/qux" - - "gno.land/r/demo/wugnot" - - "gno.land/r/onbloc/obl" - - "gno.land/r/gnoswap/v1/gns" - - "gno.land/r/gnoswap/v1/consts" - - pusers "gno.land/p/demo/users" -) - -type FooToken struct{} - -func (FooToken) Transfer() func(to pusers.AddressOrName, amount uint64) { - return foo.Transfer -} -func (FooToken) TransferFrom() func(from, to pusers.AddressOrName, amount uint64) { - return foo.TransferFrom -} -func (FooToken) BalanceOf() func(owner pusers.AddressOrName) uint64 { - return foo.BalanceOf -} -func (FooToken) Approve() func(spender pusers.AddressOrName, amount uint64) { - return foo.Approve -} - -type BarToken struct{} - -func (BarToken) Transfer() func(to pusers.AddressOrName, amount uint64) { - return bar.Transfer -} -func (BarToken) TransferFrom() func(from, to pusers.AddressOrName, amount uint64) { - return bar.TransferFrom -} -func (BarToken) BalanceOf() func(owner pusers.AddressOrName) uint64 { - return bar.BalanceOf -} -func (BarToken) Approve() func(spender pusers.AddressOrName, amount uint64) { - return bar.Approve -} - -type BazToken struct{} - -func (BazToken) Transfer() func(to pusers.AddressOrName, amount uint64) { - return baz.Transfer -} -func (BazToken) TransferFrom() func(from, to pusers.AddressOrName, amount uint64) { - return baz.TransferFrom -} -func (BazToken) BalanceOf() func(owner pusers.AddressOrName) uint64 { - return baz.BalanceOf -} -func (BazToken) Approve() func(spender pusers.AddressOrName, amount uint64) { - return baz.Approve -} - -type QuxToken struct{} - -func (QuxToken) Transfer() func(to pusers.AddressOrName, amount uint64) { - return qux.Transfer -} -func (QuxToken) TransferFrom() func(from, to pusers.AddressOrName, amount uint64) { - return qux.TransferFrom -} -func (QuxToken) BalanceOf() func(owner pusers.AddressOrName) uint64 { - return qux.BalanceOf -} -func (QuxToken) Approve() func(spender pusers.AddressOrName, amount uint64) { - return qux.Approve -} - -type WugnotToken struct{} - -func (WugnotToken) Transfer() func(to pusers.AddressOrName, amount uint64) { - return wugnot.Transfer -} -func (WugnotToken) TransferFrom() func(from, to pusers.AddressOrName, amount uint64) { - return wugnot.TransferFrom -} -func (WugnotToken) BalanceOf() func(owner pusers.AddressOrName) uint64 { - return wugnot.BalanceOf -} -func (WugnotToken) Approve() func(spender pusers.AddressOrName, amount uint64) { - return wugnot.Approve -} - -type OBLToken struct{} - -func (OBLToken) Transfer() func(to pusers.AddressOrName, amount uint64) { - return obl.Transfer -} -func (OBLToken) TransferFrom() func(from, to pusers.AddressOrName, amount uint64) { - return obl.TransferFrom -} -func (OBLToken) BalanceOf() func(owner pusers.AddressOrName) uint64 { - return obl.BalanceOf -} -func (OBLToken) Approve() func(spender pusers.AddressOrName, amount uint64) { - return obl.Approve -} - -type GNSToken struct{} - -func (GNSToken) Transfer() func(to pusers.AddressOrName, amount uint64) { - return gns.Transfer -} - -func (GNSToken) TransferFrom() func(from, to pusers.AddressOrName, amount uint64) { - return gns.TransferFrom -} - -func (GNSToken) BalanceOf() func(owner pusers.AddressOrName) uint64 { - return gns.BalanceOf -} - -func (GNSToken) Approve() func(spender pusers.AddressOrName, amount uint64) { - return gns.Approve -} - -func init() { - std.TestSetRealm(std.NewUserRealm(consts.TOKEN_REGISTER)) - - RegisterGRC20Interface("gno.land/r/onbloc/bar", BarToken{}) - RegisterGRC20Interface("gno.land/r/onbloc/foo", FooToken{}) - RegisterGRC20Interface("gno.land/r/onbloc/baz", BazToken{}) - RegisterGRC20Interface("gno.land/r/onbloc/qux", QuxToken{}) - RegisterGRC20Interface("gno.land/r/demo/wugnot", WugnotToken{}) - RegisterGRC20Interface("gno.land/r/onbloc/obl", OBLToken{}) - RegisterGRC20Interface("gno.land/r/gnoswap/v1/gns", GNSToken{}) -} - -func TestGetRegisteredTokens(t *testing.T) { - uassert.Equal(t, len(GetRegisteredTokens()), 7) -} - -func TestRegisterGRC20Interface(t *testing.T) { - uassert.PanicsWithMessage(t, - `[GNOSWAP-POOL-001] caller has no permission || token_register.gno__RegisterGRC20Interface() || only register(g1er355fkjksqpdtwmhf5penwa82p0rhqxkkyhk5) can register token, called from g1wymu47drhr0kuq2098m792lytgtj2nyx77yrsm`, - func() { - RegisterGRC20Interface("gno.land/r/onbloc/bar", BarToken{}) - }, - ) -} - -func TestUnregisterGRC20Interface(t *testing.T) { - dummy := testutils.TestAddress("dummy") - std.TestSetRealm(std.NewUserRealm(dummy)) - - uassert.PanicsWithMessage(t, - `[GNOSWAP-POOL-001] caller has no permission || token_register.gno__UnregisterGRC20Interface() || unauthorized address(g1v36k6mteta047h6lta047h6lta047h6lz7gmv8) to unregister`, - func() { - UnregisterGRC20Interface("gno.land/r/onbloc/bar") - }, - ) - - uassert.Equal(t, len(GetRegisteredTokens()), 7) - - std.TestSetRealm(std.NewUserRealm(consts.TOKEN_REGISTER)) - UnregisterGRC20Interface("gno.land/r/onbloc/bar") - uassert.Equal(t, len(GetRegisteredTokens()), 6) - - // re-register to avoid panic in other tests - RegisterGRC20Interface("gno.land/r/onbloc/bar", BarToken{}) - - std.TestSetRealm(adminRealm) -} From 403bd65b7411ea77de299e6cb94b736bb421da34 Mon Sep 17 00:00:00 2001 From: n3wbie Date: Tue, 10 Dec 2024 12:39:02 +0900 Subject: [PATCH 23/44] fix: gno.mod tidy --- pool/gno.mod | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/pool/gno.mod b/pool/gno.mod index a4197a6cc..3c88fce34 100644 --- a/pool/gno.mod +++ b/pool/gno.mod @@ -1,14 +1 @@ module gno.land/r/gnoswap/v1/pool - -require ( - gno.land/p/demo/json v0.0.0-latest - gno.land/p/demo/ufmt v0.0.0-latest - gno.land/p/demo/users v0.0.0-latest - gno.land/p/gnoswap/int256 v0.0.0-latest - gno.land/p/gnoswap/pool v0.0.0-latest - gno.land/p/gnoswap/uint256 v0.0.0-latest - gno.land/r/gnoswap/v1/common v0.0.0-latest - gno.land/r/gnoswap/v1/consts v0.0.0-latest - gno.land/r/gnoswap/v1/emission v0.0.0-latest - gno.land/r/gnoswap/v1/gns v0.0.0-latest -) From db311cda48bde55685583265240ea0058287789b Mon Sep 17 00:00:00 2001 From: n3wbie Date: Tue, 10 Dec 2024 12:39:21 +0900 Subject: [PATCH 24/44] fix: missing guard logic for HandleWithdrawalFee --- pool/protocol_fee_withdrawal.gno | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pool/protocol_fee_withdrawal.gno b/pool/protocol_fee_withdrawal.gno index bf55e2c2c..cacc602c7 100644 --- a/pool/protocol_fee_withdrawal.gno +++ b/pool/protocol_fee_withdrawal.gno @@ -46,6 +46,8 @@ func HandleWithdrawalFee( positionCaller std.Address, ) (string, string) { // uint256 x2 common.IsHalted() + common.MustRegistered(token0Path) + common.MustRegistered(token1Path) // only position contract can call this function caller := std.PrevRealm().Addr() From 5e84d81ec5466d41eec34017e02e5b7939654dfa Mon Sep 17 00:00:00 2001 From: n3wbie Date: Tue, 10 Dec 2024 12:40:04 +0900 Subject: [PATCH 25/44] fix: failing testcase --- pool/_helper_test.gno | 117 -------------------------- pool/pool_manager_test.gno | 30 ++++--- pool/protocol_fee_withdrawal_test.gno | 15 ++-- 3 files changed, 24 insertions(+), 138 deletions(-) diff --git a/pool/_helper_test.gno b/pool/_helper_test.gno index ce061a777..5fc7ed5b9 100644 --- a/pool/_helper_test.gno +++ b/pool/_helper_test.gno @@ -45,123 +45,6 @@ const ( addr02 = testutils.TestAddress("addr02") ) -type WugnotToken struct{} - -func (WugnotToken) Transfer() func(to pusers.AddressOrName, amount uint64) { - return wugnot.Transfer -} -func (WugnotToken) TransferFrom() func(from, to pusers.AddressOrName, amount uint64) { - return wugnot.TransferFrom -} -func (WugnotToken) BalanceOf() func(owner pusers.AddressOrName) uint64 { - return wugnot.BalanceOf -} -func (WugnotToken) Approve() func(spender pusers.AddressOrName, amount uint64) { - return wugnot.Approve -} - -type GNSToken struct{} - -func (GNSToken) Transfer() func(to pusers.AddressOrName, amount uint64) { - return gns.Transfer -} -func (GNSToken) TransferFrom() func(from, to pusers.AddressOrName, amount uint64) { - return gns.TransferFrom -} -func (GNSToken) BalanceOf() func(owner pusers.AddressOrName) uint64 { - return gns.BalanceOf -} -func (GNSToken) Approve() func(spender pusers.AddressOrName, amount uint64) { - return gns.Approve -} - -type BarToken struct{} - -func (BarToken) Transfer() func(to pusers.AddressOrName, amount uint64) { - return bar.Transfer -} -func (BarToken) TransferFrom() func(from, to pusers.AddressOrName, amount uint64) { - return bar.TransferFrom -} -func (BarToken) BalanceOf() func(owner pusers.AddressOrName) uint64 { - return bar.BalanceOf -} -func (BarToken) Approve() func(spender pusers.AddressOrName, amount uint64) { - return bar.Approve -} - -type BazToken struct{} - -func (BazToken) Transfer() func(to pusers.AddressOrName, amount uint64) { - return baz.Transfer -} -func (BazToken) TransferFrom() func(from, to pusers.AddressOrName, amount uint64) { - return baz.TransferFrom -} -func (BazToken) BalanceOf() func(owner pusers.AddressOrName) uint64 { - return baz.BalanceOf -} -func (BazToken) Approve() func(spender pusers.AddressOrName, amount uint64) { - return baz.Approve -} - -type FooToken struct{} - -func (FooToken) Transfer() func(to pusers.AddressOrName, amount uint64) { - return foo.Transfer -} -func (FooToken) TransferFrom() func(from, to pusers.AddressOrName, amount uint64) { - return foo.TransferFrom -} -func (FooToken) BalanceOf() func(owner pusers.AddressOrName) uint64 { - return foo.BalanceOf -} -func (FooToken) Approve() func(spender pusers.AddressOrName, amount uint64) { - return foo.Approve -} - -type OBLToken struct{} - -func (OBLToken) Transfer() func(to pusers.AddressOrName, amount uint64) { - return obl.Transfer -} -func (OBLToken) TransferFrom() func(from, to pusers.AddressOrName, amount uint64) { - return obl.TransferFrom -} -func (OBLToken) BalanceOf() func(owner pusers.AddressOrName) uint64 { - return obl.BalanceOf -} -func (OBLToken) Approve() func(spender pusers.AddressOrName, amount uint64) { - return obl.Approve -} - -type QuxToken struct{} - -func (QuxToken) Transfer() func(to pusers.AddressOrName, amount uint64) { - return qux.Transfer -} -func (QuxToken) TransferFrom() func(from, to pusers.AddressOrName, amount uint64) { - return qux.TransferFrom -} -func (QuxToken) BalanceOf() func(owner pusers.AddressOrName) uint64 { - return qux.BalanceOf -} -func (QuxToken) Approve() func(spender pusers.AddressOrName, amount uint64) { - return qux.Approve -} - -func init() { - std.TestSetRealm(std.NewUserRealm(consts.TOKEN_REGISTER)) - - RegisterGRC20Interface(wugnotPath, WugnotToken{}) - RegisterGRC20Interface(gnsPath, GNSToken{}) - RegisterGRC20Interface(barPath, BarToken{}) - RegisterGRC20Interface(bazPath, BazToken{}) - RegisterGRC20Interface(fooPath, FooToken{}) - RegisterGRC20Interface(oblPath, OBLToken{}) - RegisterGRC20Interface(quxPath, QuxToken{}) -} - var ( admin = pusers.AddressOrName(consts.ADMIN) alice = pusers.AddressOrName(testutils.TestAddress("alice")) diff --git a/pool/pool_manager_test.gno b/pool/pool_manager_test.gno index da1eed7c1..0a9776dcf 100644 --- a/pool/pool_manager_test.gno +++ b/pool/pool_manager_test.gno @@ -74,13 +74,13 @@ func TestNewPoolParams(t *testing.T) { } func TestGetPoolPath(t *testing.T) { - path := GetPoolPath("tokenA", "tokenB", 500) - expected := "tokenA:tokenB:500" + path := GetPoolPath("gno.land/r/onbloc/bar", "gno.land/r/onbloc/foo", 500) + expected := "gno.land/r/onbloc/bar:gno.land/r/onbloc/foo:500" if path != expected { t.Errorf("Expected path %s, got %s", expected, path) } - path = GetPoolPath("tokenB", "tokenA", 500) + path = GetPoolPath("gno.land/r/onbloc/foo", "gno.land/r/onbloc/bar", 500) if path != expected { t.Errorf("Expected tokens to be sorted, expected %s, got %s", expected, path) } @@ -118,37 +118,37 @@ func TestCreatePool(t *testing.T) { }{ { name: "success - normal token pair", - token0Path: "test/token0", - token1Path: "test/token1", + token0Path: "gno.land/r/onbloc/bar", + token1Path: "gno.land/r/onbloc/foo", fee: 3000, sqrtPrice: "4295128740", }, { name: "fail - same tokens", - token0Path: "test/token0", - token1Path: "test/token0", + token0Path: "gno.land/r/onbloc/bar", + token1Path: "gno.land/r/onbloc/bar", fee: 3000, sqrtPrice: "4295128740", shouldPanic: true, - panicMsg: "[GNOSWAP-POOL-011] same token used in single pool || pool_manager.gno__CreatePool() || expected token0Path(test/token0) != token1Path(test/token0", + panicMsg: "[GNOSWAP-POOL-011] same token used in single pool || pool_manager.gno__CreatePool() || expected token0Path(gno.land/r/onbloc/bar) != token1Path(gno.land/r/onbloc/bar", }, { name: "fail - tokens not in order", - token0Path: "test/tokenB", - token1Path: "test/tokenA", + token0Path: "gno.land/r/onbloc/foo", + token1Path: "gno.land/r/onbloc/bar", fee: 3000, sqrtPrice: "4295128740", shouldPanic: true, - panicMsg: "[GNOSWAP-POOL-012] tokens must be in lexicographical order || pool_manager.gno__CreatePool() || expected token0Path(test/tokenB) < token1Path(test/tokenA)", + panicMsg: "[GNOSWAP-POOL-012] tokens must be in lexicographical order || pool_manager.gno__CreatePool() || expected token0Path(gno.land/r/onbloc/foo) < token1Path(gno.land/r/onbloc/bar)", }, { name: "fail - pool already exists", - token0Path: "test/token0", - token1Path: "test/token1", + token0Path: "gno.land/r/onbloc/bar", + token1Path: "gno.land/r/onbloc/foo", fee: 3000, sqrtPrice: "4295128740", shouldPanic: true, - panicMsg: "[GNOSWAP-POOL-013] pool already created || pool_manager.gno__CreatePool() || expected poolPath(test/token0:test/token1:3000) not to exist", + panicMsg: "[GNOSWAP-POOL-013] pool already created || pool_manager.gno__CreatePool() || expected poolPath(gno.land/r/onbloc/bar:gno.land/r/onbloc/foo:3000) not to exist", }, } @@ -199,4 +199,6 @@ func TestCreatePool(t *testing.T) { } }) } + + resetObject(t) } diff --git a/pool/protocol_fee_withdrawal_test.gno b/pool/protocol_fee_withdrawal_test.gno index 6cbec74e9..241459d91 100644 --- a/pool/protocol_fee_withdrawal_test.gno +++ b/pool/protocol_fee_withdrawal_test.gno @@ -1,16 +1,17 @@ package pool import ( + "std" + "strconv" + "strings" + "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" pn "gno.land/r/gnoswap/v1/position" - "std" - "strconv" - "strings" - "testing" ) func TestHandleWithdrawalFee(t *testing.T) { @@ -32,7 +33,7 @@ func TestHandleWithdrawalFee(t *testing.T) { name: "Panic if caller is not position contract", action: func(t *testing.T) { std.TestSetOrigCaller(users.Resolve(admin)) - HandleWithdrawalFee(0, "", "0", "", "0", "", users.Resolve(admin)) + HandleWithdrawalFee(0, "gno.land/r/onbloc/foo", "0", "gno.land/r/onbloc/foo", "0", "", users.Resolve(admin)) }, verify: nil, expected: "[GNOSWAP-POOL-001] caller has no permission || withdrawal_fee.gno__HandleWithdrawalFee() || only position(g1q646ctzhvn60v492x8ucvyqnrj2w30cwh6efk5) can call this function, called from g17290cwvmrapvp869xfnhhawa8sm9edpufzat7d", @@ -45,7 +46,7 @@ func TestHandleWithdrawalFee(t *testing.T) { HandleWithdrawalFee(0, "pkgPath", "1000", "pkgPath", "1000", "poolPath", users.Resolve(admin)) }, verify: nil, - expected: "[GNOSWAP-POOL-002] not registered token || token_register.gno__transferFromByRegisterCall() || token(pkgPath) not registered", + expected: "[GNOSWAP-COMMON-004] token is not registered || token(pkgPath) is not registered", shouldPanic: true, }, { @@ -63,7 +64,7 @@ func TestHandleWithdrawalFee(t *testing.T) { HandleWithdrawalFee(1, wugnotPath, "1000", gnsPath, "1000", poolPath, users.Resolve(alice)) }, verify: nil, - expected: "insufficient allowance", + expected: "[GNOSWAP-POOL-021] token transfer failed || insufficient allowance", shouldPanic: true, }, { From dca84d4b05601cff7dd80c2dac11badac51e3022 Mon Sep 17 00:00:00 2001 From: n3wbie Date: Tue, 10 Dec 2024 16:11:08 +0900 Subject: [PATCH 26/44] fix: tc --- pool/position_modify_test.gno | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pool/position_modify_test.gno b/pool/position_modify_test.gno index dec483e61..a6c2da6d0 100644 --- a/pool/position_modify_test.gno +++ b/pool/position_modify_test.gno @@ -129,8 +129,8 @@ func TestModifyPositionEdgeCases(t *testing.T) { params.liquidityDelta = i256.MustFromDecimal("-100000000") _, amount0, amount1 := pool.modifyPosition(params) - // remove amount should be negative value of added amount - uassert.Equal(t, amount0.ToString(), "-8040315") - uassert.Equal(t, amount1.ToString(), "-2958014") + // remove amount should be same as added amount + uassert.Equal(t, amount0.ToString(), "8040315") + uassert.Equal(t, amount1.ToString(), "2958014") }) } From de5f079f10dd7fa24b3117f593264d7ab2ce45e8 Mon Sep 17 00:00:00 2001 From: n3wbie Date: Tue, 10 Dec 2024 16:11:47 +0900 Subject: [PATCH 27/44] feat: use teller to transfer/transferfrom --- pool/pool.gno | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/pool/pool.gno b/pool/pool.gno index 3980adcf4..ec96d53d0 100644 --- a/pool/pool.gno +++ b/pool/pool.gno @@ -151,12 +151,10 @@ func Collect( var amount0, amount1 *u256.Uint - var amount0, amount1 *u256.Uint - // Smallest of three: amount0Requested, position.tokensOwed0, pool.balances.token0 amount0Req := u256.MustFromDecimal(amount0Requested) amount0, position.tokensOwed0, pool.balances.token0 = collectToken(amount0Req, position.tokensOwed0, pool.balances.token0) - token0 := common.GetToken(pool.token0Path) + token0 := common.GetTokenTeller(pool.token0Path) checkTransferError(token0.Transfer(recipient, amount0.Uint64())) // Smallest of three: amount0Requested, position.tokensOwed0, pool.balances.token0 @@ -166,7 +164,7 @@ func Collect( // Update state first then transfer position.tokensOwed1 = new(u256.Uint).Sub(position.tokensOwed1, amount1) pool.balances.token1 = new(u256.Uint).Sub(pool.balances.token1, amount1) - token1 := common.GetToken(pool.token1Path) + token1 := common.GetTokenTeller(pool.token1Path) checkTransferError(token1.Transfer(recipient, amount1.Uint64())) pool.positions[positionKey] = position @@ -739,8 +737,8 @@ func CollectProtocolByAdmin( token1Path string, fee uint32, recipient std.Address, - _amount0Requested string, // uint128 - _amount1Requested string, // uint128 + amount0Requested string, // uint128 + amount1Requested string, // uint128 ) (string, string) { // uint128 x2 common.MustRegistered(token0Path) common.MustRegistered(token1Path) @@ -784,8 +782,8 @@ func CollectProtocol( token1Path string, fee uint32, recipient std.Address, - _amount0Requested string, // uint128 - _amount1Requested string, // uint128 + amount0Requested string, // uint128 + amount1Requested string, // uint128 ) (string, string) { // uint128 x2 common.MustRegistered(token0Path) common.MustRegistered(token1Path) @@ -897,7 +895,8 @@ func (pool *Pool) transferAndVerify( panic(err) } - transferByRegisterCall(tokenPath, to, amountUint64) + token := common.GetTokenTeller(tokenPath) + checkTransferError(token.Transfer(to, amountUint64)) newBalance, err := updatePoolBalance(token0, token1, absAmount, isToken0) if err != nil { From 89350f80d46052be15b5bc4ca993dbb86695b8c9 Mon Sep 17 00:00:00 2001 From: n3wbie Date: Tue, 10 Dec 2024 16:12:09 +0900 Subject: [PATCH 28/44] test: fix mocking old functions --- pool/pool_test.gno | 33 ++++++++++++++++++--------------- 1 file changed, 18 insertions(+), 15 deletions(-) diff --git a/pool/pool_test.gno b/pool/pool_test.gno index af02987d9..c6ef82d46 100644 --- a/pool/pool_test.gno +++ b/pool/pool_test.gno @@ -6,8 +6,10 @@ import ( "gno.land/p/demo/testutils" "gno.land/p/demo/uassert" + i256 "gno.land/p/gnoswap/int256" u256 "gno.land/p/gnoswap/uint256" + "gno.land/r/gnoswap/v1/consts" ) @@ -576,7 +578,7 @@ func TestTransferFromAndVerify(t *testing.T) { }, from: testutils.TestAddress("from_addr"), to: testutils.TestAddress("to_addr"), - tokenPath: "token0_path", + tokenPath: "gno.land/r/onbloc/bar", amount: i256.NewInt(500), isToken0: true, expectedBal0: u256.NewUint(1500), // 1000 + 500 @@ -592,7 +594,7 @@ func TestTransferFromAndVerify(t *testing.T) { }, from: testutils.TestAddress("from_addr"), to: testutils.TestAddress("to_addr"), - tokenPath: "token1_path", + tokenPath: "gno.land/r/onbloc/foo", amount: i256.NewInt(800), isToken0: false, expectedBal0: u256.NewUint(1000), // unchanged @@ -608,7 +610,7 @@ func TestTransferFromAndVerify(t *testing.T) { }, from: testutils.TestAddress("from_addr"), to: testutils.TestAddress("to_addr"), - tokenPath: "token0_path", + tokenPath: "gno.land/r/onbloc/bar", amount: i256.NewInt(0), isToken0: true, expectedBal0: u256.NewUint(1000), // unchanged @@ -618,13 +620,14 @@ func TestTransferFromAndVerify(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - // mock transferFromByRegisterCall - oldTransferFromByRegisterCall := transferFromByRegisterCall - defer func() { transferFromByRegisterCall = oldTransferFromByRegisterCall }() - transferFromByRegisterCall = func(tokenPath string, from, to std.Address, amount uint64) bool { - // mock the transfer (just return true) - return true + oldCheckTransferError := checkTransferError + defer func() { + checkTransferError = oldCheckTransferError + }() + + checkTransferError = func(err error) { + return } tt.pool.transferFromAndVerify(tt.from, tt.to, tt.tokenPath, u256.MustFromDecimal(tt.amount.ToString()), tt.isToken0) @@ -651,18 +654,18 @@ func TestTransferFromAndVerify(t *testing.T) { }, } - oldTransferFromByRegisterCall := transferFromByRegisterCall - defer func() { transferFromByRegisterCall = oldTransferFromByRegisterCall }() + oldCheckTransferError := checkTransferError + defer func() { checkTransferError = oldCheckTransferError }() - transferFromByRegisterCall = func(tokenPath string, from, to std.Address, amount uint64) bool { - return true + checkTransferError = func(err error) { + return } negativeAmount := i256.NewInt(-500) pool.transferFromAndVerify( testutils.TestAddress("from_addr"), testutils.TestAddress("to_addr"), - "token0_path", + "gno.land/r/onbloc/qux", u256.MustFromDecimal(negativeAmount.Abs().ToString()), true, ) @@ -694,7 +697,7 @@ func TestTransferFromAndVerify(t *testing.T) { pool.transferFromAndVerify( testutils.TestAddress("from_addr"), testutils.TestAddress("to_addr"), - "token0_path", + "gno.land/r/onbloc/qux", u256.MustFromDecimal(hugeAmount.ToString()), true, ) From 5965d6dbcf8e5d423f96cf33c14027e45b415da2 Mon Sep 17 00:00:00 2001 From: Blake <104744707+r3v4s@users.noreply.github.com> Date: Tue, 10 Dec 2024 19:04:45 +0900 Subject: [PATCH 29/44] GSW-1838 feat: grc20reg (#412) * GSW-1838 feat: grc20reg - replace previous token_register to latest grc20reg * feat: update grc20 spec for grc20reg * hotfix: update bar token spec * fix: typo * chore: remove grc20reg * test: txtar for approve & transferfrom using grc20reg --- .../grc20reg/approve_transferfrom.txtar | 53 ++++++++++++++++++ __local/grc20_tokens/onbloc/bar/bar.gno | 36 ++++++------ __local/grc20_tokens/onbloc/bar/gno.mod | 1 + __local/grc20_tokens/onbloc/baz/baz.gno | 36 ++++++------ __local/grc20_tokens/onbloc/baz/gno.mod | 1 + __local/grc20_tokens/onbloc/foo/foo.gno | 36 ++++++------ __local/grc20_tokens/onbloc/foo/gno.mod | 1 + __local/grc20_tokens/onbloc/obl/gno.mod | 1 + __local/grc20_tokens/onbloc/obl/obl.gno | 36 ++++++------ __local/grc20_tokens/onbloc/qux/gno.mod | 1 + __local/grc20_tokens/onbloc/qux/qux.gno | 36 ++++++------ __local/grc20_tokens/onbloc/usdc/gno.mod | 1 + __local/grc20_tokens/onbloc/usdc/usdc.gno | 36 ++++++------ _deploy/r/gnoswap/gns/gno.mod | 1 + _deploy/r/gnoswap/gns/gns.gno | 55 ++++++++++--------- 15 files changed, 209 insertions(+), 122 deletions(-) create mode 100644 __local/grc20_tokens/grc20reg/approve_transferfrom.txtar diff --git a/__local/grc20_tokens/grc20reg/approve_transferfrom.txtar b/__local/grc20_tokens/grc20reg/approve_transferfrom.txtar new file mode 100644 index 000000000..ff43ac448 --- /dev/null +++ b/__local/grc20_tokens/grc20reg/approve_transferfrom.txtar @@ -0,0 +1,53 @@ +loadpkg gno.land/p/demo/users + +loadpkg gno.land/r/demo/foo20 +loadpkg gno.land/r/demo/grc20reg + +loadpkg gno.land/r/demo/reg $WORK/reg + +## start a new node +gnoland start + +## faucet +# gnokey maketx call -pkgpath gno.land/r/demo/foo20 -func Faucet -gas-fee 1ugnot -gas-wanted 4000000 -broadcast -chainid=tendermint_test test1 + +## print reg addr +gnokey maketx call -pkgpath gno.land/r/demo/reg -func RelamAddr -gas-fee 1ugnot -gas-wanted 4000000 -broadcast -chainid=tendermint_test test1 +stdout 'g19tlskvga928es8ug2empargp0teul03apzjud9' + +## approve +gnokey maketx call -pkgpath gno.land/r/demo/foo20 -func Approve -args 'g19tlskvga928es8ug2empargp0teul03apzjud9' -args '100' -gas-fee 1ugnot -gas-wanted 4000000 -broadcast -chainid=tendermint_test test1 + +## transfer from +gnokey maketx call -pkgpath gno.land/r/demo/reg -func TransferFromWithReg -gas-fee 1ugnot -gas-wanted 4000000 -broadcast -chainid=tendermint_test test1 +stdout '' + +-- reg/reg.gno -- +package reg + +import ( + "std" + + "gno.land/r/demo/grc20reg" +) + +func RelamAddr() string { + addr := std.CurrentRealm().Addr().String() + return addr +} + +func TransferFromWithReg() { + caller := std.PrevRealm().Addr() + curr := std.CurrentRealm().Addr() + + + // using import + // foo20.TransferFrom(uCaller, uCurr, uint64(100)) + + // using grc20reg + fooTokenGetter := grc20reg.Get("gno.land/r/demo/foo20") + fooToken := fooTokenGetter() + userTeller := fooToken.CallerTeller() + + userTeller.TransferFrom(caller, curr, uint64(100)) +} \ No newline at end of file diff --git a/__local/grc20_tokens/onbloc/bar/bar.gno b/__local/grc20_tokens/onbloc/bar/bar.gno index c759395b3..d3d998e8a 100644 --- a/__local/grc20_tokens/onbloc/bar/bar.gno +++ b/__local/grc20_tokens/onbloc/bar/bar.gno @@ -7,54 +7,58 @@ import ( "gno.land/p/demo/ownable" "gno.land/p/demo/ufmt" pusers "gno.land/p/demo/users" + + "gno.land/r/demo/grc20reg" "gno.land/r/demo/users" ) var ( - admin *ownable.Ownable - token *grc20.Token - ledger *grc20.PrivateLedger + Token, privateLedger = grc20.NewToken("Bar", "BAR", 6) + UserTeller = Token.CallerTeller() + owner = ownable.NewWithAddress("g17290cwvmrapvp869xfnhhawa8sm9edpufzat7d") // ADMIN ) func init() { - admin = ownable.NewWithAddress("g17290cwvmrapvp869xfnhhawa8sm9edpufzat7d") // ADMIN - token, ledger = grc20.NewToken("Bar", "BAR", 6) - ledger.Mint(admin.Owner(), 100_000_000_000_000) + privateLedger.Mint(owner.Owner(), 100_000_000_000_000) + getter := func() *grc20.Token { return Token } + grc20reg.Register(getter, "") } -func TotalSupply() uint64 { return token.TotalSupply() } +func TotalSupply() uint64 { + return UserTeller.TotalSupply() +} func BalanceOf(owner pusers.AddressOrName) uint64 { ownerAddr := users.Resolve(owner) - return token.BalanceOf(ownerAddr) + return UserTeller.BalanceOf(ownerAddr) } func Allowance(owner, spender pusers.AddressOrName) uint64 { ownerAddr := users.Resolve(owner) spenderAddr := users.Resolve(spender) - return token.Allowance(ownerAddr, spenderAddr) + return UserTeller.Allowance(ownerAddr, spenderAddr) } func Transfer(to pusers.AddressOrName, amount uint64) { toAddr := users.Resolve(to) - checkErr(token.CallerTeller().Transfer(toAddr, amount)) + checkErr(UserTeller.Transfer(toAddr, amount)) } func Approve(spender pusers.AddressOrName, amount uint64) { spenderAddr := users.Resolve(spender) - checkErr(token.CallerTeller().Approve(spenderAddr, amount)) + checkErr(UserTeller.Approve(spenderAddr, amount)) } func TransferFrom(from, to pusers.AddressOrName, amount uint64) { fromAddr := users.Resolve(from) toAddr := users.Resolve(to) - checkErr(token.CallerTeller().TransferFrom(fromAddr, toAddr, amount)) + checkErr(UserTeller.TransferFrom(fromAddr, toAddr, amount)) } func Burn(from pusers.AddressOrName, amount uint64) { - admin.AssertCallerIsOwner() + owner.AssertCallerIsOwner() fromAddr := users.Resolve(from) - checkErr(ledger.Burn(fromAddr, amount)) + checkErr(privateLedger.Burn(fromAddr, amount)) } func Render(path string) string { @@ -63,11 +67,11 @@ func Render(path string) string { switch { case path == "": - return token.RenderHome() + return Token.RenderHome() case c == 2 && parts[0] == "balance": owner := pusers.AddressOrName(parts[1]) ownerAddr := users.Resolve(owner) - balance := token.BalanceOf(ownerAddr) + balance := UserTeller.BalanceOf(ownerAddr) return ufmt.Sprintf("%d\n", balance) default: return "404\n" diff --git a/__local/grc20_tokens/onbloc/bar/gno.mod b/__local/grc20_tokens/onbloc/bar/gno.mod index 8ed87d516..4e18b2676 100644 --- a/__local/grc20_tokens/onbloc/bar/gno.mod +++ b/__local/grc20_tokens/onbloc/bar/gno.mod @@ -6,4 +6,5 @@ require ( gno.land/p/demo/ufmt v0.0.0-latest gno.land/p/demo/users v0.0.0-latest gno.land/r/demo/users v0.0.0-latest + gno.land/r/demo/grc20reg v0.0.0-latest ) diff --git a/__local/grc20_tokens/onbloc/baz/baz.gno b/__local/grc20_tokens/onbloc/baz/baz.gno index ad003225c..c1dbdca1e 100644 --- a/__local/grc20_tokens/onbloc/baz/baz.gno +++ b/__local/grc20_tokens/onbloc/baz/baz.gno @@ -7,54 +7,58 @@ import ( "gno.land/p/demo/ownable" "gno.land/p/demo/ufmt" pusers "gno.land/p/demo/users" + + "gno.land/r/demo/grc20reg" "gno.land/r/demo/users" ) var ( - admin *ownable.Ownable - token *grc20.Token - ledger *grc20.PrivateLedger + Token, privateLedger = grc20.NewToken("Baz", "BAZ", 6) + UserTeller = Token.CallerTeller() + owner = ownable.NewWithAddress("g17290cwvmrapvp869xfnhhawa8sm9edpufzat7d") // ADMIN ) func init() { - admin = ownable.NewWithAddress("g17290cwvmrapvp869xfnhhawa8sm9edpufzat7d") // ADMIN - token, ledger = grc20.NewToken("Baz", "BAZ", 6) - ledger.Mint(admin.Owner(), 100_000_000_000_000) + privateLedger.Mint(owner.Owner(), 100_000_000_000_000) + getter := func() *grc20.Token { return Token } + grc20reg.Register(getter, "") } -func TotalSupply() uint64 { return token.TotalSupply() } +func TotalSupply() uint64 { + return UserTeller.TotalSupply() +} func BalanceOf(owner pusers.AddressOrName) uint64 { ownerAddr := users.Resolve(owner) - return token.BalanceOf(ownerAddr) + return UserTeller.BalanceOf(ownerAddr) } func Allowance(owner, spender pusers.AddressOrName) uint64 { ownerAddr := users.Resolve(owner) spenderAddr := users.Resolve(spender) - return token.Allowance(ownerAddr, spenderAddr) + return UserTeller.Allowance(ownerAddr, spenderAddr) } func Transfer(to pusers.AddressOrName, amount uint64) { toAddr := users.Resolve(to) - checkErr(token.CallerTeller().Transfer(toAddr, amount)) + checkErr(UserTeller.Transfer(toAddr, amount)) } func Approve(spender pusers.AddressOrName, amount uint64) { spenderAddr := users.Resolve(spender) - checkErr(token.CallerTeller().Approve(spenderAddr, amount)) + checkErr(UserTeller.Approve(spenderAddr, amount)) } func TransferFrom(from, to pusers.AddressOrName, amount uint64) { fromAddr := users.Resolve(from) toAddr := users.Resolve(to) - checkErr(token.CallerTeller().TransferFrom(fromAddr, toAddr, amount)) + checkErr(UserTeller.TransferFrom(fromAddr, toAddr, amount)) } func Burn(from pusers.AddressOrName, amount uint64) { - admin.AssertCallerIsOwner() + owner.AssertCallerIsOwner() fromAddr := users.Resolve(from) - checkErr(ledger.Burn(fromAddr, amount)) + checkErr(privateLedger.Burn(fromAddr, amount)) } func Render(path string) string { @@ -63,11 +67,11 @@ func Render(path string) string { switch { case path == "": - return token.RenderHome() + return Token.RenderHome() case c == 2 && parts[0] == "balance": owner := pusers.AddressOrName(parts[1]) ownerAddr := users.Resolve(owner) - balance := token.BalanceOf(ownerAddr) + balance := UserTeller.BalanceOf(ownerAddr) return ufmt.Sprintf("%d\n", balance) default: return "404\n" diff --git a/__local/grc20_tokens/onbloc/baz/gno.mod b/__local/grc20_tokens/onbloc/baz/gno.mod index 6d1b8cd2c..05230b44d 100644 --- a/__local/grc20_tokens/onbloc/baz/gno.mod +++ b/__local/grc20_tokens/onbloc/baz/gno.mod @@ -6,4 +6,5 @@ require ( gno.land/p/demo/ufmt v0.0.0-latest gno.land/p/demo/users v0.0.0-latest gno.land/r/demo/users v0.0.0-latest + gno.land/r/demo/grc20reg v0.0.0-latest ) diff --git a/__local/grc20_tokens/onbloc/foo/foo.gno b/__local/grc20_tokens/onbloc/foo/foo.gno index 54451f3d4..e99ee1d95 100644 --- a/__local/grc20_tokens/onbloc/foo/foo.gno +++ b/__local/grc20_tokens/onbloc/foo/foo.gno @@ -7,54 +7,58 @@ import ( "gno.land/p/demo/ownable" "gno.land/p/demo/ufmt" pusers "gno.land/p/demo/users" + + "gno.land/r/demo/grc20reg" "gno.land/r/demo/users" ) var ( - admin *ownable.Ownable - token *grc20.Token - ledger *grc20.PrivateLedger + Token, privateLedger = grc20.NewToken("Baz", "BAZ", 6) + UserTeller = Token.CallerTeller() + owner = ownable.NewWithAddress("g17290cwvmrapvp869xfnhhawa8sm9edpufzat7d") // ADMIN ) func init() { - admin = ownable.NewWithAddress("g17290cwvmrapvp869xfnhhawa8sm9edpufzat7d") // ADMIN - token, ledger = grc20.NewToken("Foo", "FOO", 6) - ledger.Mint(admin.Owner(), 100_000_000_000_000) + privateLedger.Mint(owner.Owner(), 100_000_000_000_000) + getter := func() *grc20.Token { return Token } + grc20reg.Register(getter, "") } -func TotalSupply() uint64 { return token.TotalSupply() } +func TotalSupply() uint64 { + return UserTeller.TotalSupply() +} func BalanceOf(owner pusers.AddressOrName) uint64 { ownerAddr := users.Resolve(owner) - return token.BalanceOf(ownerAddr) + return UserTeller.BalanceOf(ownerAddr) } func Allowance(owner, spender pusers.AddressOrName) uint64 { ownerAddr := users.Resolve(owner) spenderAddr := users.Resolve(spender) - return token.Allowance(ownerAddr, spenderAddr) + return UserTeller.Allowance(ownerAddr, spenderAddr) } func Transfer(to pusers.AddressOrName, amount uint64) { toAddr := users.Resolve(to) - checkErr(token.CallerTeller().Transfer(toAddr, amount)) + checkErr(UserTeller.Transfer(toAddr, amount)) } func Approve(spender pusers.AddressOrName, amount uint64) { spenderAddr := users.Resolve(spender) - checkErr(token.CallerTeller().Approve(spenderAddr, amount)) + checkErr(UserTeller.Approve(spenderAddr, amount)) } func TransferFrom(from, to pusers.AddressOrName, amount uint64) { fromAddr := users.Resolve(from) toAddr := users.Resolve(to) - checkErr(token.CallerTeller().TransferFrom(fromAddr, toAddr, amount)) + checkErr(UserTeller.TransferFrom(fromAddr, toAddr, amount)) } func Burn(from pusers.AddressOrName, amount uint64) { - admin.AssertCallerIsOwner() + owner.AssertCallerIsOwner() fromAddr := users.Resolve(from) - checkErr(ledger.Burn(fromAddr, amount)) + checkErr(privateLedger.Burn(fromAddr, amount)) } func Render(path string) string { @@ -63,11 +67,11 @@ func Render(path string) string { switch { case path == "": - return token.RenderHome() + return Token.RenderHome() case c == 2 && parts[0] == "balance": owner := pusers.AddressOrName(parts[1]) ownerAddr := users.Resolve(owner) - balance := token.BalanceOf(ownerAddr) + balance := UserTeller.BalanceOf(ownerAddr) return ufmt.Sprintf("%d\n", balance) default: return "404\n" diff --git a/__local/grc20_tokens/onbloc/foo/gno.mod b/__local/grc20_tokens/onbloc/foo/gno.mod index bdd7de698..42e302af2 100644 --- a/__local/grc20_tokens/onbloc/foo/gno.mod +++ b/__local/grc20_tokens/onbloc/foo/gno.mod @@ -6,4 +6,5 @@ require ( gno.land/p/demo/ufmt v0.0.0-latest gno.land/p/demo/users v0.0.0-latest gno.land/r/demo/users v0.0.0-latest + gno.land/r/demo/grc20reg v0.0.0-latest ) diff --git a/__local/grc20_tokens/onbloc/obl/gno.mod b/__local/grc20_tokens/onbloc/obl/gno.mod index a45fb79bb..ffdeb5df8 100644 --- a/__local/grc20_tokens/onbloc/obl/gno.mod +++ b/__local/grc20_tokens/onbloc/obl/gno.mod @@ -6,4 +6,5 @@ require ( gno.land/p/demo/ufmt v0.0.0-latest gno.land/p/demo/users v0.0.0-latest gno.land/r/demo/users v0.0.0-latest + gno.land/r/demo/grc20reg v0.0.0-latest ) diff --git a/__local/grc20_tokens/onbloc/obl/obl.gno b/__local/grc20_tokens/onbloc/obl/obl.gno index 890debb65..976cc088a 100644 --- a/__local/grc20_tokens/onbloc/obl/obl.gno +++ b/__local/grc20_tokens/onbloc/obl/obl.gno @@ -7,54 +7,58 @@ import ( "gno.land/p/demo/ownable" "gno.land/p/demo/ufmt" pusers "gno.land/p/demo/users" + + "gno.land/r/demo/grc20reg" "gno.land/r/demo/users" ) var ( - admin *ownable.Ownable - token *grc20.Token - ledger *grc20.PrivateLedger + Token, privateLedger = grc20.NewToken("Obl", "OBL", 6) + UserTeller = Token.CallerTeller() + owner = ownable.NewWithAddress("g17290cwvmrapvp869xfnhhawa8sm9edpufzat7d") // ADMIN ) func init() { - admin = ownable.NewWithAddress("g17290cwvmrapvp869xfnhhawa8sm9edpufzat7d") // ADMIN - token, ledger = grc20.NewToken("Obl", "OBL", 6) - ledger.Mint(admin.Owner(), 100_000_000_000_000) + privateLedger.Mint(owner.Owner(), 100_000_000_000_000) + getter := func() *grc20.Token { return Token } + grc20reg.Register(getter, "") } -func TotalSupply() uint64 { return token.TotalSupply() } +func TotalSupply() uint64 { + return UserTeller.TotalSupply() +} func BalanceOf(owner pusers.AddressOrName) uint64 { ownerAddr := users.Resolve(owner) - return token.BalanceOf(ownerAddr) + return UserTeller.BalanceOf(ownerAddr) } func Allowance(owner, spender pusers.AddressOrName) uint64 { ownerAddr := users.Resolve(owner) spenderAddr := users.Resolve(spender) - return token.Allowance(ownerAddr, spenderAddr) + return UserTeller.Allowance(ownerAddr, spenderAddr) } func Transfer(to pusers.AddressOrName, amount uint64) { toAddr := users.Resolve(to) - checkErr(token.CallerTeller().Transfer(toAddr, amount)) + checkErr(UserTeller.Transfer(toAddr, amount)) } func Approve(spender pusers.AddressOrName, amount uint64) { spenderAddr := users.Resolve(spender) - checkErr(token.CallerTeller().Approve(spenderAddr, amount)) + checkErr(UserTeller.Approve(spenderAddr, amount)) } func TransferFrom(from, to pusers.AddressOrName, amount uint64) { fromAddr := users.Resolve(from) toAddr := users.Resolve(to) - checkErr(token.CallerTeller().TransferFrom(fromAddr, toAddr, amount)) + checkErr(UserTeller.TransferFrom(fromAddr, toAddr, amount)) } func Burn(from pusers.AddressOrName, amount uint64) { - admin.AssertCallerIsOwner() + owner.AssertCallerIsOwner() fromAddr := users.Resolve(from) - checkErr(ledger.Burn(fromAddr, amount)) + checkErr(privateLedger.Burn(fromAddr, amount)) } func Render(path string) string { @@ -63,11 +67,11 @@ func Render(path string) string { switch { case path == "": - return token.RenderHome() + return Token.RenderHome() case c == 2 && parts[0] == "balance": owner := pusers.AddressOrName(parts[1]) ownerAddr := users.Resolve(owner) - balance := token.BalanceOf(ownerAddr) + balance := UserTeller.BalanceOf(ownerAddr) return ufmt.Sprintf("%d\n", balance) default: return "404\n" diff --git a/__local/grc20_tokens/onbloc/qux/gno.mod b/__local/grc20_tokens/onbloc/qux/gno.mod index 8e74cede7..1528a0331 100644 --- a/__local/grc20_tokens/onbloc/qux/gno.mod +++ b/__local/grc20_tokens/onbloc/qux/gno.mod @@ -6,4 +6,5 @@ require ( gno.land/p/demo/ufmt v0.0.0-latest gno.land/p/demo/users v0.0.0-latest gno.land/r/demo/users v0.0.0-latest + gno.land/r/demo/grc20reg v0.0.0-latest ) diff --git a/__local/grc20_tokens/onbloc/qux/qux.gno b/__local/grc20_tokens/onbloc/qux/qux.gno index 49585a9c5..29aaec1b6 100644 --- a/__local/grc20_tokens/onbloc/qux/qux.gno +++ b/__local/grc20_tokens/onbloc/qux/qux.gno @@ -7,54 +7,58 @@ import ( "gno.land/p/demo/ownable" "gno.land/p/demo/ufmt" pusers "gno.land/p/demo/users" + + "gno.land/r/demo/grc20reg" "gno.land/r/demo/users" ) var ( - admin *ownable.Ownable - token *grc20.Token - ledger *grc20.PrivateLedger + Token, privateLedger = grc20.NewToken("Qux", "QUX", 6) + UserTeller = Token.CallerTeller() + owner = ownable.NewWithAddress("g17290cwvmrapvp869xfnhhawa8sm9edpufzat7d") // ADMIN ) func init() { - admin = ownable.NewWithAddress("g17290cwvmrapvp869xfnhhawa8sm9edpufzat7d") // ADMIN - token, ledger = grc20.NewToken("Qux", "QUX", 6) - ledger.Mint(admin.Owner(), 100_000_000_000_000) + privateLedger.Mint(owner.Owner(), 100_000_000_000_000) + getter := func() *grc20.Token { return Token } + grc20reg.Register(getter, "") } -func TotalSupply() uint64 { return token.TotalSupply() } +func TotalSupply() uint64 { + return UserTeller.TotalSupply() +} func BalanceOf(owner pusers.AddressOrName) uint64 { ownerAddr := users.Resolve(owner) - return token.BalanceOf(ownerAddr) + return UserTeller.BalanceOf(ownerAddr) } func Allowance(owner, spender pusers.AddressOrName) uint64 { ownerAddr := users.Resolve(owner) spenderAddr := users.Resolve(spender) - return token.Allowance(ownerAddr, spenderAddr) + return UserTeller.Allowance(ownerAddr, spenderAddr) } func Transfer(to pusers.AddressOrName, amount uint64) { toAddr := users.Resolve(to) - checkErr(token.CallerTeller().Transfer(toAddr, amount)) + checkErr(UserTeller.Transfer(toAddr, amount)) } func Approve(spender pusers.AddressOrName, amount uint64) { spenderAddr := users.Resolve(spender) - checkErr(token.CallerTeller().Approve(spenderAddr, amount)) + checkErr(UserTeller.Approve(spenderAddr, amount)) } func TransferFrom(from, to pusers.AddressOrName, amount uint64) { fromAddr := users.Resolve(from) toAddr := users.Resolve(to) - checkErr(token.CallerTeller().TransferFrom(fromAddr, toAddr, amount)) + checkErr(UserTeller.TransferFrom(fromAddr, toAddr, amount)) } func Burn(from pusers.AddressOrName, amount uint64) { - admin.AssertCallerIsOwner() + owner.AssertCallerIsOwner() fromAddr := users.Resolve(from) - checkErr(ledger.Burn(fromAddr, amount)) + checkErr(privateLedger.Burn(fromAddr, amount)) } func Render(path string) string { @@ -63,11 +67,11 @@ func Render(path string) string { switch { case path == "": - return token.RenderHome() + return Token.RenderHome() case c == 2 && parts[0] == "balance": owner := pusers.AddressOrName(parts[1]) ownerAddr := users.Resolve(owner) - balance := token.BalanceOf(ownerAddr) + balance := UserTeller.BalanceOf(ownerAddr) return ufmt.Sprintf("%d\n", balance) default: return "404\n" diff --git a/__local/grc20_tokens/onbloc/usdc/gno.mod b/__local/grc20_tokens/onbloc/usdc/gno.mod index 25836f4de..13d6b5cbe 100644 --- a/__local/grc20_tokens/onbloc/usdc/gno.mod +++ b/__local/grc20_tokens/onbloc/usdc/gno.mod @@ -6,4 +6,5 @@ require ( gno.land/p/demo/ufmt v0.0.0-latest gno.land/p/demo/users v0.0.0-latest gno.land/r/demo/users v0.0.0-latest + gno.land/r/demo/grc20reg v0.0.0-latest ) diff --git a/__local/grc20_tokens/onbloc/usdc/usdc.gno b/__local/grc20_tokens/onbloc/usdc/usdc.gno index cd1db0df2..c0469bd1f 100644 --- a/__local/grc20_tokens/onbloc/usdc/usdc.gno +++ b/__local/grc20_tokens/onbloc/usdc/usdc.gno @@ -7,54 +7,58 @@ import ( "gno.land/p/demo/ownable" "gno.land/p/demo/ufmt" pusers "gno.land/p/demo/users" + + "gno.land/r/demo/grc20reg" "gno.land/r/demo/users" ) var ( - admin *ownable.Ownable - token *grc20.Token - ledger *grc20.PrivateLedger + Token, privateLedger = grc20.NewToken("Usd Coin", "USDC", 6) + UserTeller = Token.CallerTeller() + owner = ownable.NewWithAddress("g17290cwvmrapvp869xfnhhawa8sm9edpufzat7d") // ADMIN ) func init() { - admin = ownable.NewWithAddress("g17290cwvmrapvp869xfnhhawa8sm9edpufzat7d") // ADMIN - token, ledger = grc20.NewToken("Usd Coin", "USDC", 6) - ledger.Mint(admin.Owner(), 100_000_000_000_000) + privateLedger.Mint(owner.Owner(), 100_000_000_000_000) + getter := func() *grc20.Token { return Token } + grc20reg.Register(getter, "") } -func TotalSupply() uint64 { return token.TotalSupply() } +func TotalSupply() uint64 { + return UserTeller.TotalSupply() +} func BalanceOf(owner pusers.AddressOrName) uint64 { ownerAddr := users.Resolve(owner) - return token.BalanceOf(ownerAddr) + return UserTeller.BalanceOf(ownerAddr) } func Allowance(owner, spender pusers.AddressOrName) uint64 { ownerAddr := users.Resolve(owner) spenderAddr := users.Resolve(spender) - return token.Allowance(ownerAddr, spenderAddr) + return UserTeller.Allowance(ownerAddr, spenderAddr) } func Transfer(to pusers.AddressOrName, amount uint64) { toAddr := users.Resolve(to) - checkErr(token.CallerTeller().Transfer(toAddr, amount)) + checkErr(UserTeller.Transfer(toAddr, amount)) } func Approve(spender pusers.AddressOrName, amount uint64) { spenderAddr := users.Resolve(spender) - checkErr(token.CallerTeller().Approve(spenderAddr, amount)) + checkErr(UserTeller.Approve(spenderAddr, amount)) } func TransferFrom(from, to pusers.AddressOrName, amount uint64) { fromAddr := users.Resolve(from) toAddr := users.Resolve(to) - checkErr(token.CallerTeller().TransferFrom(fromAddr, toAddr, amount)) + checkErr(UserTeller.TransferFrom(fromAddr, toAddr, amount)) } func Burn(from pusers.AddressOrName, amount uint64) { - admin.AssertCallerIsOwner() + owner.AssertCallerIsOwner() fromAddr := users.Resolve(from) - checkErr(ledger.Burn(fromAddr, amount)) + checkErr(privateLedger.Burn(fromAddr, amount)) } func Render(path string) string { @@ -63,11 +67,11 @@ func Render(path string) string { switch { case path == "": - return token.RenderHome() + return Token.RenderHome() case c == 2 && parts[0] == "balance": owner := pusers.AddressOrName(parts[1]) ownerAddr := users.Resolve(owner) - balance := token.BalanceOf(ownerAddr) + balance := UserTeller.BalanceOf(ownerAddr) return ufmt.Sprintf("%d\n", balance) default: return "404\n" diff --git a/_deploy/r/gnoswap/gns/gno.mod b/_deploy/r/gnoswap/gns/gno.mod index 7963234e4..8c541067d 100644 --- a/_deploy/r/gnoswap/gns/gno.mod +++ b/_deploy/r/gnoswap/gns/gno.mod @@ -7,6 +7,7 @@ require ( gno.land/p/demo/ufmt v0.0.0-latest gno.land/p/demo/users v0.0.0-latest gno.land/r/demo/users v0.0.0-latest + gno.land/r/demo/grc20reg v0.0.0-latest gno.land/r/gnoswap/v1/common v0.0.0-latest gno.land/r/gnoswap/v1/consts v0.0.0-latest ) diff --git a/_deploy/r/gnoswap/gns/gns.gno b/_deploy/r/gnoswap/gns/gns.gno index cfe319271..327ffb448 100644 --- a/_deploy/r/gnoswap/gns/gns.gno +++ b/_deploy/r/gnoswap/gns/gns.gno @@ -9,6 +9,7 @@ import ( "gno.land/p/demo/ufmt" pusers "gno.land/p/demo/users" + "gno.land/r/demo/grc20reg" "gno.land/r/demo/users" "gno.land/r/gnoswap/v1/common" @@ -18,22 +19,20 @@ import ( const MAXIMUM_SUPPLY = uint64(1_000_000_000_000_000) // 1B var ( - banker *grc20.Teller - admin *ownable.Ownable - token *grc20.Token - ledger *grc20.PrivateLedger + lastMintedHeight int64 + amountToEmission uint64 ) var ( - lastMintedHeight int64 - amountToEmission uint64 + Token, privateLedger = grc20.NewToken("Gnoswap", "GNS", 6) + UserTeller = Token.CallerTeller() + owner = ownable.NewWithAddress("g17290cwvmrapvp869xfnhhawa8sm9edpufzat7d") // ADMIN ) func init() { - admin = ownable.NewWithAddress("g17290cwvmrapvp869xfnhhawa8sm9edpufzat7d") // ADMIN - token, ledger = grc20.NewToken("Gnoswap", "GNS", 6) - - ledger.Mint(admin.Owner(), 100_000_000_000_000) // 100_000_000 GNS ≈ 0.1B + privateLedger.Mint(owner.Owner(), 100_000_000_000_000) // 100_000_000 GNS ≈ 0.1B + getter := func() *grc20.Token { return Token } + grc20reg.Register(getter, "") amountToEmission = MAXIMUM_SUPPLY - uint64(100_000_000_000_000) @@ -42,41 +41,39 @@ func init() { func GetAmountToEmission() uint64 { return amountToEmission } -func TotalSupply() uint64 { return token.TotalSupply() } +func TotalSupply() uint64 { + return UserTeller.TotalSupply() +} -func TotalMinted() uint64 { return token.TotalSupply() - uint64(100_000_000_000_000) } +func TotalMinted() uint64 { + return UserTeller.TotalSupply() - uint64(100_000_000_000_000) +} func BalanceOf(owner pusers.AddressOrName) uint64 { ownerAddr := users.Resolve(owner) - return token.BalanceOf(ownerAddr) + return UserTeller.BalanceOf(ownerAddr) } func Allowance(owner, spender pusers.AddressOrName) uint64 { ownerAddr := users.Resolve(owner) spenderAddr := users.Resolve(spender) - return token.Allowance(ownerAddr, spenderAddr) + return UserTeller.Allowance(ownerAddr, spenderAddr) } func Transfer(to pusers.AddressOrName, amount uint64) { - common.IsHalted() - toAddr := users.Resolve(to) - checkErr(token.CallerTeller().Transfer(toAddr, amount)) + checkErr(UserTeller.Transfer(toAddr, amount)) } func Approve(spender pusers.AddressOrName, amount uint64) { - common.IsHalted() - spenderAddr := users.Resolve(spender) - checkErr(token.CallerTeller().Approve(spenderAddr, amount)) + checkErr(UserTeller.Approve(spenderAddr, amount)) } func TransferFrom(from, to pusers.AddressOrName, amount uint64) { - common.IsHalted() - fromAddr := users.Resolve(from) toAddr := users.Resolve(to) - checkErr(token.CallerTeller().TransferFrom(fromAddr, toAddr, amount)) + checkErr(UserTeller.TransferFrom(fromAddr, toAddr, amount)) } func Render(path string) string { @@ -85,11 +82,11 @@ func Render(path string) string { switch { case path == "": - return token.RenderHome() + return Token.RenderHome() case c == 2 && parts[0] == "balance": owner := pusers.AddressOrName(parts[1]) ownerAddr := users.Resolve(owner) - balance := token.BalanceOf(ownerAddr) + balance := UserTeller.BalanceOf(ownerAddr) return ufmt.Sprintf("%d\n", balance) default: return "404\n" @@ -146,7 +143,7 @@ func Mint(address pusers.AddressOrName) uint64 { } } - err := ledger.Mint(users.Resolve(address), amountToMint) + err := privateLedger.Mint(users.Resolve(address), amountToMint) if err != nil { panic(err.Error()) } @@ -156,6 +153,12 @@ func Mint(address pusers.AddressOrName) uint64 { return amountToMint } +func Burn(from pusers.AddressOrName, amount uint64) { + owner.AssertCallerIsOwner() + fromAddr := users.Resolve(from) + checkErr(privateLedger.Burn(fromAddr, amount)) +} + func checkAndHandleIfLastBlockOfHalvingYear(height int64, amount uint64) uint64 { year := GetHalvingYearByHeight(height) lastBlock := halvingYearBlock[year] From ddac7e5c14e6796bbe975110f8dd221dd517a73b Mon Sep 17 00:00:00 2001 From: Blake <104744707+r3v4s@users.noreply.github.com> Date: Tue, 10 Dec 2024 19:07:35 +0900 Subject: [PATCH 30/44] GSW-1968 feat: get token + teller object from `grc20-reg` in `common` (#413) * feat: get `token` and `teller` object from grc20reg * feat: IsRegistered() to check if token is registered or not --- _deploy/r/gnoswap/common/grc20reg_helper.gno | 46 ++++++ .../r/gnoswap/common/grc20reg_helper_test.gno | 134 ++++++++++++++++++ 2 files changed, 180 insertions(+) create mode 100644 _deploy/r/gnoswap/common/grc20reg_helper.gno create mode 100644 _deploy/r/gnoswap/common/grc20reg_helper_test.gno diff --git a/_deploy/r/gnoswap/common/grc20reg_helper.gno b/_deploy/r/gnoswap/common/grc20reg_helper.gno new file mode 100644 index 000000000..239c42ca8 --- /dev/null +++ b/_deploy/r/gnoswap/common/grc20reg_helper.gno @@ -0,0 +1,46 @@ +package common + +import ( + "gno.land/p/demo/grc/grc20" + "gno.land/p/demo/ufmt" + "gno.land/r/demo/grc20reg" +) + +// GetToken returns a grc20.Token instance +// if token is not registered, it will panic +// token instance supports following methods: +// - GetName +// - GetSymbol +// - GetDecimals +// - TotalSupply +// - KnownAccounts +// - BalanceOf +// - Allowance +// - RenderHome +func GetToken(path string) *grc20.Token { + tokenGetter := grc20reg.MustGet(path) // if token is not registered, it will panic + + return tokenGetter() +} + +// GetTokenTeller returns a grc20.Teller instance +// if token is not registered, it will panic +// teller instance supports following methods: +// - Transfer +// - Approve +// - TransferFrom +func GetTokenTeller(path string) grc20.Teller { + tokenGetter := grc20reg.MustGet(path) // if token is not registered, it will panic + token := tokenGetter() + return token.CallerTeller() +} + +// IsRegistered returns nil if token is registered to grc20reg +// otherwise, it returns an error +func IsRegistered(path string) error { + getter := grc20reg.Get(path) + if getter == nil { + return ufmt.Errorf("token(%s) is not registered to grc20reg", path) + } + return nil +} diff --git a/_deploy/r/gnoswap/common/grc20reg_helper_test.gno b/_deploy/r/gnoswap/common/grc20reg_helper_test.gno new file mode 100644 index 000000000..4287a62db --- /dev/null +++ b/_deploy/r/gnoswap/common/grc20reg_helper_test.gno @@ -0,0 +1,134 @@ +package common + +import ( + "std" + "testing" + + "gno.land/p/demo/testutils" + "gno.land/p/demo/uassert" + "gno.land/p/demo/ufmt" + + _ "gno.land/r/demo/foo20" +) + +var ( + tokenPath = "gno.land/r/demo/foo20" +) + +func TestGetToken(t *testing.T) { + t.Run("registered(by default)", func(t *testing.T) { + token := GetToken(tokenPath) + if token == nil { + t.Error("Expected non-nil teller for foo20") + } + }) + + t.Run("not registered", func(t *testing.T) { + defer func() { + if r := recover(); r == nil { + t.Errorf("Expected panic for non-registered token") + } + }() + GetToken("not_registered") + }) +} + +func TestTokenMethod(t *testing.T) { + token := GetToken(tokenPath) + + t.Run("GetName()", func(t *testing.T) { + uassert.Equal(t, "Foo", token.GetName()) + }) + + t.Run("GetSymbol()", func(t *testing.T) { + uassert.Equal(t, "FOO", token.GetSymbol()) + }) + + t.Run("GetDecimals()", func(t *testing.T) { + uassert.Equal(t, uint(4), token.GetDecimals()) + }) + + t.Run("TotalSupply()", func(t *testing.T) { + uassert.Equal(t, uint64(10000000000), token.TotalSupply()) + }) + + t.Run("KnownAccounts()", func(t *testing.T) { + uassert.Equal(t, int(1), token.KnownAccounts()) + }) + + t.Run("BalanceOf()", func(t *testing.T) { + uassert.Equal(t, uint64(10000000000), token.BalanceOf(std.Address("g1manfred47kzduec920z88wfr64ylksmdcedlf5"))) + }) + + t.Run("Allowance()", func(t *testing.T) { + uassert.Equal(t, uint64(0), token.Allowance(std.Address("g1manfred47kzduec920z88wfr64ylksmdcedlf5"), std.Address("g1pf6dv9fjk3rn0m4jjcne306ga4he3mzmupfjl6"))) + }) + + t.Run("RenderHome()", func(t *testing.T) { + expected := "" + expected += ufmt.Sprintf("# %s ($%s)\n\n", "Foo", "FOO") + expected += ufmt.Sprintf("* **Decimals**: %d\n", 4) + expected += ufmt.Sprintf("* **Total supply**: %d\n", 10000000000) + expected += ufmt.Sprintf("* **Known accounts**: %d\n", 1) + uassert.Equal(t, expected, token.RenderHome()) + }) +} + +func TestGetTokenTeller(t *testing.T) { + t.Run("registered(by default)", func(t *testing.T) { + teller := GetTokenTeller(tokenPath) + if teller == nil { + t.Error("Expected non-nil teller for foo20") + } + }) + + t.Run("not registered", func(t *testing.T) { + defer func() { + if r := recover(); r == nil { + t.Errorf("Expected panic for non-registered token") + } + }() + GetTokenTeller("not_registered") + }) +} + +func TestTellerMethod(t *testing.T) { + teller := GetTokenTeller(tokenPath) + token := GetToken(tokenPath) + defaultHolder := std.Address("g1manfred47kzduec920z88wfr64ylksmdcedlf5") + addr01 := testutils.TestAddress("addr01") + + t.Run("Transfer()", func(t *testing.T) { + std.TestSetRealm(std.NewUserRealm(defaultHolder)) + + uassert.Equal(t, uint64(10000000000), token.BalanceOf(defaultHolder)) + + uassert.NoError(t, teller.Transfer(addr01, uint64(10000000000))) // transfer all balance to addr01 + + uassert.Equal(t, uint64(0), token.BalanceOf(defaultHolder)) + uassert.Equal(t, uint64(10000000000), token.BalanceOf(addr01)) + + uassert.Error(t, teller.Transfer(addr01, uint64(10000000000))) // not enough balance + }) + + t.Run("Approve()", func(t *testing.T) { + std.TestSetRealm(std.NewUserRealm(addr01)) + uassert.NoError(t, teller.Approve(defaultHolder, uint64(500))) + uassert.Equal(t, uint64(500), token.Allowance(addr01, defaultHolder)) + }) + + t.Run("TransferFrom()", func(t *testing.T) { + std.TestSetRealm(std.NewUserRealm(defaultHolder)) + uassert.NoError(t, teller.TransferFrom(addr01, defaultHolder, uint64(500))) + uassert.Equal(t, uint64(9999999500), token.BalanceOf(addr01)) + uassert.Equal(t, uint64(500), token.BalanceOf(defaultHolder)) + uassert.Equal(t, uint64(0), token.Allowance(addr01, defaultHolder)) + + uassert.Error(t, teller.TransferFrom(addr01, defaultHolder, uint64(500))) // not enough allowance + }) +} + +func TestIsRegistered(t *testing.T) { + uassert.NoError(t, IsRegistered(tokenPath)) + uassert.Error(t, IsRegistered("not_registered")) +} From 064ba86c1e1ef5684a6488dcf451b6b822353643 Mon Sep 17 00:00:00 2001 From: 0xTopaz <60733299+onlyhyde@users.noreply.github.com> Date: Tue, 10 Dec 2024 19:19:49 +0900 Subject: [PATCH 31/44] GSW-1839 refactor: helper test code integration and update gno.mod (#427) - remove require in gno.mod - integrated helper test code --- position/_helper_test.gno | 472 +++++++++++++++++- position/gno.mod | 16 - position/position.gno | 16 +- .../__TEST_0_INIT_TOKEN_REGISTER_test.gno | 176 ------- .../__TEST_0_INIT_TOKEN_REGISTER_test.gnoA} | 0 .../tests/__TEST_0_INIT_VARS_HELPERS_test.gno | 69 --- .../__TEST_0_INIT_VARS_HELPERS_test.gnoA} | 0 7 files changed, 474 insertions(+), 275 deletions(-) delete mode 100644 position/tests/__TEST_0_INIT_TOKEN_REGISTER_test.gno rename position/{__TEST_0_INIT_TOKEN_REGISTER_test.gno => tests/__TEST_0_INIT_TOKEN_REGISTER_test.gnoA} (100%) delete mode 100644 position/tests/__TEST_0_INIT_VARS_HELPERS_test.gno rename position/{__TEST_0_INIT_VARS_HELPERS_test.gno => tests/__TEST_0_INIT_VARS_HELPERS_test.gnoA} (100%) diff --git a/position/_helper_test.gno b/position/_helper_test.gno index 4d87113fd..be52f83be 100644 --- a/position/_helper_test.gno +++ b/position/_helper_test.gno @@ -4,17 +4,477 @@ import ( "std" "testing" + "gno.land/p/demo/testutils" "gno.land/p/demo/uassert" - "gno.land/r/onbloc/bar" - "gno.land/r/onbloc/foo" - + 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/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/baz" + "gno.land/r/onbloc/foo" + "gno.land/r/onbloc/obl" + "gno.land/r/onbloc/qux" +) + +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 + maxApprove uint64 = 18446744073709551615 + max_timeout int64 = 9999999999 +) + +const ( + // define addresses to use in tests + addr01 = testutils.TestAddress("addr01") + addr02 = testutils.TestAddress("addr02") ) +type WugnotToken struct{} + +func (WugnotToken) Transfer() func(to pusers.AddressOrName, amount uint64) { + return wugnot.Transfer +} +func (WugnotToken) TransferFrom() func(from, to pusers.AddressOrName, amount uint64) { + return wugnot.TransferFrom +} +func (WugnotToken) BalanceOf() func(owner pusers.AddressOrName) uint64 { + return wugnot.BalanceOf +} +func (WugnotToken) Approve() func(spender pusers.AddressOrName, amount uint64) { + return wugnot.Approve +} + +type GNSToken struct{} + +func (GNSToken) Transfer() func(to pusers.AddressOrName, amount uint64) { + return gns.Transfer +} +func (GNSToken) TransferFrom() func(from, to pusers.AddressOrName, amount uint64) { + return gns.TransferFrom +} +func (GNSToken) BalanceOf() func(owner pusers.AddressOrName) uint64 { + return gns.BalanceOf +} +func (GNSToken) Approve() func(spender pusers.AddressOrName, amount uint64) { + return gns.Approve +} + +type BarToken struct{} + +func (BarToken) Transfer() func(to pusers.AddressOrName, amount uint64) { + return bar.Transfer +} +func (BarToken) TransferFrom() func(from, to pusers.AddressOrName, amount uint64) { + return bar.TransferFrom +} +func (BarToken) BalanceOf() func(owner pusers.AddressOrName) uint64 { + return bar.BalanceOf +} +func (BarToken) Approve() func(spender pusers.AddressOrName, amount uint64) { + return bar.Approve +} + +type BazToken struct{} + +func (BazToken) Transfer() func(to pusers.AddressOrName, amount uint64) { + return baz.Transfer +} +func (BazToken) TransferFrom() func(from, to pusers.AddressOrName, amount uint64) { + return baz.TransferFrom +} +func (BazToken) BalanceOf() func(owner pusers.AddressOrName) uint64 { + return baz.BalanceOf +} +func (BazToken) Approve() func(spender pusers.AddressOrName, amount uint64) { + return baz.Approve +} + +type FooToken struct{} + +func (FooToken) Transfer() func(to pusers.AddressOrName, amount uint64) { + return foo.Transfer +} +func (FooToken) TransferFrom() func(from, to pusers.AddressOrName, amount uint64) { + return foo.TransferFrom +} +func (FooToken) BalanceOf() func(owner pusers.AddressOrName) uint64 { + return foo.BalanceOf +} +func (FooToken) Approve() func(spender pusers.AddressOrName, amount uint64) { + return foo.Approve +} + +type OBLToken struct{} + +func (OBLToken) Transfer() func(to pusers.AddressOrName, amount uint64) { + return obl.Transfer +} +func (OBLToken) TransferFrom() func(from, to pusers.AddressOrName, amount uint64) { + return obl.TransferFrom +} +func (OBLToken) BalanceOf() func(owner pusers.AddressOrName) uint64 { + return obl.BalanceOf +} +func (OBLToken) Approve() func(spender pusers.AddressOrName, amount uint64) { + return obl.Approve +} + +type QuxToken struct{} + +func (QuxToken) Transfer() func(to pusers.AddressOrName, amount uint64) { + return qux.Transfer +} +func (QuxToken) TransferFrom() func(from, to pusers.AddressOrName, amount uint64) { + return qux.TransferFrom +} +func (QuxToken) BalanceOf() func(owner pusers.AddressOrName) uint64 { + return qux.BalanceOf +} +func (QuxToken) Approve() func(spender pusers.AddressOrName, amount uint64) { + return qux.Approve +} + +func init() { + std.TestSetRealm(std.NewUserRealm(consts.TOKEN_REGISTER)) + + pl.RegisterGRC20Interface(wugnotPath, WugnotToken{}) + pl.RegisterGRC20Interface(gnsPath, GNSToken{}) + pl.RegisterGRC20Interface(barPath, BarToken{}) + pl.RegisterGRC20Interface(bazPath, BazToken{}) + pl.RegisterGRC20Interface(fooPath, FooToken{}) + pl.RegisterGRC20Interface(oblPath, OBLToken{}) + pl.RegisterGRC20Interface(quxPath, QuxToken{}) +} + +var ( + admin = pusers.AddressOrName(consts.ADMIN) + alice = pusers.AddressOrName(testutils.TestAddress("alice")) + pool = pusers.AddressOrName(consts.POOL_ADDR) + protocolFee = pusers.AddressOrName(consts.PROTOCOL_FEE_ADDR) + adminRealm = std.NewUserRealm(users.Resolve(admin)) + posRealm = std.NewCodeRealm(consts.POSITION_PATH) + + // addresses used in tests + addrUsedInTest = []std.Address{addr01, addr02} +) + +func InitialisePoolTest(t *testing.T) { + t.Helper() + + ugnotFaucet(t, users.Resolve(admin), 100_000_000_000_000) + ugnotDeposit(t, users.Resolve(admin), 100_000_000_000_000) + + std.TestSetOrigCaller(users.Resolve(admin)) + TokenApprove(t, gnsPath, admin, pool, maxApprove) + poolPath := pl.GetPoolPath(wugnotPath, gnsPath, fee3000) + if !pl.DoesPoolPathExist(poolPath) { + pl.CreatePool(wugnotPath, gnsPath, fee3000, "79228162514264337593543950336") + } + + //2. create position + std.TestSetOrigCaller(users.Resolve(alice)) + TokenFaucet(t, wugnotPath, alice) + TokenFaucet(t, gnsPath, alice) + TokenApprove(t, wugnotPath, alice, pool, uint64(1000)) + TokenApprove(t, gnsPath, alice, pool, uint64(1000)) + MintPosition(t, + wugnotPath, + gnsPath, + fee3000, + int32(1020), + int32(5040), + "1000", + "1000", + "0", + "0", + max_timeout, + users.Resolve(alice), + users.Resolve(alice), + ) +} + +func TokenFaucet(t *testing.T, tokenPath string, to pusers.AddressOrName) { + t.Helper() + std.TestSetOrigCaller(users.Resolve(admin)) + defaultAmount := uint64(5_000_000_000) + + switch tokenPath { + case wugnotPath: + wugnotTransfer(t, to, defaultAmount) + case gnsPath: + gnsTransfer(t, to, defaultAmount) + case barPath: + barTransfer(t, to, defaultAmount) + case bazPath: + bazTransfer(t, to, defaultAmount) + case fooPath: + fooTransfer(t, to, defaultAmount) + case oblPath: + oblTransfer(t, to, defaultAmount) + case quxPath: + quxTransfer(t, to, defaultAmount) + default: + panic("token not found") + } +} + +func TokenBalance(t *testing.T, tokenPath string, owner pusers.AddressOrName) uint64 { + t.Helper() + switch tokenPath { + case wugnotPath: + return wugnot.BalanceOf(owner) + case gnsPath: + return gns.BalanceOf(owner) + case barPath: + return bar.BalanceOf(owner) + case bazPath: + return baz.BalanceOf(owner) + case fooPath: + return foo.BalanceOf(owner) + case oblPath: + return obl.BalanceOf(owner) + case quxPath: + return qux.BalanceOf(owner) + default: + panic("token not found") + } +} + +func TokenAllowance(t *testing.T, tokenPath string, owner, spender pusers.AddressOrName) uint64 { + t.Helper() + switch tokenPath { + case wugnotPath: + return wugnot.Allowance(owner, spender) + case gnsPath: + return gns.Allowance(owner, spender) + case barPath: + return bar.Allowance(owner, spender) + case bazPath: + return baz.Allowance(owner, spender) + case fooPath: + return foo.Allowance(owner, spender) + case oblPath: + return obl.Allowance(owner, spender) + case quxPath: + return qux.Allowance(owner, spender) + default: + panic("token not found") + } +} + +func TokenApprove(t *testing.T, tokenPath string, owner, spender pusers.AddressOrName, amount uint64) { + t.Helper() + switch tokenPath { + case wugnotPath: + wugnotApprove(t, owner, spender, amount) + case gnsPath: + gnsApprove(t, owner, spender, amount) + case barPath: + barApprove(t, owner, spender, amount) + case bazPath: + bazApprove(t, owner, spender, amount) + case fooPath: + fooApprove(t, owner, spender, amount) + case oblPath: + oblApprove(t, owner, spender, amount) + case quxPath: + quxApprove(t, owner, spender, amount) + default: + panic("token not found") + } +} + +func MintPosition(t *testing.T, + token0 string, + token1 string, + fee uint32, + tickLower int32, + tickUpper int32, + amount0Desired string, // *u256.Uint + amount1Desired string, // *u256.Uint + amount0Min string, // *u256.Uint + amount1Min string, // *u256.Uint + deadline int64, + mintTo std.Address, + caller std.Address, +) (uint64, string, string, string) { + t.Helper() + std.TestSetRealm(std.NewUserRealm(caller)) + + return Mint( + token0, + token1, + fee, + tickLower, + tickUpper, + amount0Desired, + amount1Desired, + amount0Min, + amount1Min, + deadline, + mintTo, + caller) +} + +func wugnotApprove(t *testing.T, owner, spender pusers.AddressOrName, amount uint64) { + t.Helper() + std.TestSetRealm(std.NewUserRealm(users.Resolve(owner))) + wugnot.Approve(spender, amount) +} + +func gnsApprove(t *testing.T, owner, spender pusers.AddressOrName, amount uint64) { + t.Helper() + std.TestSetRealm(std.NewUserRealm(users.Resolve(owner))) + gns.Approve(spender, amount) +} + +func barApprove(t *testing.T, owner, spender pusers.AddressOrName, amount uint64) { + t.Helper() + std.TestSetRealm(std.NewUserRealm(users.Resolve(owner))) + bar.Approve(spender, amount) +} + +func bazApprove(t *testing.T, owner, spender pusers.AddressOrName, amount uint64) { + t.Helper() + std.TestSetRealm(std.NewUserRealm(users.Resolve(owner))) + baz.Approve(spender, amount) +} + +func fooApprove(t *testing.T, owner, spender pusers.AddressOrName, amount uint64) { + t.Helper() + std.TestSetRealm(std.NewUserRealm(users.Resolve(owner))) + foo.Approve(spender, amount) +} + +func oblApprove(t *testing.T, owner, spender pusers.AddressOrName, amount uint64) { + t.Helper() + std.TestSetRealm(std.NewUserRealm(users.Resolve(owner))) + obl.Approve(spender, amount) +} + +func quxApprove(t *testing.T, owner, spender pusers.AddressOrName, amount uint64) { + t.Helper() + std.TestSetRealm(std.NewUserRealm(users.Resolve(owner))) + qux.Approve(spender, amount) +} + +func wugnotTransfer(t *testing.T, to pusers.AddressOrName, amount uint64) { + t.Helper() + std.TestSetRealm(std.NewUserRealm(users.Resolve(admin))) + wugnot.Transfer(to, amount) +} + +func gnsTransfer(t *testing.T, to pusers.AddressOrName, amount uint64) { + t.Helper() + std.TestSetRealm(std.NewUserRealm(users.Resolve(admin))) + gns.Transfer(to, amount) +} + +func barTransfer(t *testing.T, to pusers.AddressOrName, amount uint64) { + t.Helper() + std.TestSetRealm(std.NewUserRealm(users.Resolve(admin))) + bar.Transfer(to, amount) +} + +func bazTransfer(t *testing.T, to pusers.AddressOrName, amount uint64) { + t.Helper() + std.TestSetRealm(std.NewUserRealm(users.Resolve(admin))) + baz.Transfer(to, amount) +} + +func fooTransfer(t *testing.T, to pusers.AddressOrName, amount uint64) { + t.Helper() + std.TestSetRealm(std.NewUserRealm(users.Resolve(admin))) + foo.Transfer(to, amount) +} + +func oblTransfer(t *testing.T, to pusers.AddressOrName, amount uint64) { + t.Helper() + std.TestSetRealm(std.NewUserRealm(users.Resolve(admin))) + obl.Transfer(to, amount) +} + +func quxTransfer(t *testing.T, to pusers.AddressOrName, amount uint64) { + t.Helper() + std.TestSetRealm(std.NewUserRealm(users.Resolve(admin))) + qux.Transfer(to, amount) +} + +// ---------------------------------------------------------------------------- +// ugnot + +func ugnotTransfer(t *testing.T, from, to std.Address, amount uint64) { + t.Helper() + + std.TestSetRealm(std.NewUserRealm(from)) + std.TestSetOrigSend(std.Coins{{ugnotDenom, int64(amount)}}, nil) + banker := std.GetBanker(std.BankerTypeRealmSend) + banker.SendCoins(from, to, std.Coins{{ugnotDenom, int64(amount)}}) +} + +func ugnotBalanceOf(t *testing.T, addr std.Address) uint64 { + t.Helper() + + banker := std.GetBanker(std.BankerTypeRealmIssue) + coins := banker.GetCoins(addr) + if len(coins) == 0 { + return 0 + } + + return uint64(coins.AmountOf(ugnotDenom)) +} + +func ugnotMint(t *testing.T, addr std.Address, denom string, amount int64) { + t.Helper() + banker := std.GetBanker(std.BankerTypeRealmIssue) + banker.IssueCoin(addr, denom, amount) + std.TestIssueCoins(addr, std.Coins{{denom, int64(amount)}}) +} + +func ugnotBurn(t *testing.T, addr std.Address, denom string, amount int64) { + t.Helper() + banker := std.GetBanker(std.BankerTypeRealmIssue) + banker.RemoveCoin(addr, denom, amount) +} + +func ugnotFaucet(t *testing.T, to std.Address, amount uint64) { + t.Helper() + faucetAddress := users.Resolve(admin) + std.TestSetOrigCaller(faucetAddress) + + if ugnotBalanceOf(t, faucetAddress) < amount { + newCoins := std.Coins{{ugnotDenom, int64(amount)}} + ugnotMint(t, faucetAddress, newCoins[0].Denom, newCoins[0].Amount) + std.TestSetOrigSend(newCoins, nil) + } + ugnotTransfer(t, faucetAddress, to, amount) +} + +func ugnotDeposit(t *testing.T, addr std.Address, amount uint64) { + t.Helper() + std.TestSetRealm(std.NewUserRealm(addr)) + wugnotAddr := consts.WUGNOT_ADDR + banker := std.GetBanker(std.BankerTypeRealmSend) + banker.SendCoins(addr, wugnotAddr, std.Coins{{ugnotDenom, int64(amount)}}) + wugnot.Deposit() +} + // resetObject resets the object state(clear or make it default values) func resetObject(t *testing.T) { positions = make(map[uint64]Position) @@ -54,8 +514,8 @@ func TestBeforeResetObject(t *testing.T) { "0", "0", max_timeout, - admin, - admin, + users.Resolve(admin), + users.Resolve(admin), ) uassert.Equal(t, tokenId, uint64(1), "tokenId should be 1") diff --git a/position/gno.mod b/position/gno.mod index ca6175e65..7278afb2f 100644 --- a/position/gno.mod +++ b/position/gno.mod @@ -1,17 +1 @@ module gno.land/r/gnoswap/v1/position - -require ( - gno.land/p/demo/grc/grc721 v0.0.0-latest - gno.land/p/demo/json v0.0.0-latest - gno.land/p/demo/ufmt v0.0.0-latest - gno.land/p/demo/users v0.0.0-latest - gno.land/p/gnoswap/int256 v0.0.0-latest - gno.land/p/gnoswap/pool v0.0.0-latest - gno.land/p/gnoswap/uint256 v0.0.0-latest - gno.land/r/demo/wugnot v0.0.0-latest - gno.land/r/gnoswap/v1/common v0.0.0-latest - gno.land/r/gnoswap/v1/consts v0.0.0-latest - gno.land/r/gnoswap/v1/emission v0.0.0-latest - gno.land/r/gnoswap/v1/gnft v0.0.0-latest - gno.land/r/gnoswap/v1/pool v0.0.0-latest -) diff --git a/position/position.gno b/position/position.gno index 2aa8ad5c6..7e5e37a2b 100644 --- a/position/position.gno +++ b/position/position.gno @@ -103,7 +103,7 @@ func Mint( handleLeftoverNativeToken(token0IsNative, token1IsNative, userWugnotBalance, caller) - poolSqrtPriceX96 := pl.PoolGetSqrtPriceX96(poolPath) + poolSqrtPriceX96 := pl.PoolGetSlot0SqrtPriceX96(poolPath) prevAddr, prevRealm := getPrev() @@ -120,7 +120,7 @@ func Mint( "internal_liquidity", liquidity.ToString(), "internal_amount0", amount0.ToString(), "internal_amount1", amount1.ToString(), - "internal_sqrtPriceX96", poolSqrtPriceX96.ToString(), + "internal_sqrtPriceX96", poolSqrtPriceX96, ) return tokenId, liquidity.ToString(), amount0.ToString(), amount1.ToString() @@ -263,7 +263,7 @@ func IncreaseLiquidity( unwrap(leftOver, std.PrevRealm().Addr()) } - poolSqrtPriceX96 := pl.PoolGetSqrtPriceX96(poolPath) + poolSqrtPriceX96 := pl.PoolGetSlot0SqrtPriceX96(poolPath) prevAddr, prevRealm := getPrev() @@ -276,7 +276,7 @@ func IncreaseLiquidity( "internal_liquidity", liquidity.ToString(), "internal_amount0", amount0.ToString(), "internal_amount1", amount1.ToString(), - "internal_sqrtPriceX96", poolSqrtPriceX96.ToString(), + "internal_sqrtPriceX96", poolSqrtPriceX96, ) return tokenId, liquidity.ToString(), amount0.ToString(), amount1.ToString(), poolPath @@ -384,7 +384,7 @@ func DecreaseLiquidity( tokenId, liquidity, fee0, fee1, amount0, amount1, poolPath := decreaseLiquidity(decreaseLiquidityParams) - poolSqrtPriceX96 := pl.PoolGetSqrtPriceX96(poolPath) + poolSqrtPriceX96 := pl.PoolGetSlot0SqrtPriceX96(poolPath) prevAddr, prevRealm := getPrev() @@ -401,7 +401,7 @@ func DecreaseLiquidity( "internal_amount0", amount0.ToString(), "internal_amount1", amount1.ToString(), "internal_unwrapResult", ufmt.Sprintf("%t", unwrapResult), - "internal_sqrtPriceX96", poolSqrtPriceX96.ToString(), + "internal_sqrtPriceX96", poolSqrtPriceX96, ) return tokenId, liquidity.ToString(), fee0.ToString(), fee1.ToString(), amount0.ToString(), amount1.ToString(), poolPath @@ -613,7 +613,7 @@ func Reposition( positions[tokenId] = position - poolSqrtPriceX96 := pl.PoolGetSqrtPriceX96(position.poolKey) + poolSqrtPriceX96 := pl.PoolGetSlot0SqrtPriceX96(position.poolKey) prevAddr, prevRealm := getPrev() @@ -630,7 +630,7 @@ func Reposition( "internal_oldTickLower", ufmt.Sprintf("%d", oldTickLower), "internal_oldTickUpper", ufmt.Sprintf("%d", oldTickUpper), "internal_poolPath", position.poolKey, - "internal_sqrtPriceX96", poolSqrtPriceX96.ToString(), + "internal_sqrtPriceX96", poolSqrtPriceX96, ) return tokenId, liquidity.ToString(), tickLower, tickUpper, amount0.ToString(), amount1.ToString() diff --git a/position/tests/__TEST_0_INIT_TOKEN_REGISTER_test.gno b/position/tests/__TEST_0_INIT_TOKEN_REGISTER_test.gno deleted file mode 100644 index 8da3b3790..000000000 --- a/position/tests/__TEST_0_INIT_TOKEN_REGISTER_test.gno +++ /dev/null @@ -1,176 +0,0 @@ -package position - -import ( - "std" - - "gno.land/r/onbloc/foo" - - "gno.land/r/onbloc/bar" - - "gno.land/r/onbloc/baz" - - "gno.land/r/onbloc/qux" - - "gno.land/r/demo/wugnot" - - "gno.land/r/onbloc/obl" - - "gno.land/r/gnoswap/v1/gns" - - "gno.land/r/onbloc/usdc" - - "gno.land/r/gnoswap/v1/consts" - - pusers "gno.land/p/demo/users" - - pl "gno.land/r/gnoswap/v1/pool" - rr "gno.land/r/gnoswap/v1/router" -) - -type FooToken struct{} - -func (FooToken) Transfer() func(to pusers.AddressOrName, amount uint64) { - return foo.Transfer -} -func (FooToken) TransferFrom() func(from, to pusers.AddressOrName, amount uint64) { - return foo.TransferFrom -} -func (FooToken) BalanceOf() func(owner pusers.AddressOrName) uint64 { - return foo.BalanceOf -} -func (FooToken) Approve() func(spender pusers.AddressOrName, amount uint64) { - return foo.Approve -} - -type BarToken struct{} - -func (BarToken) Transfer() func(to pusers.AddressOrName, amount uint64) { - return bar.Transfer -} -func (BarToken) TransferFrom() func(from, to pusers.AddressOrName, amount uint64) { - return bar.TransferFrom -} -func (BarToken) BalanceOf() func(owner pusers.AddressOrName) uint64 { - return bar.BalanceOf -} -func (BarToken) Approve() func(spender pusers.AddressOrName, amount uint64) { - return bar.Approve -} - -type BazToken struct{} - -func (BazToken) Transfer() func(to pusers.AddressOrName, amount uint64) { - return baz.Transfer -} -func (BazToken) TransferFrom() func(from, to pusers.AddressOrName, amount uint64) { - return baz.TransferFrom -} -func (BazToken) BalanceOf() func(owner pusers.AddressOrName) uint64 { - return baz.BalanceOf -} -func (BazToken) Approve() func(spender pusers.AddressOrName, amount uint64) { - return baz.Approve -} - -type QuxToken struct{} - -func (QuxToken) Transfer() func(to pusers.AddressOrName, amount uint64) { - return qux.Transfer -} -func (QuxToken) TransferFrom() func(from, to pusers.AddressOrName, amount uint64) { - return qux.TransferFrom -} -func (QuxToken) BalanceOf() func(owner pusers.AddressOrName) uint64 { - return qux.BalanceOf -} -func (QuxToken) Approve() func(spender pusers.AddressOrName, amount uint64) { - return qux.Approve -} - -type WugnotToken struct{} - -func (WugnotToken) Transfer() func(to pusers.AddressOrName, amount uint64) { - return wugnot.Transfer -} -func (WugnotToken) TransferFrom() func(from, to pusers.AddressOrName, amount uint64) { - return wugnot.TransferFrom -} -func (WugnotToken) BalanceOf() func(owner pusers.AddressOrName) uint64 { - return wugnot.BalanceOf -} -func (WugnotToken) Approve() func(spender pusers.AddressOrName, amount uint64) { - return wugnot.Approve -} - -type OBLToken struct{} - -func (OBLToken) Transfer() func(to pusers.AddressOrName, amount uint64) { - return obl.Transfer -} -func (OBLToken) TransferFrom() func(from, to pusers.AddressOrName, amount uint64) { - return obl.TransferFrom -} -func (OBLToken) BalanceOf() func(owner pusers.AddressOrName) uint64 { - return obl.BalanceOf -} -func (OBLToken) Approve() func(spender pusers.AddressOrName, amount uint64) { - return obl.Approve -} - -type GNSToken struct{} - -func (GNSToken) Transfer() func(to pusers.AddressOrName, amount uint64) { - return gns.Transfer -} - -func (GNSToken) TransferFrom() func(from, to pusers.AddressOrName, amount uint64) { - return gns.TransferFrom -} - -func (GNSToken) BalanceOf() func(owner pusers.AddressOrName) uint64 { - return gns.BalanceOf -} - -func (GNSToken) Approve() func(spender pusers.AddressOrName, amount uint64) { - return gns.Approve -} - -type USDCToken struct{} - -func (USDCToken) Transfer() func(to pusers.AddressOrName, amount uint64) { - return usdc.Transfer -} - -func (USDCToken) TransferFrom() func(from, to pusers.AddressOrName, amount uint64) { - return usdc.TransferFrom -} - -func (USDCToken) BalanceOf() func(owner pusers.AddressOrName) uint64 { - return usdc.BalanceOf -} - -func (USDCToken) Approve() func(spender pusers.AddressOrName, amount uint64) { - return usdc.Approve -} - -func init() { - std.TestSetRealm(std.NewUserRealm(consts.TOKEN_REGISTER)) - - pl.RegisterGRC20Interface("gno.land/r/onbloc/bar", BarToken{}) - pl.RegisterGRC20Interface("gno.land/r/onbloc/foo", FooToken{}) - pl.RegisterGRC20Interface("gno.land/r/onbloc/baz", BazToken{}) - pl.RegisterGRC20Interface("gno.land/r/onbloc/qux", QuxToken{}) - pl.RegisterGRC20Interface("gno.land/r/demo/wugnot", WugnotToken{}) - pl.RegisterGRC20Interface("gno.land/r/onbloc/obl", OBLToken{}) - pl.RegisterGRC20Interface("gno.land/r/gnoswap/v1/gns", GNSToken{}) - pl.RegisterGRC20Interface("gno.land/r/onbloc/usdc", USDCToken{}) - - rr.RegisterGRC20Interface("gno.land/r/onbloc/bar", BarToken{}) - rr.RegisterGRC20Interface("gno.land/r/onbloc/foo", FooToken{}) - rr.RegisterGRC20Interface("gno.land/r/onbloc/baz", BazToken{}) - rr.RegisterGRC20Interface("gno.land/r/onbloc/qux", QuxToken{}) - rr.RegisterGRC20Interface("gno.land/r/demo/wugnot", WugnotToken{}) - rr.RegisterGRC20Interface("gno.land/r/onbloc/obl", OBLToken{}) - rr.RegisterGRC20Interface("gno.land/r/gnoswap/v1/gns", GNSToken{}) - rr.RegisterGRC20Interface("gno.land/r/onbloc/usdc", USDCToken{}) -} diff --git a/position/__TEST_0_INIT_TOKEN_REGISTER_test.gno b/position/tests/__TEST_0_INIT_TOKEN_REGISTER_test.gnoA similarity index 100% rename from position/__TEST_0_INIT_TOKEN_REGISTER_test.gno rename to position/tests/__TEST_0_INIT_TOKEN_REGISTER_test.gnoA diff --git a/position/tests/__TEST_0_INIT_VARS_HELPERS_test.gno b/position/tests/__TEST_0_INIT_VARS_HELPERS_test.gno deleted file mode 100644 index f7972bb9a..000000000 --- a/position/tests/__TEST_0_INIT_VARS_HELPERS_test.gno +++ /dev/null @@ -1,69 +0,0 @@ -package position - -import ( - "std" - "testing" - - "gno.land/r/gnoswap/v1/consts" - - "gno.land/r/gnoswap/v1/gnft" - - pl "gno.land/r/gnoswap/v1/pool" -) - -var ( - admin std.Address = consts.ADMIN - test1 std.Address = std.Address("g1jg8mtutu9khhfwc4nxmuhcpftf0pajdhfvsqf5") - - fooPath string = "gno.land/r/onbloc/foo" - barPath string = "gno.land/r/onbloc/bar" - bazPath string = "gno.land/r/onbloc/baz" - quxPath string = "gno.land/r/onbloc/qux" - - oblPath string = "gno.land/r/onbloc/obl" - // wugnotPath string = "gno.land/r/demo/wugnot" // from consts - // gnsPath string = "gno.land/r/gnoswap/v1/gns" // from consts - - fee100 uint32 = 100 - fee500 uint32 = 500 - fee3000 uint32 = 3000 - - max_timeout int64 = 9999999999 -) - -// Realms to mock frames -var ( - adminRealm = std.NewUserRealm(admin) - posRealm = std.NewCodeRealm(consts.POSITION_PATH) - rouRealm = std.NewCodeRealm(consts.ROUTER_PATH) - stkRealm = std.NewCodeRealm(consts.STAKER_PATH) -) - -/* HELPER */ -func ugnotBalanceOf(addr std.Address) uint64 { - testBanker := std.GetBanker(std.BankerTypeRealmIssue) - - coins := testBanker.GetCoins(addr) - if len(coins) == 0 { - return 0 - } - - return uint64(coins.AmountOf("ugnot")) -} - -func isOwner(t *testing.T, tokenId uint64, addr std.Address) bool { - owner := gnft.OwnerOf(tid(tokenId)) - - if owner == addr { - return true - } - - t.Errorf("expected owner %v, got %v", addr, owner) - return false -} - -func getPoolFromLpTokenId(lpTokenId uint64) *pl.Pool { - position := positions[lpTokenId] - - return pl.GetPoolFromPoolPath(position.poolKey) -} diff --git a/position/__TEST_0_INIT_VARS_HELPERS_test.gno b/position/tests/__TEST_0_INIT_VARS_HELPERS_test.gnoA similarity index 100% rename from position/__TEST_0_INIT_VARS_HELPERS_test.gno rename to position/tests/__TEST_0_INIT_VARS_HELPERS_test.gnoA From 49eb03fbb93532a938acfc0e531758012cf8f707 Mon Sep 17 00:00:00 2001 From: Blake <104744707+r3v4s@users.noreply.github.com> Date: Tue, 10 Dec 2024 19:22:13 +0900 Subject: [PATCH 32/44] GSW-2020 feat: tlin ci to check only changed files (#428) * feat: tlin ci to check only changed files * feat: check only gno file --- .github/workflows/tlin_check.yml | 30 +++++++++++++++++++----------- 1 file changed, 19 insertions(+), 11 deletions(-) diff --git a/.github/workflows/tlin_check.yml b/.github/workflows/tlin_check.yml index 9b6f23fb0..a66ec53d9 100644 --- a/.github/workflows/tlin_check.yml +++ b/.github/workflows/tlin_check.yml @@ -7,37 +7,45 @@ on: jobs: tlin-check: + strategy: + fail-fast: false + runs-on: ubuntu-latest + steps: - name: checkout code - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: ref: ${{ github.event.pull_request.head.ref }} + - name: checkout tlin - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: repository: gnoverse/tlin ref: main path: ./tlin + - name: setup go uses: actions/setup-go@v5 with: go-version: 1.22 - - name: changed directories - id: changed_directories + + - name: changed files + id: changed_files uses: tj-actions/changed-files@v45 with: - dir_names: "true" - - name: list changed directories - run: | - echo "Changed directories: ${{ steps.changed_directories.outputs.all_changed_files }}" + files: | + *.gno + **.gno + - name: install tlin run: | cd tlin go install ./cmd/tlin + - name: tlin check run: | - for directory in ${{ steps.changed_directories.outputs.all_changed_files }}; do - echo "checking $directory ..." - tlin $directory + for file in ${{ steps.changed_files.outputs.all_changed_files }}; do + echo "checking ${file} ..." + tlin ${file} done From c86dab4a4fc98f9626373937949c1929907943c4 Mon Sep 17 00:00:00 2001 From: 0xTopaz <60733299+onlyhyde@users.noreply.github.com> Date: Tue, 10 Dec 2024 19:29:12 +0900 Subject: [PATCH 33/44] GSW-2023 chore: update gno.mod and use getter function in test code (#426) - remove require in gno.mod - use getter function in test code --- pool/_helper_test.gno | 3 +-- pool/gno.mod | 13 ------------- 2 files changed, 1 insertion(+), 15 deletions(-) diff --git a/pool/_helper_test.gno b/pool/_helper_test.gno index ce061a777..8deb8863e 100644 --- a/pool/_helper_test.gno +++ b/pool/_helper_test.gno @@ -183,8 +183,7 @@ func InitialisePoolTest(t *testing.T) { std.TestSetOrigCaller(users.Resolve(admin)) TokenApprove(t, gnsPath, admin, pool, maxApprove) poolPath := GetPoolPath(wugnotPath, gnsPath, fee3000) - _, exist := pools[poolPath] - if !exist { + if !DoesPoolPathExist(poolPath) { CreatePool(wugnotPath, gnsPath, fee3000, "79228162514264337593543950336") } diff --git a/pool/gno.mod b/pool/gno.mod index a4197a6cc..3c88fce34 100644 --- a/pool/gno.mod +++ b/pool/gno.mod @@ -1,14 +1 @@ module gno.land/r/gnoswap/v1/pool - -require ( - gno.land/p/demo/json v0.0.0-latest - gno.land/p/demo/ufmt v0.0.0-latest - gno.land/p/demo/users v0.0.0-latest - gno.land/p/gnoswap/int256 v0.0.0-latest - gno.land/p/gnoswap/pool v0.0.0-latest - gno.land/p/gnoswap/uint256 v0.0.0-latest - gno.land/r/gnoswap/v1/common v0.0.0-latest - gno.land/r/gnoswap/v1/consts v0.0.0-latest - gno.land/r/gnoswap/v1/emission v0.0.0-latest - gno.land/r/gnoswap/v1/gns v0.0.0-latest -) From 40f9b722074f8c5929006bad10efd2e4fe19897e Mon Sep 17 00:00:00 2001 From: n3wbie Date: Tue, 10 Dec 2024 19:38:44 +0900 Subject: [PATCH 34/44] test: remove hardcoded value --- pool/pool_manager_test.gno | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/pool/pool_manager_test.gno b/pool/pool_manager_test.gno index 0a9776dcf..896e4ce2b 100644 --- a/pool/pool_manager_test.gno +++ b/pool/pool_manager_test.gno @@ -118,15 +118,15 @@ func TestCreatePool(t *testing.T) { }{ { name: "success - normal token pair", - token0Path: "gno.land/r/onbloc/bar", - token1Path: "gno.land/r/onbloc/foo", + token0Path: barPath, + token1Path: GetPoolFromPoolPath, fee: 3000, sqrtPrice: "4295128740", }, { name: "fail - same tokens", - token0Path: "gno.land/r/onbloc/bar", - token1Path: "gno.land/r/onbloc/bar", + token0Path: barPath, + token1Path: barPath, fee: 3000, sqrtPrice: "4295128740", shouldPanic: true, @@ -134,8 +134,8 @@ func TestCreatePool(t *testing.T) { }, { name: "fail - tokens not in order", - token0Path: "gno.land/r/onbloc/foo", - token1Path: "gno.land/r/onbloc/bar", + token0Path: GetPoolFromPoolPath, + token1Path: barPath, fee: 3000, sqrtPrice: "4295128740", shouldPanic: true, @@ -143,8 +143,8 @@ func TestCreatePool(t *testing.T) { }, { name: "fail - pool already exists", - token0Path: "gno.land/r/onbloc/bar", - token1Path: "gno.land/r/onbloc/foo", + token0Path: barPath, + token1Path: GetPoolFromPoolPath, fee: 3000, sqrtPrice: "4295128740", shouldPanic: true, From 7b61cf9e6e0b792acf7e7a325f0737b59d173217 Mon Sep 17 00:00:00 2001 From: n3wbie Date: Tue, 10 Dec 2024 19:39:00 +0900 Subject: [PATCH 35/44] test: detail error message --- .../r/gnoswap/common/grc20reg_helper_test.gno | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/_deploy/r/gnoswap/common/grc20reg_helper_test.gno b/_deploy/r/gnoswap/common/grc20reg_helper_test.gno index 023461f14..31c7aa9e8 100644 --- a/_deploy/r/gnoswap/common/grc20reg_helper_test.gno +++ b/_deploy/r/gnoswap/common/grc20reg_helper_test.gno @@ -16,20 +16,20 @@ var ( ) func TestGetToken(t *testing.T) { - t.Run("registered(by default)", func(t *testing.T) { + t.Run("get regsitered token", func(t *testing.T) { token := GetToken(tokenPath) if token == nil { - t.Error("Expected non-nil teller for foo20") + t.Error("Expected non-nil token for foo20") } }) - t.Run("not registered", func(t *testing.T) { + t.Run("get non registered token", func(t *testing.T) { defer func() { if r := recover(); r == nil { t.Errorf("Expected panic for non-registered token") } }() - GetToken("not_registered") + GetToken("not_registered_token") }) } @@ -75,20 +75,20 @@ func TestTokenMethod(t *testing.T) { } func TestGetTokenTeller(t *testing.T) { - t.Run("registered(by default)", func(t *testing.T) { + t.Run("get registered token teller", func(t *testing.T) { teller := GetTokenTeller(tokenPath) if teller == nil { t.Error("Expected non-nil teller for foo20") } }) - t.Run("not registered", func(t *testing.T) { + t.Run("get non registered token teller", func(t *testing.T) { defer func() { if r := recover(); r == nil { t.Errorf("Expected panic for non-registered token") } }() - GetTokenTeller("not_registered") + GetTokenTeller("not_registered_teller") }) } @@ -129,21 +129,21 @@ func TestTellerMethod(t *testing.T) { } func TestIsRegistered(t *testing.T) { - t.Run("registered(by default)", func(t *testing.T) { + t.Run("check if token is registered", func(t *testing.T) { uassert.NoError(t, IsRegistered(tokenPath)) }) - t.Run("not registered", func(t *testing.T) { - uassert.Error(t, IsRegistered("not_registered")) + t.Run("check if token is not registered", func(t *testing.T) { + uassert.Error(t, IsRegistered("not_registered_token")) }) } func TestMustRegistered(t *testing.T) { - t.Run("registered(by default)", func(t *testing.T) { + t.Run("must be registered", func(t *testing.T) { MustRegistered(tokenPath) }) - t.Run("not registered", func(t *testing.T) { + t.Run("panic for non registered token", func(t *testing.T) { defer func() { if r := recover(); r == nil { t.Errorf("Expected panic for non-registered token") From 1aa50f60416f435e93985cbf0812f3a7fa5a6ca1 Mon Sep 17 00:00:00 2001 From: n3wbie Date: Tue, 10 Dec 2024 19:42:41 +0900 Subject: [PATCH 36/44] fix: typo --- _deploy/r/gnoswap/common/grc20reg_helper_test.gno | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/_deploy/r/gnoswap/common/grc20reg_helper_test.gno b/_deploy/r/gnoswap/common/grc20reg_helper_test.gno index 31c7aa9e8..086320ed2 100644 --- a/_deploy/r/gnoswap/common/grc20reg_helper_test.gno +++ b/_deploy/r/gnoswap/common/grc20reg_helper_test.gno @@ -85,7 +85,7 @@ func TestGetTokenTeller(t *testing.T) { t.Run("get non registered token teller", func(t *testing.T) { defer func() { if r := recover(); r == nil { - t.Errorf("Expected panic for non-registered token") + t.Errorf("Expected panic for non-registered token teller") } }() GetTokenTeller("not_registered_teller") From 099f1cb15ad3d0863e728d01735d69aabe1471c3 Mon Sep 17 00:00:00 2001 From: n3wbie Date: Tue, 10 Dec 2024 20:05:41 +0900 Subject: [PATCH 37/44] test: define test helper function --- _deploy/p/gnoswap/pool/swap_math_test.gno | 34 +++++++++++++---------- 1 file changed, 19 insertions(+), 15 deletions(-) diff --git a/_deploy/p/gnoswap/pool/swap_math_test.gno b/_deploy/p/gnoswap/pool/swap_math_test.gno index 356664824..587b83909 100644 --- a/_deploy/p/gnoswap/pool/swap_math_test.gno +++ b/_deploy/p/gnoswap/pool/swap_math_test.gno @@ -22,12 +22,12 @@ func TestSwapMathComputeSwapStepStr(t *testing.T) { }{ { name: "exact amount in that gets capped at price target in one for zero", - currentX96: encodePriceSqrt("1", "1"), - targetX96: encodePriceSqrt("101", "100"), + currentX96: encodePriceSqrt(t, "1", "1"), + targetX96: encodePriceSqrt(t, "101", "100"), liquidity: u256.MustFromDecimal("2000000000000000000"), amountRemaining: i256.MustFromDecimal("1000000000000000000"), feePips: 600, - sqrtNextX96: encodePriceSqrt("101", "100"), + sqrtNextX96: encodePriceSqrt(t, "101", "100"), chkSqrtNextX96: func(sqrtRatioNextX96, priceTarget *u256.Uint) { uassert.True(t, sqrtRatioNextX96.Eq(priceTarget)) }, @@ -37,12 +37,12 @@ func TestSwapMathComputeSwapStepStr(t *testing.T) { }, { name: "exact amount out that gets capped at price target in one for zero", - currentX96: encodePriceSqrt("1", "1"), - targetX96: encodePriceSqrt("101", "100"), + currentX96: encodePriceSqrt(t, "1", "1"), + targetX96: encodePriceSqrt(t, "101", "100"), liquidity: u256.MustFromDecimal("2000000000000000000"), amountRemaining: i256.MustFromDecimal("-1000000000000000000"), feePips: 600, - sqrtNextX96: encodePriceSqrt("101", "100"), + sqrtNextX96: encodePriceSqrt(t, "101", "100"), chkSqrtNextX96: func(sqrtRatioNextX96, priceTarget *u256.Uint) { uassert.True(t, sqrtRatioNextX96.Eq(priceTarget)) }, @@ -52,11 +52,11 @@ func TestSwapMathComputeSwapStepStr(t *testing.T) { }, { name: "exact amount in that is fully spent in one for zero", - currentX96: encodePriceSqrt("1", "1"), - targetX96: encodePriceSqrt("1000", "100"), + currentX96: encodePriceSqrt(t, "1", "1"), + targetX96: encodePriceSqrt(t, "1000", "100"), liquidity: u256.MustFromDecimal("2000000000000000000"), amountRemaining: i256.MustFromDecimal("1000000000000000000"), - sqrtNextX96: encodePriceSqrt("1000", "100"), + sqrtNextX96: encodePriceSqrt(t, "1000", "100"), feePips: 600, chkSqrtNextX96: func(sqrtRatioNextX96, priceTarget *u256.Uint) { uassert.True(t, sqrtRatioNextX96.Lte(priceTarget)) @@ -67,12 +67,12 @@ func TestSwapMathComputeSwapStepStr(t *testing.T) { }, { name: "exact amount out that is fully received in one for zero", - currentX96: encodePriceSqrt("1", "1"), - targetX96: encodePriceSqrt("1000", "100"), + currentX96: encodePriceSqrt(t, "1", "1"), + targetX96: encodePriceSqrt(t, "1000", "100"), liquidity: u256.MustFromDecimal("2000000000000000000"), amountRemaining: i256.MustFromDecimal("-1000000000000000000"), feePips: 600, - sqrtNextX96: encodePriceSqrt("1000", "100"), + sqrtNextX96: encodePriceSqrt(t, "1000", "100"), chkSqrtNextX96: func(sqrtRatioNextX96, priceTarget *u256.Uint) { uassert.True(t, sqrtRatioNextX96.Lt(priceTarget)) }, @@ -169,7 +169,9 @@ func TestSwapMathComputeSwapStepStr(t *testing.T) { } // encodePriceSqrt calculates the sqrt((reserve1 << 192) / reserve0) -func encodePriceSqrt(reserve1, reserve0 string) *u256.Uint { +func encodePriceSqrt(t *testing.T, reserve1, reserve0 string) *u256.Uint { + t.Helper() + reserve1Uint := u256.MustFromDecimal(reserve1) reserve0Uint := u256.MustFromDecimal(reserve0) @@ -185,11 +187,13 @@ func encodePriceSqrt(reserve1, reserve0 string) *u256.Uint { ratioX192 := new(u256.Uint).Div(numerator, reserve0Uint) // Return sqrt(ratioX192) - return sqrt(ratioX192) + return sqrt(t, ratioX192) } // sqrt computes the integer square root of a u256.Uint -func sqrt(x *u256.Uint) *u256.Uint { +func sqrt(t *testing.T, x *u256.Uint) *u256.Uint { + t.Helper() + if x.IsZero() { return u256.NewUint(0) } From 5654380f6ea9f8dda54fd0d80cb31a317d7ec5c3 Mon Sep 17 00:00:00 2001 From: n3wbie Date: Tue, 10 Dec 2024 20:09:43 +0900 Subject: [PATCH 38/44] test: remove unnecessary initialization --- _deploy/p/gnoswap/pool/swap_math_test.gno | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/_deploy/p/gnoswap/pool/swap_math_test.gno b/_deploy/p/gnoswap/pool/swap_math_test.gno index 587b83909..7455b7e64 100644 --- a/_deploy/p/gnoswap/pool/swap_math_test.gno +++ b/_deploy/p/gnoswap/pool/swap_math_test.gno @@ -201,9 +201,10 @@ func sqrt(t *testing.T, x *u256.Uint) *u256.Uint { z := new(u256.Uint).Set(x) y := new(u256.Uint).Rsh(z, 1) // Initial guess is x / 2 + temp := new(u256.Uint) for y.Cmp(z) < 0 { z.Set(y) - temp := new(u256.Uint).Div(x, z) + temp.Div(x, z) y.Add(z, temp).Rsh(y, 1) } return z From f29ae3f67c094056622d6ff59e92591092a4732a Mon Sep 17 00:00:00 2001 From: 0xTopaz Date: Wed, 11 Dec 2024 01:24:00 +0900 Subject: [PATCH 39/44] chore: update sonar option - exclusion test code - ignore related with sonar local test file --- .gitignore | 5 ++++- sonar-project.properties | 15 +++++++++++++++ 2 files changed, 19 insertions(+), 1 deletion(-) create mode 100644 sonar-project.properties diff --git a/.gitignore b/.gitignore index 34c9fa96f..48acd8abf 100644 --- a/.gitignore +++ b/.gitignore @@ -348,4 +348,7 @@ pyrightconfig.json .history .ionide -# End of https://www.toptal.com/developers/gitignore/api/macos,go,goland+all,visualstudiocode,dotenv,python,jupyternotebooks \ No newline at end of file +# End of https://www.toptal.com/developers/gitignore/api/macos,go,goland+all,visualstudiocode,dotenv,python,jupyternotebooks + +.scannerwork + diff --git a/sonar-project.properties b/sonar-project.properties new file mode 100644 index 000000000..1a754b874 --- /dev/null +++ b/sonar-project.properties @@ -0,0 +1,15 @@ +sonar.projectKey=gnoswap-labs_gnoswap-swap +sonar.organization=gnoswap-labs +sonar.exclusions=**/*_test.gno + +# This is the name and version displayed in the SonarCloud UI. +#sonar.projectName=gnoswap-batch-service +#sonar.projectVersion=1.0 + + +# Path is relative to the sonar-project.properties file. Replace "\" by "/" on Windows. +#sonar.sources=. + +# Encoding of the source code. Default is default system encoding +#sonar.sourceEncoding=UTF-8 + From c22562621bbc8bd386716876e274e7403fb53b1b Mon Sep 17 00:00:00 2001 From: n3wbie Date: Wed, 11 Dec 2024 12:51:10 +0900 Subject: [PATCH 40/44] remove: duplicate token register check --- pool/pool_manager.gno | 6 ------ 1 file changed, 6 deletions(-) diff --git a/pool/pool_manager.gno b/pool/pool_manager.gno index cf318736b..9598bb4f8 100644 --- a/pool/pool_manager.gno +++ b/pool/pool_manager.gno @@ -227,9 +227,6 @@ func DoesPoolPathExist(poolPath string) bool { // It constructs the poolPath from the given parameters and returns the corresponding pool. // Returns pool struct func GetPool(token0Path, token1Path string, fee uint32) *Pool { - common.MustRegistered(token0Path) - common.MustRegistered(token1Path) - poolPath := GetPoolPath(token0Path, token1Path, fee) pool, exist := pools[poolPath] if !exist { @@ -258,9 +255,6 @@ func GetPoolFromPoolPath(poolPath string) *Pool { // GetPoolPath generates a poolPath from the given token paths and fee. // The poolPath is constructed by joining the token paths and fee with colons. func GetPoolPath(token0Path, token1Path string, fee uint32) string { - common.MustRegistered(token0Path) - common.MustRegistered(token1Path) - // TODO: this check is not unnecessary, if we are sure that // all the token paths in the pool are sorted in alphabetical order. if strings.Compare(token1Path, token0Path) < 0 { From 7eab407918c9d737b7d78dccd06b8f83eb0a033a Mon Sep 17 00:00:00 2001 From: n3wbie Date: Wed, 11 Dec 2024 12:58:09 +0900 Subject: [PATCH 41/44] fix: typo --- pool/pool_manager_test.gno | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pool/pool_manager_test.gno b/pool/pool_manager_test.gno index 896e4ce2b..328315b56 100644 --- a/pool/pool_manager_test.gno +++ b/pool/pool_manager_test.gno @@ -119,7 +119,7 @@ func TestCreatePool(t *testing.T) { { name: "success - normal token pair", token0Path: barPath, - token1Path: GetPoolFromPoolPath, + token1Path: fooPath, fee: 3000, sqrtPrice: "4295128740", }, @@ -134,7 +134,7 @@ func TestCreatePool(t *testing.T) { }, { name: "fail - tokens not in order", - token0Path: GetPoolFromPoolPath, + token0Path: fooPath, token1Path: barPath, fee: 3000, sqrtPrice: "4295128740", @@ -144,7 +144,7 @@ func TestCreatePool(t *testing.T) { { name: "fail - pool already exists", token0Path: barPath, - token1Path: GetPoolFromPoolPath, + token1Path: fooPath, fee: 3000, sqrtPrice: "4295128740", shouldPanic: true, From 2e656adc31665630b6051aa8b9f1ca2f7f0c22d8 Mon Sep 17 00:00:00 2001 From: 0xTopaz Date: Wed, 11 Dec 2024 13:28:44 +0900 Subject: [PATCH 42/44] refactor: duplicate token register check - GetPoolPath has check function --- pool/pool.gno | 12 ------------ pool/pool_manager.gno | 8 ++++---- 2 files changed, 4 insertions(+), 16 deletions(-) diff --git a/pool/pool.gno b/pool/pool.gno index ec96d53d0..4816c6b11 100644 --- a/pool/pool.gno +++ b/pool/pool.gno @@ -28,9 +28,6 @@ func Mint( positionCaller std.Address, ) (string, string) { common.IsHalted() - common.MustRegistered(token0Path) - common.MustRegistered(token1Path) - if common.GetLimitCaller() { caller := std.PrevRealm().Addr() if err := common.PositionOnly(caller); err != nil { @@ -77,9 +74,6 @@ func Burn( liquidityAmount string, // uint128 ) (string, string) { // uint256 x2 common.IsHalted() - common.MustRegistered(token0Path) - common.MustRegistered(token1Path) - caller := std.PrevRealm().Addr() if common.GetLimitCaller() { if err := common.PositionOnly(caller); err != nil { @@ -125,9 +119,6 @@ func Collect( amount1Requested string, ) (string, string) { common.IsHalted() - common.MustRegistered(token0Path) - common.MustRegistered(token1Path) - if common.GetLimitCaller() { caller := std.PrevRealm().Addr() if err := common.PositionOnly(caller); err != nil { @@ -225,9 +216,6 @@ func Swap( payer std.Address, // router ) (string, string) { common.IsHalted() - common.MustRegistered(token0Path) - common.MustRegistered(token1Path) - if common.GetLimitCaller() { caller := std.PrevRealm().Addr() if err := common.RouterOnly(caller); err != nil { diff --git a/pool/pool_manager.gno b/pool/pool_manager.gno index 9598bb4f8..c8f5c25be 100644 --- a/pool/pool_manager.gno +++ b/pool/pool_manager.gno @@ -150,8 +150,7 @@ func CreatePool( // wrap first token0Path, token1Path = poolInfo.wrap() - common.MustRegistered(token0Path) - common.MustRegistered(token1Path) + poolPath := GetPoolPath(token0Path, token1Path, fee) // reinitialize poolInfo with wrapped tokens poolInfo = newPoolParams(token0Path, token1Path, fee, _sqrtPriceX96) @@ -174,8 +173,6 @@ func CreatePool( )) } - poolPath := GetPoolPath(token0Path, token1Path, fee) - // TODO: make this as a parameter prevAddr, prevRealm := getPrev() @@ -255,6 +252,9 @@ func GetPoolFromPoolPath(poolPath string) *Pool { // GetPoolPath generates a poolPath from the given token paths and fee. // The poolPath is constructed by joining the token paths and fee with colons. func GetPoolPath(token0Path, token1Path string, fee uint32) string { + common.MustRegistered(token0Path) + common.MustRegistered(token1Path) + // TODO: this check is not unnecessary, if we are sure that // all the token paths in the pool are sorted in alphabetical order. if strings.Compare(token1Path, token0Path) < 0 { From ce2e2923ce5eb80ac2e24c6817fd8b67e3a9490b Mon Sep 17 00:00:00 2001 From: Blake <104744707+r3v4s@users.noreply.github.com> Date: Sun, 15 Dec 2024 14:10:20 +0900 Subject: [PATCH 43/44] chore: remove `Pool` prefix from pool's receiver getter (#423) --- pool/getter.gno | 58 +++++++++++++++---------------- pool/pool.gno | 34 +++++++++--------- pool/position.gno | 10 +++--- pool/tick.gno | 16 ++++----- position/_RPC_api.gno | 16 ++++----- position/liquidity_management.gno | 2 +- position/position.gno | 10 +++--- 7 files changed, 73 insertions(+), 73 deletions(-) diff --git a/pool/getter.gno b/pool/getter.gno index d71ecbf1e..f962846fa 100644 --- a/pool/getter.gno +++ b/pool/getter.gno @@ -11,119 +11,119 @@ func PoolGetPoolList() []string { } func PoolGetToken0Path(poolPath string) string { - return mustGetPool(poolPath).PoolGetToken0Path() + return mustGetPool(poolPath).GetToken0Path() } func PoolGetToken1Path(poolPath string) string { - return mustGetPool(poolPath).PoolGetToken1Path() + return mustGetPool(poolPath).GetToken1Path() } func PoolGetFee(poolPath string) uint32 { - return mustGetPool(poolPath).PoolGetFee() + return mustGetPool(poolPath).GetFee() } func PoolGetBalanceToken0(poolPath string) string { - return mustGetPool(poolPath).PoolGetBalanceToken0().ToString() + return mustGetPool(poolPath).GetBalanceToken0().ToString() } func PoolGetBalanceToken1(poolPath string) string { - return mustGetPool(poolPath).PoolGetBalanceToken1().ToString() + return mustGetPool(poolPath).GetBalanceToken1().ToString() } func PoolGetTickSpacing(poolPath string) int32 { - return mustGetPool(poolPath).PoolGetTickSpacing() + return mustGetPool(poolPath).GetTickSpacing() } func PoolGetMaxLiquidityPerTick(poolPath string) string { - return mustGetPool(poolPath).PoolGetMaxLiquidityPerTick().ToString() + return mustGetPool(poolPath).GetMaxLiquidityPerTick().ToString() } func PoolGetSlot0SqrtPriceX96(poolPath string) string { - return mustGetPool(poolPath).PoolGetSlot0SqrtPriceX96().ToString() + return mustGetPool(poolPath).GetSlot0SqrtPriceX96().ToString() } func PoolGetSlot0Tick(poolPath string) int32 { - return mustGetPool(poolPath).PoolGetSlot0Tick() + return mustGetPool(poolPath).GetSlot0Tick() } func PoolGetSlot0FeeProtocol(poolPath string) uint8 { - return mustGetPool(poolPath).PoolGetSlot0FeeProtocol() + return mustGetPool(poolPath).GetSlot0FeeProtocol() } func PoolGetSlot0Unlocked(poolPath string) bool { - return mustGetPool(poolPath).PoolGetSlot0Unlocked() + return mustGetPool(poolPath).GetSlot0Unlocked() } func PoolGetFeeGrowthGlobal0X128(poolPath string) string { - return mustGetPool(poolPath).PoolGetFeeGrowthGlobal0X128().ToString() + return mustGetPool(poolPath).GetFeeGrowthGlobal0X128().ToString() } func PoolGetFeeGrowthGlobal1X128(poolPath string) string { - return mustGetPool(poolPath).PoolGetFeeGrowthGlobal1X128().ToString() + return mustGetPool(poolPath).GetFeeGrowthGlobal1X128().ToString() } func PoolGetProtocolFeesToken0(poolPath string) string { - return mustGetPool(poolPath).PoolGetProtocolFeesToken0().ToString() + return mustGetPool(poolPath).GetProtocolFeesToken0().ToString() } func PoolGetProtocolFeesToken1(poolPath string) string { - return mustGetPool(poolPath).PoolGetProtocolFeesToken1().ToString() + return mustGetPool(poolPath).GetProtocolFeesToken1().ToString() } func PoolGetLiquidity(poolPath string) string { - return mustGetPool(poolPath).PoolGetLiquidity().ToString() + return mustGetPool(poolPath).GetLiquidity().ToString() } // position func PoolGetPositionLiquidity(poolPath, key string) string { - return mustGetPool(poolPath).PoolGetPositionLiquidity(key).ToString() + return mustGetPool(poolPath).GetPositionLiquidity(key).ToString() } func PoolGetPositionFeeGrowthInside0LastX128(poolPath, key string) string { - return mustGetPool(poolPath).PoolGetPositionFeeGrowthInside0LastX128(key).ToString() + return mustGetPool(poolPath).GetPositionFeeGrowthInside0LastX128(key).ToString() } func PoolGetPositionFeeGrowthInside1LastX128(poolPath, key string) string { - return mustGetPool(poolPath).PoolGetPositionFeeGrowthInside1LastX128(key).ToString() + return mustGetPool(poolPath).GetPositionFeeGrowthInside1LastX128(key).ToString() } func PoolGetPositionTokensOwed0(poolPath, key string) string { - return mustGetPool(poolPath).PoolGetPositionTokensOwed0(key).ToString() + return mustGetPool(poolPath).GetPositionTokensOwed0(key).ToString() } func PoolGetPositionTokensOwed1(poolPath, key string) string { - return mustGetPool(poolPath).PoolGetPositionTokensOwed1(key).ToString() + return mustGetPool(poolPath).GetPositionTokensOwed1(key).ToString() } // tick func PoolGetTickLiquidityGross(poolPath string, tick int32) string { - return mustGetPool(poolPath).PoolGetTickLiquidityGross(tick).ToString() + return mustGetPool(poolPath).GetTickLiquidityGross(tick).ToString() } func PoolGetTickLiquidityNet(poolPath string, tick int32) string { - return mustGetPool(poolPath).PoolGetTickLiquidityNet(tick).ToString() + return mustGetPool(poolPath).GetTickLiquidityNet(tick).ToString() } func PoolGetTickFeeGrowthOutside0X128(poolPath string, tick int32) string { - return mustGetPool(poolPath).PoolGetTickFeeGrowthOutside0X128(tick).ToString() + return mustGetPool(poolPath).GetTickFeeGrowthOutside0X128(tick).ToString() } func PoolGetTickFeeGrowthOutside1X128(poolPath string, tick int32) string { - return mustGetPool(poolPath).PoolGetTickFeeGrowthOutside1X128(tick).ToString() + return mustGetPool(poolPath).GetTickFeeGrowthOutside1X128(tick).ToString() } func PoolGetTickCumulativeOutside(poolPath string, tick int32) int64 { - return mustGetPool(poolPath).PoolGetTickCumulativeOutside(tick) + return mustGetPool(poolPath).GetTickCumulativeOutside(tick) } func PoolGetTickSecondsPerLiquidityOutsideX128(poolPath string, tick int32) string { - return mustGetPool(poolPath).PoolGetTickSecondsPerLiquidityOutsideX128(tick).ToString() + return mustGetPool(poolPath).GetTickSecondsPerLiquidityOutsideX128(tick).ToString() } func PoolGetTickSecondsOutside(poolPath string, tick int32) uint32 { - return mustGetPool(poolPath).PoolGetTickSecondsOutside(tick) + return mustGetPool(poolPath).GetTickSecondsOutside(tick) } func PoolGetTickInitialized(poolPath string, tick int32) bool { - return mustGetPool(poolPath).PoolGetTickInitialized(tick) + return mustGetPool(poolPath).GetTickInitialized(tick) } diff --git a/pool/pool.gno b/pool/pool.gno index 4816c6b11..cfb655f0e 100644 --- a/pool/pool.gno +++ b/pool/pool.gno @@ -985,71 +985,71 @@ func checkAmountRange(amount *u256.Uint) (uint64, error) { } // receiver getters -func (p *Pool) PoolGetToken0Path() string { +func (p *Pool) GetToken0Path() string { return p.token0Path } -func (p *Pool) PoolGetToken1Path() string { +func (p *Pool) GetToken1Path() string { return p.token1Path } -func (p *Pool) PoolGetFee() uint32 { +func (p *Pool) GetFee() uint32 { return p.fee } -func (p *Pool) PoolGetBalanceToken0() *u256.Uint { +func (p *Pool) GetBalanceToken0() *u256.Uint { return p.balances.token0 } -func (p *Pool) PoolGetBalanceToken1() *u256.Uint { +func (p *Pool) GetBalanceToken1() *u256.Uint { return p.balances.token1 } -func (p *Pool) PoolGetTickSpacing() int32 { +func (p *Pool) GetTickSpacing() int32 { return p.tickSpacing } -func (p *Pool) PoolGetMaxLiquidityPerTick() *u256.Uint { +func (p *Pool) GetMaxLiquidityPerTick() *u256.Uint { return p.maxLiquidityPerTick } -func (p *Pool) PoolGetSlot0() Slot0 { +func (p *Pool) GetSlot0() Slot0 { return p.slot0 } -func (p *Pool) PoolGetSlot0SqrtPriceX96() *u256.Uint { +func (p *Pool) GetSlot0SqrtPriceX96() *u256.Uint { return p.slot0.sqrtPriceX96 } -func (p *Pool) PoolGetSlot0Tick() int32 { +func (p *Pool) GetSlot0Tick() int32 { return p.slot0.tick } -func (p *Pool) PoolGetSlot0FeeProtocol() uint8 { +func (p *Pool) GetSlot0FeeProtocol() uint8 { return p.slot0.feeProtocol } -func (p *Pool) PoolGetSlot0Unlocked() bool { +func (p *Pool) GetSlot0Unlocked() bool { return p.slot0.unlocked } -func (p *Pool) PoolGetFeeGrowthGlobal0X128() *u256.Uint { +func (p *Pool) GetFeeGrowthGlobal0X128() *u256.Uint { return p.feeGrowthGlobal0X128 } -func (p *Pool) PoolGetFeeGrowthGlobal1X128() *u256.Uint { +func (p *Pool) GetFeeGrowthGlobal1X128() *u256.Uint { return p.feeGrowthGlobal1X128 } -func (p *Pool) PoolGetProtocolFeesToken0() *u256.Uint { +func (p *Pool) GetProtocolFeesToken0() *u256.Uint { return p.protocolFees.token0 } -func (p *Pool) PoolGetProtocolFeesToken1() *u256.Uint { +func (p *Pool) GetProtocolFeesToken1() *u256.Uint { return p.protocolFees.token1 } -func (p *Pool) PoolGetLiquidity() *u256.Uint { +func (p *Pool) GetLiquidity() *u256.Uint { return p.liquidity } diff --git a/pool/position.gno b/pool/position.gno index 9b020ef5a..64d37e9d5 100644 --- a/pool/position.gno +++ b/pool/position.gno @@ -107,23 +107,23 @@ func positionUpdate( // receiver getters -func (p *Pool) PoolGetPositionLiquidity(key string) *u256.Uint { +func (p *Pool) GetPositionLiquidity(key string) *u256.Uint { return p.mustGetPosition(key).liquidity } -func (p *Pool) PoolGetPositionFeeGrowthInside0LastX128(key string) *u256.Uint { +func (p *Pool) GetPositionFeeGrowthInside0LastX128(key string) *u256.Uint { return p.mustGetPosition(key).feeGrowthInside0LastX128 } -func (p *Pool) PoolGetPositionFeeGrowthInside1LastX128(key string) *u256.Uint { +func (p *Pool) GetPositionFeeGrowthInside1LastX128(key string) *u256.Uint { return p.mustGetPosition(key).feeGrowthInside1LastX128 } -func (p *Pool) PoolGetPositionTokensOwed0(key string) *u256.Uint { +func (p *Pool) GetPositionTokensOwed0(key string) *u256.Uint { return p.mustGetPosition(key).tokensOwed0 } -func (p *Pool) PoolGetPositionTokensOwed1(key string) *u256.Uint { +func (p *Pool) GetPositionTokensOwed1(key string) *u256.Uint { return p.mustGetPosition(key).tokensOwed1 } diff --git a/pool/tick.gno b/pool/tick.gno index da19e939a..d325826fb 100644 --- a/pool/tick.gno +++ b/pool/tick.gno @@ -144,35 +144,35 @@ func getFeeGrowthAboveX128( } // receiver getters -func (p *Pool) PoolGetTickLiquidityGross(tick int32) *u256.Uint { +func (p *Pool) GetTickLiquidityGross(tick int32) *u256.Uint { return p.mustGetTick(tick).liquidityGross } -func (p *Pool) PoolGetTickLiquidityNet(tick int32) *i256.Int { +func (p *Pool) GetTickLiquidityNet(tick int32) *i256.Int { return p.mustGetTick(tick).liquidityNet } -func (p *Pool) PoolGetTickFeeGrowthOutside0X128(tick int32) *u256.Uint { +func (p *Pool) GetTickFeeGrowthOutside0X128(tick int32) *u256.Uint { return p.mustGetTick(tick).feeGrowthOutside0X128 } -func (p *Pool) PoolGetTickFeeGrowthOutside1X128(tick int32) *u256.Uint { +func (p *Pool) GetTickFeeGrowthOutside1X128(tick int32) *u256.Uint { return p.mustGetTick(tick).feeGrowthOutside1X128 } -func (p *Pool) PoolGetTickCumulativeOutside(tick int32) int64 { +func (p *Pool) GetTickCumulativeOutside(tick int32) int64 { return p.mustGetTick(tick).tickCumulativeOutside } -func (p *Pool) PoolGetTickSecondsPerLiquidityOutsideX128(tick int32) *u256.Uint { +func (p *Pool) GetTickSecondsPerLiquidityOutsideX128(tick int32) *u256.Uint { return p.mustGetTick(tick).secondsPerLiquidityOutsideX128 } -func (p *Pool) PoolGetTickSecondsOutside(tick int32) uint32 { +func (p *Pool) GetTickSecondsOutside(tick int32) uint32 { return p.mustGetTick(tick).secondsOutside } -func (p *Pool) PoolGetTickInitialized(tick int32) bool { +func (p *Pool) GetTickInitialized(tick int32) bool { return p.mustGetTick(tick).initialized } diff --git a/position/_RPC_api.gno b/position/_RPC_api.gno index 5abf9a1b6..b42e871d1 100644 --- a/position/_RPC_api.gno +++ b/position/_RPC_api.gno @@ -390,7 +390,7 @@ func rpcMakePosition(lpTokenId uint64) RpcPosition { burned := isBurned(lpTokenId) pool := pl.GetPoolFromPoolPath(position.poolKey) - currentX96 := pool.PoolGetSlot0SqrtPriceX96() + currentX96 := pool.GetSlot0SqrtPriceX96() lowerX96 := common.TickMathGetSqrtRatioAtTick(position.tickLower) upperX96 := common.TickMathGetSqrtRatioAtTick(position.tickUpper) @@ -439,24 +439,24 @@ func unclaimedFee(tokenId uint64) (*i256.Int, *i256.Int) { poolKey := positions[tokenId].poolKey pool := pl.GetPoolFromPoolPath(poolKey) - currentTick := pool.PoolGetSlot0Tick() + currentTick := pool.GetSlot0Tick() - _feeGrowthGlobal0X128 := pool.PoolGetFeeGrowthGlobal0X128() // u256 + _feeGrowthGlobal0X128 := pool.GetFeeGrowthGlobal0X128() // u256 feeGrowthGlobal0X128 := i256.FromUint256(_feeGrowthGlobal0X128) // i256 - _feeGrowthGlobal1X128 := pool.PoolGetFeeGrowthGlobal1X128() // u256 + _feeGrowthGlobal1X128 := pool.GetFeeGrowthGlobal1X128() // u256 feeGrowthGlobal1X128 := i256.FromUint256(_feeGrowthGlobal1X128) // i256 - _tickUpperFeeGrowthOutside0X128 := pool.PoolGetTickFeeGrowthOutside0X128(tickUpper) // u256 + _tickUpperFeeGrowthOutside0X128 := pool.GetTickFeeGrowthOutside0X128(tickUpper) // u256 tickUpperFeeGrowthOutside0X128 := i256.FromUint256(_tickUpperFeeGrowthOutside0X128) // i256 - _tickUpperFeeGrowthOutside1X128 := pool.PoolGetTickFeeGrowthOutside1X128(tickUpper) // u256 + _tickUpperFeeGrowthOutside1X128 := pool.GetTickFeeGrowthOutside1X128(tickUpper) // u256 tickUpperFeeGrowthOutside1X128 := i256.FromUint256(_tickUpperFeeGrowthOutside1X128) // i256 - _tickLowerFeeGrowthOutside0X128 := pool.PoolGetTickFeeGrowthOutside0X128(tickLower) // u256 + _tickLowerFeeGrowthOutside0X128 := pool.GetTickFeeGrowthOutside0X128(tickLower) // u256 tickLowerFeeGrowthOutside0X128 := i256.FromUint256(_tickLowerFeeGrowthOutside0X128) // i256 - _tickLowerFeeGrowthOutside1X128 := pool.PoolGetTickFeeGrowthOutside1X128(tickLower) // u256 + _tickLowerFeeGrowthOutside1X128 := pool.GetTickFeeGrowthOutside1X128(tickLower) // u256 tickLowerFeeGrowthOutside1X128 := i256.FromUint256(_tickLowerFeeGrowthOutside1X128) // i256 _feeGrowthInside0LastX128 := positions[tokenId].feeGrowthInside0LastX128 // u256 diff --git a/position/liquidity_management.gno b/position/liquidity_management.gno index 417b6a4a0..8b74794ca 100644 --- a/position/liquidity_management.gno +++ b/position/liquidity_management.gno @@ -16,7 +16,7 @@ import ( func addLiquidity(params AddLiquidityParams) (*u256.Uint, *u256.Uint, *u256.Uint) { pool := pl.GetPoolFromPoolPath(params.poolKey) - sqrtPriceX96 := pool.PoolGetSlot0SqrtPriceX96() + sqrtPriceX96 := pool.GetSlot0SqrtPriceX96() sqrtRatioAX96 := common.TickMathGetSqrtRatioAtTick(params.tickLower) sqrtRatioBX96 := common.TickMathGetSqrtRatioAtTick(params.tickUpper) diff --git a/position/position.gno b/position/position.gno index 7e5e37a2b..d4ffb1a88 100644 --- a/position/position.gno +++ b/position/position.gno @@ -189,7 +189,7 @@ func mint(params MintParams) (uint64, *u256.Uint, *u256.Uint, *u256.Uint) { nextId++ positionKey := positionKeyCompute(GetOrigPkgAddr(), params.tickLower, params.tickUpper) - _feeGrowthInside0LastX128, _feeGrowthInside1LastX128 := pool.PoolGetPositionFeeGrowthInside0LastX128(positionKey), pool.PoolGetPositionFeeGrowthInside1LastX128(positionKey) + _feeGrowthInside0LastX128, _feeGrowthInside1LastX128 := pool.GetPositionFeeGrowthInside0LastX128(positionKey), pool.GetPositionFeeGrowthInside1LastX128(positionKey) feeGrowthInside0LastX128 := u256.MustFromDecimal(_feeGrowthInside0LastX128.ToString()) feeGrowthInside1LastX128 := u256.MustFromDecimal(_feeGrowthInside1LastX128.ToString()) @@ -320,7 +320,7 @@ func increaseLiquidity(params IncreaseLiquidityParams) (uint64, *u256.Uint, *u25 pool := pl.GetPoolFromPoolPath(position.poolKey) positionKey := positionKeyCompute(GetOrigPkgAddr(), position.tickLower, position.tickUpper) - _feeGrowthInside0LastX128, _feeGrowthInside1LastX128 := pool.PoolGetPositionFeeGrowthInside0LastX128(positionKey), pool.PoolGetPositionFeeGrowthInside1LastX128(positionKey) + _feeGrowthInside0LastX128, _feeGrowthInside1LastX128 := pool.GetPositionFeeGrowthInside0LastX128(positionKey), pool.GetPositionFeeGrowthInside1LastX128(positionKey) feeGrowthInside0LastX128 := u256.MustFromDecimal(_feeGrowthInside0LastX128.ToString()) feeGrowthInside1LastX128 := u256.MustFromDecimal(_feeGrowthInside1LastX128.ToString()) @@ -446,7 +446,7 @@ func decreaseLiquidity(params DecreaseLiquidityParams) (uint64, *u256.Uint, *u25 verifyBurnedAmounts(burnedAmount0, burnedAmount1, params.amount0Min, params.amount1Min) positionKey := positionKeyCompute(GetOrigPkgAddr(), position.tickLower, position.tickUpper) - _feeGrowthInside0LastX128, _feeGrowthInside1LastX128 := pool.PoolGetPositionFeeGrowthInside0LastX128(positionKey), pool.PoolGetPositionFeeGrowthInside1LastX128(positionKey) + _feeGrowthInside0LastX128, _feeGrowthInside1LastX128 := pool.GetPositionFeeGrowthInside0LastX128(positionKey), pool.GetPositionFeeGrowthInside1LastX128(positionKey) feeGrowthInside0LastX128 := u256.MustFromDecimal(_feeGrowthInside0LastX128.ToString()) feeGrowthInside1LastX128 := u256.MustFromDecimal(_feeGrowthInside1LastX128.ToString()) @@ -592,7 +592,7 @@ func Reposition( pool := pl.GetPoolFromPoolPath(position.poolKey) positionKey := positionKeyCompute(GetOrigPkgAddr(), tickLower, tickUpper) - _feeGrowthInside0LastX128, _feeGrowthInside1LastX128 := pool.PoolGetPositionFeeGrowthInside0LastX128(positionKey), pool.PoolGetPositionFeeGrowthInside1LastX128(positionKey) + _feeGrowthInside0LastX128, _feeGrowthInside1LastX128 := pool.GetPositionFeeGrowthInside0LastX128(positionKey), pool.GetPositionFeeGrowthInside1LastX128(positionKey) feeGrowthInside0LastX128 := u256.MustFromDecimal(_feeGrowthInside0LastX128.ToString()) feeGrowthInside1LastX128 := u256.MustFromDecimal(_feeGrowthInside1LastX128.ToString()) @@ -676,7 +676,7 @@ func CollectFee(tokenId uint64, unwrapResult bool) (uint64, string, string, stri positionKey := positionKeyCompute(GetOrigPkgAddr(), position.tickLower, position.tickUpper) pool := pl.GetPoolFromPoolPath(position.poolKey) - _feeGrowthInside0LastX128, _feeGrowthInside1LastX128 := pool.PoolGetPositionFeeGrowthInside0LastX128(positionKey), pool.PoolGetPositionFeeGrowthInside1LastX128(positionKey) + _feeGrowthInside0LastX128, _feeGrowthInside1LastX128 := pool.GetPositionFeeGrowthInside0LastX128(positionKey), pool.GetPositionFeeGrowthInside1LastX128(positionKey) feeGrowthInside0LastX128 := u256.MustFromDecimal(_feeGrowthInside0LastX128.ToString()) feeGrowthInside1LastX128 := u256.MustFromDecimal(_feeGrowthInside1LastX128.ToString()) From ff6eb546833eb035f22f61c7afc2db021cd21e22 Mon Sep 17 00:00:00 2001 From: 0xTopaz Date: Sun, 15 Dec 2024 20:16:32 +0900 Subject: [PATCH 44/44] refactor: Use Clone data in function calls to protect original data --- _deploy/p/gnoswap/pool/swap_math.gno | 127 ++++++++++++++-------- _deploy/p/gnoswap/pool/swap_math_test.gno | 62 +++++++++++ 2 files changed, 145 insertions(+), 44 deletions(-) diff --git a/_deploy/p/gnoswap/pool/swap_math.gno b/_deploy/p/gnoswap/pool/swap_math.gno index 6150981f3..81b9c2d28 100644 --- a/_deploy/p/gnoswap/pool/swap_math.gno +++ b/_deploy/p/gnoswap/pool/swap_math.gno @@ -5,14 +5,34 @@ import ( u256 "gno.land/p/gnoswap/uint256" ) +// SwapMathComputeSwapStepStr computes the next sqrt price, amount in, amount out, and fee amount +// Computes the result of swapping some amount in, or amount out, given the parameters of the swap +// The fee, plus the amount in, will never exceed the amount remaining if the swap's `amountSpecified` is positive +// +// input: +// - sqrtRatioCurrentX96: the current sqrt price of the pool +// - sqrtRatioTargetX96: The price that cannot be exceeded, from which the direction of the swap is inferred +// - liquidity: The usable liquidity of the pool +// - amountRemaining: How much input or output amount is remaining to be swapped in/out +// - feePips: The fee taken from the input amount, expressed in hundredths of a bip +// +// output: +// - sqrtRatioNextX96: The price after swapping the amount in/out, not to exceed the price target +// - amountIn: The amount to be swapped in, of either token0 or token1, based on the direction of the swap +// - amountOut: The amount to be received, of either token0 or token1, based on the direction of the swap +// - feeAmount: The amount of input that will be taken as a fee func SwapMathComputeSwapStepStr( - sqrtRatioCurrentX96 *u256.Uint, // uint160 - sqrtRatioTargetX96 *u256.Uint, // uint160 - liquidity *u256.Uint, // uint128 - amountRemaining *i256.Int, // int256 + sqrtRatioCurrentX96 *u256.Uint, + sqrtRatioTargetX96 *u256.Uint, + liquidity *u256.Uint, + amountRemaining *i256.Int, feePips uint64, -) (string, string, string, string) { // (sqrtRatioNextX96, amountIn, amountOut, feeAmount *u256.Uint) - isToken1Expensive := sqrtRatioCurrentX96.Gte(sqrtRatioTargetX96) +) (string, string, string, string) { + if sqrtRatioCurrentX96 == nil || sqrtRatioTargetX96 == nil || liquidity == nil || amountRemaining == nil { + panic("SwapMathComputeSwapStepStr: invalid input") + } + + zeroForOne := sqrtRatioCurrentX96.Gte(sqrtRatioTargetX96) // POSTIVIE == EXACT_IN => Estimated AmountOut // NEGATIVE == EXACT_OUT => Estimated AmountIn @@ -25,75 +45,94 @@ func SwapMathComputeSwapStepStr( if exactIn { amountRemainingLessFee := u256.MulDiv(amountRemaining.Abs(), u256.NewUint(1000000-feePips), u256.NewUint(1000000)) - - if isToken1Expensive { - amountIn = sqrtPriceMathGetAmount0DeltaHelper(sqrtRatioTargetX96, sqrtRatioCurrentX96, liquidity, true) + if zeroForOne { + amountIn = sqrtPriceMathGetAmount0DeltaHelper( + sqrtRatioTargetX96.Clone(), + sqrtRatioCurrentX96.Clone(), + liquidity.Clone(), + true) } else { - amountIn = sqrtPriceMathGetAmount1DeltaHelper(sqrtRatioCurrentX96, sqrtRatioTargetX96, liquidity, true) + amountIn = sqrtPriceMathGetAmount1DeltaHelper( + sqrtRatioCurrentX96.Clone(), + sqrtRatioTargetX96.Clone(), + liquidity.Clone(), + true) } if amountRemainingLessFee.Gte(amountIn) { - sqrtRatioNextX96 = sqrtRatioTargetX96 + sqrtRatioNextX96 = sqrtRatioTargetX96.Clone() } else { sqrtRatioNextX96 = sqrtPriceMathGetNextSqrtPriceFromInput( - sqrtRatioCurrentX96, - liquidity, - amountRemainingLessFee, - isToken1Expensive, + sqrtRatioCurrentX96.Clone(), + liquidity.Clone(), + amountRemainingLessFee.Clone(), + zeroForOne, ) } - } else { - if isToken1Expensive { - amountOut = sqrtPriceMathGetAmount1DeltaHelper(sqrtRatioTargetX96, sqrtRatioCurrentX96, liquidity, false) + if zeroForOne { + amountOut = sqrtPriceMathGetAmount1DeltaHelper( + sqrtRatioTargetX96.Clone(), + sqrtRatioCurrentX96.Clone(), + liquidity.Clone(), + false) } else { - amountOut = sqrtPriceMathGetAmount0DeltaHelper(sqrtRatioCurrentX96, sqrtRatioTargetX96, liquidity, false) + amountOut = sqrtPriceMathGetAmount0DeltaHelper( + sqrtRatioCurrentX96.Clone(), + sqrtRatioTargetX96.Clone(), + liquidity.Clone(), + false) } if amountRemaining.Abs().Gte(amountOut) { - sqrtRatioNextX96 = sqrtRatioTargetX96 + sqrtRatioNextX96 = sqrtRatioTargetX96.Clone() } else { sqrtRatioNextX96 = sqrtPriceMathGetNextSqrtPriceFromOutput( - sqrtRatioCurrentX96, - liquidity, + sqrtRatioCurrentX96.Clone(), + liquidity.Clone(), amountRemaining.Abs(), - isToken1Expensive, + zeroForOne, ) } } - max := sqrtRatioTargetX96.Eq(sqrtRatioNextX96) + isMax := sqrtRatioTargetX96.Eq(sqrtRatioNextX96) - if isToken1Expensive { - if max && exactIn { - amountIn = amountIn - } else { - amountIn = sqrtPriceMathGetAmount0DeltaHelper(sqrtRatioNextX96, sqrtRatioCurrentX96, liquidity, true) + if zeroForOne { + if !(isMax && exactIn) { + amountIn = sqrtPriceMathGetAmount0DeltaHelper( + sqrtRatioNextX96.Clone(), + sqrtRatioCurrentX96.Clone(), + liquidity.Clone(), + true) } - - if max && !exactIn { - amountOut = amountOut - } else { - amountOut = sqrtPriceMathGetAmount1DeltaHelper(sqrtRatioNextX96, sqrtRatioCurrentX96, liquidity, false) + if !(isMax && !exactIn) { + amountOut = sqrtPriceMathGetAmount1DeltaHelper( + sqrtRatioNextX96.Clone(), + sqrtRatioCurrentX96.Clone(), + liquidity.Clone(), + false) } } else { - if max && exactIn { - amountIn = amountIn - } else { - amountIn = sqrtPriceMathGetAmount1DeltaHelper(sqrtRatioCurrentX96, sqrtRatioNextX96, liquidity, true) + if !(isMax && exactIn) { + amountIn = sqrtPriceMathGetAmount1DeltaHelper( + sqrtRatioCurrentX96.Clone(), + sqrtRatioNextX96.Clone(), + liquidity.Clone(), + true) } - - if max && !exactIn { - amountOut = amountOut - } else { - amountOut = sqrtPriceMathGetAmount0DeltaHelper(sqrtRatioCurrentX96, sqrtRatioNextX96, liquidity, false) + if !(isMax && !exactIn) { + amountOut = sqrtPriceMathGetAmount0DeltaHelper( + sqrtRatioCurrentX96.Clone(), + sqrtRatioNextX96.Clone(), + liquidity.Clone(), + false) } } if !exactIn && amountOut.Gt(amountRemaining.Abs()) { amountOut = amountRemaining.Abs() } - if exactIn && !(sqrtRatioNextX96.Eq(sqrtRatioTargetX96)) { feeAmount = new(u256.Uint).Sub(amountRemaining.Abs(), amountIn) } else { diff --git a/_deploy/p/gnoswap/pool/swap_math_test.gno b/_deploy/p/gnoswap/pool/swap_math_test.gno index 7455b7e64..505a8e716 100644 --- a/_deploy/p/gnoswap/pool/swap_math_test.gno +++ b/_deploy/p/gnoswap/pool/swap_math_test.gno @@ -168,6 +168,68 @@ func TestSwapMathComputeSwapStepStr(t *testing.T) { } } +func TestSwapMathComputeSwapStepStrFail(t *testing.T) { + tests := []struct { + name string + currentX96, targetX96 *u256.Uint + liquidity *u256.Uint + amountRemaining *i256.Int + feePips uint64 + sqrtNextX96 *u256.Uint + chkSqrtNextX96 func(sqrtRatioNextX96, priceTarget *u256.Uint) + amountIn, amountOut, feeAmount string + shouldPanic bool + expectedMessage string + }{ + { + name: "input parameter is nil", + currentX96: nil, + targetX96: nil, + liquidity: nil, + amountRemaining: nil, + feePips: 600, + sqrtNextX96: encodePriceSqrt(t, "101", "100"), + chkSqrtNextX96: func(sqrtRatioNextX96, priceTarget *u256.Uint) { + uassert.True(t, sqrtRatioNextX96.Eq(priceTarget)) + }, + amountIn: "9975124224178055", + amountOut: "9925619580021728", + feeAmount: "5988667735148", + shouldPanic: true, + expectedMessage: "SwapMathComputeSwapStepStr: invalid input", + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + defer func() { + if r := recover(); r != nil { + if test.shouldPanic { + if errMsg, ok := r.(string); ok { + uassert.Equal(t, test.expectedMessage, errMsg) + } else { + t.Errorf("expected a panic with message, got: %v", r) + } + } else { + t.Errorf("unexpected panic: %v", r) + } + } else { + if test.shouldPanic { + t.Errorf("expected a panic, but none occurred") + } + } + }() + + SwapMathComputeSwapStepStr( + test.currentX96, + test.targetX96, + test.liquidity, + test.amountRemaining, + test.feePips) + }) + } +} + // encodePriceSqrt calculates the sqrt((reserve1 << 192) / reserve0) func encodePriceSqrt(t *testing.T, reserve1, reserve0 string) *u256.Uint { t.Helper()