diff --git a/_deploy/r/gnoswap/common/errors.gno b/_deploy/r/gnoswap/common/errors.gno index 8cf952f2..43bd3133 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 239c42ca..50f945e0 100644 --- a/_deploy/r/gnoswap/common/grc20reg_helper.gno +++ b/_deploy/r/gnoswap/common/grc20reg_helper.gno @@ -44,3 +44,14 @@ func IsRegistered(path string) error { } return nil } + +// 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, + ufmt.Sprintf("token(%s) is not registered", path), + )) + } +} diff --git a/_deploy/r/gnoswap/common/grc20reg_helper_test.gno b/_deploy/r/gnoswap/common/grc20reg_helper_test.gno index 4287a62d..086320ed 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") + t.Errorf("Expected panic for non-registered token teller") } }() - GetTokenTeller("not_registered") + GetTokenTeller("not_registered_teller") }) } @@ -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("check if token is registered", func(t *testing.T) { + uassert.NoError(t, IsRegistered(tokenPath)) + }) + + 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("must be registered", func(t *testing.T) { + MustRegistered(tokenPath) + }) + + 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") + } + }() + MustRegistered("not_registered") + }) } diff --git a/pool/_helper_test.gno b/pool/_helper_test.gno index 8deb8863..992d99cc 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.gno b/pool/pool.gno index 1ab2b869..4816c6b1 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.GetTokenTeller(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.GetTokenTeller(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) @@ -717,9 +725,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.MustRegistered(token0Path) + common.MustRegistered(token1Path) + caller := std.PrevRealm().Addr() if err := common.AdminOnly(caller); err != nil { panic(err) @@ -759,9 +770,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.MustRegistered(token0Path) + common.MustRegistered(token1Path) + caller := std.PrevRealm().Addr() if err := common.GovernanceOnly(caller); err != nil { panic(err) @@ -814,8 +828,11 @@ func collectProtocol( uAmount0 := amount0.Uint64() uAmount1 := amount1.Uint64() - transferByRegisterCall(pool.token0Path, recipient, uAmount0) - transferByRegisterCall(pool.token1Path, recipient, uAmount1) + token0Teller := common.GetTokenTeller(pool.token0Path) + checkTransferError(token0Teller.Transfer(recipient, uAmount0)) + + token1Teller := common.GetTokenTeller(pool.token1Path) + checkTransferError(token1Teller.Transfer(recipient, uAmount1)) return amount0.ToString(), amount1.ToString() } @@ -866,7 +883,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 { @@ -943,12 +961,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.GetTokenTeller(tokenPath) + checkTransferError(token.TransferFrom(from, to, amountUint64)) // update pool balances if isToken0 { diff --git a/pool/pool_manager.gno b/pool/pool_manager.gno index e6652805..c8f5c25b 100644 --- a/pool/pool_manager.gno +++ b/pool/pool_manager.gno @@ -150,6 +150,8 @@ func CreatePool( // wrap first token0Path, token1Path = poolInfo.wrap() + poolPath := GetPoolPath(token0Path, token1Path, fee) + // reinitialize poolInfo with wrapped tokens poolInfo = newPoolParams(token0Path, token1Path, fee, _sqrtPriceX96) @@ -171,8 +173,6 @@ func CreatePool( )) } - poolPath := GetPoolPath(token0Path, token1Path, fee) - // TODO: make this as a parameter prevAddr, prevRealm := getPrev() @@ -252,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 { diff --git a/pool/pool_manager_test.gno b/pool/pool_manager_test.gno index da1eed7c..328315b5 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: barPath, + token1Path: fooPath, fee: 3000, sqrtPrice: "4295128740", }, { name: "fail - same tokens", - token0Path: "test/token0", - token1Path: "test/token0", + token0Path: barPath, + token1Path: barPath, 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: fooPath, + token1Path: barPath, 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: barPath, + token1Path: fooPath, 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/pool_test.gno b/pool/pool_test.gno index af02987d..c6ef82d4 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, ) diff --git a/pool/position_modify_test.gno b/pool/position_modify_test.gno index dec483e6..a6c2da6d 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") }) } diff --git a/pool/protocol_fee_withdrawal.gno b/pool/protocol_fee_withdrawal.gno index 4e79388f..cacc602c 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() @@ -64,8 +66,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( diff --git a/pool/protocol_fee_withdrawal_test.gno b/pool/protocol_fee_withdrawal_test.gno index 6cbec74e..241459d9 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, }, { 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 6702bd67..00000000 --- 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 4b6ba043..e92df972 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/token_register.gno b/pool/token_register.gno deleted file mode 100644 index 419b2ee4..00000000 --- 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 -} diff --git a/pool/utils.gno b/pool/utils.gno index 0ad4f4e6..b8d627a9 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(), + )) + } +}