From 15ae0831fdffbf94433d20a4d894a6f52c1246d4 Mon Sep 17 00:00:00 2001 From: n3wbie Date: Tue, 17 Dec 2024 22:09:18 +0900 Subject: [PATCH 1/7] feat: min/max for int64,uint64,int256,uint256 --- _deploy/r/gnoswap/common/math.gno | 70 ++++++++++ _deploy/r/gnoswap/common/math_test.gno | 172 +++++++++++++++++++++++++ 2 files changed, 242 insertions(+) create mode 100644 _deploy/r/gnoswap/common/math.gno create mode 100644 _deploy/r/gnoswap/common/math_test.gno diff --git a/_deploy/r/gnoswap/common/math.gno b/_deploy/r/gnoswap/common/math.gno new file mode 100644 index 00000000..ff69b1ba --- /dev/null +++ b/_deploy/r/gnoswap/common/math.gno @@ -0,0 +1,70 @@ +package common + +import ( + i256 "gno.land/p/gnoswap/int256" + u256 "gno.land/p/gnoswap/uint256" +) + +// I64Min returns the minimum of two int64 values +func I64Min(x, y int64) int64 { + if x < y { + return x + } + return y +} + +// I64Max returns the maximum of two int64 values +func I64Max(x, y int64) int64 { + if x > y { + return x + } + return y +} + +// U64Min returns the minimum of two uint64 values +func U64Min(x, y uint64) uint64 { + if x < y { + return x + } + return y +} + +// U64Max returns the maximum of two uint64 values +func U64Max(x, y uint64) uint64 { + if x > y { + return x + } + return y +} + +// I256Min returns the minimum of two Int256 values +func I256Min(x, y *i256.Int) *i256.Int { + if x.Cmp(y) < 0 { + return x + } + return y +} + +// I256Max returns the maximum of two Int256 values +func I256Max(x, y *i256.Int) *i256.Int { + if x.Cmp(y) > 0 { + return x + } + return y +} + +// U256Min returns the minimum of two Uint256 values +func U256Min(x, y *u256.Uint) *u256.Uint { + if x.Cmp(y) < 0 { + return x + } + return y +} + +// U256Max returns the maximum of two Uint256 values +func U256Max(x, y *u256.Uint) *u256.Uint { + if x.Cmp(y) > 0 { + return x + } + return y +} diff --git a/_deploy/r/gnoswap/common/math_test.gno b/_deploy/r/gnoswap/common/math_test.gno new file mode 100644 index 00000000..8481d84e --- /dev/null +++ b/_deploy/r/gnoswap/common/math_test.gno @@ -0,0 +1,172 @@ +package common + +import ( + "testing" + + i256 "gno.land/p/gnoswap/int256" + u256 "gno.land/p/gnoswap/uint256" +) + +func TestI64Min(t *testing.T) { + tests := []struct { + x, y, want int64 + }{ + {1, 2, 1}, + {-1, 1, -1}, + {5, 5, 5}, + {-10, -5, -10}, + {9223372036854775807, 0, 0}, // Max int64 + } + + for _, tt := range tests { + got := I64Min(tt.x, tt.y) + if got != tt.want { + t.Errorf("I64Min(%v, %v) = %v, want %v", tt.x, tt.y, got, tt.want) + } + } +} + +func TestI64Max(t *testing.T) { + tests := []struct { + x, y, want int64 + }{ + {1, 2, 2}, + {-1, 1, 1}, + {5, 5, 5}, + {-10, -5, -5}, + {9223372036854775807, 0, 9223372036854775807}, // Max int64 + } + + for _, tt := range tests { + got := I64Max(tt.x, tt.y) + if got != tt.want { + t.Errorf("I64Max(%v, %v) = %v, want %v", tt.x, tt.y, got, tt.want) + } + } +} + +func TestU64Min(t *testing.T) { + tests := []struct { + x, y, want uint64 + }{ + {1, 2, 1}, + {0, 1, 0}, + {5, 5, 5}, + {10, 5, 5}, + {18446744073709551615, 0, 0}, // Max uint64 + } + + for _, tt := range tests { + got := U64Min(tt.x, tt.y) + if got != tt.want { + t.Errorf("U64Min(%v, %v) = %v, want %v", tt.x, tt.y, got, tt.want) + } + } +} + +func TestU64Max(t *testing.T) { + tests := []struct { + x, y, want uint64 + }{ + {1, 2, 2}, + {0, 1, 1}, + {5, 5, 5}, + {10, 5, 10}, + {18446744073709551615, 0, 18446744073709551615}, // Max uint64 + } + + for _, tt := range tests { + got := U64Max(tt.x, tt.y) + if got != tt.want { + t.Errorf("U64Max(%v, %v) = %v, want %v", tt.x, tt.y, got, tt.want) + } + } +} + +func TestI256Min(t *testing.T) { + tests := []struct { + x, y string // hex strings for creating Int + want string + }{ + {"1", "2", "1"}, + {"-1", "1", "-1"}, + {"5", "5", "5"}, + {"-10", "-5", "-10"}, + } + + for _, tt := range tests { + x, _ := i256.FromDecimal(tt.x) + y, _ := i256.FromDecimal(tt.y) + want, _ := i256.FromDecimal(tt.want) + got := I256Min(x, y) + if got.Cmp(want) != 0 { + t.Errorf("I256Min(%v, %v) = %v, want %v", tt.x, tt.y, got, want) + } + } +} + +func TestI256Max(t *testing.T) { + tests := []struct { + x, y string // hex strings for creating Int + want string + }{ + {"1", "2", "2"}, + {"-1", "1", "1"}, + {"5", "5", "5"}, + {"-10", "-5", "-5"}, + } + + for _, tt := range tests { + x, _ := i256.FromDecimal(tt.x) + y, _ := i256.FromDecimal(tt.y) + want, _ := i256.FromDecimal(tt.want) + got := I256Max(x, y) + if got.Cmp(want) != 0 { + t.Errorf("I256Max(%v, %v) = %v, want %v", tt.x, tt.y, got, want) + } + } +} + +func TestU256Min(t *testing.T) { + tests := []struct { + x, y string // decimal strings for creating Uint + want string + }{ + {"1", "2", "1"}, + {"0", "1", "0"}, + {"5", "5", "5"}, + {"10", "5", "5"}, + } + + for _, tt := range tests { + x, _ := u256.FromDecimal(tt.x) + y, _ := u256.FromDecimal(tt.y) + want, _ := u256.FromDecimal(tt.want) + got := U256Min(x, y) + if got.Cmp(want) != 0 { + t.Errorf("U256Min(%v, %v) = %v, want %v", tt.x, tt.y, got, want) + } + } +} + +func TestU256Max(t *testing.T) { + tests := []struct { + x, y string // decimal strings for creating Uint + want string + }{ + {"1", "2", "2"}, + {"0", "1", "1"}, + {"5", "5", "5"}, + {"10", "5", "10"}, + } + + for _, tt := range tests { + x, _ := u256.FromDecimal(tt.x) + y, _ := u256.FromDecimal(tt.y) + want, _ := u256.FromDecimal(tt.want) + got := U256Max(x, y) + if got.Cmp(want) != 0 { + t.Errorf("U256Max(%v, %v) = %v, want %v", tt.x, tt.y, got, want) + } + } +} From b7990ca3364ef4e35dd03cff5e8d0d5d011056ee Mon Sep 17 00:00:00 2001 From: n3wbie Date: Tue, 17 Dec 2024 22:09:34 +0900 Subject: [PATCH 2/7] feat: std.Address <-> pusers.AddressOrName type conversion --- .../r/gnoswap/common/address_and_username.gno | 29 +++++ .../common/address_and_username_test.gno | 115 ++++++++++++++++++ 2 files changed, 144 insertions(+) create mode 100644 _deploy/r/gnoswap/common/address_and_username.gno create mode 100644 _deploy/r/gnoswap/common/address_and_username_test.gno diff --git a/_deploy/r/gnoswap/common/address_and_username.gno b/_deploy/r/gnoswap/common/address_and_username.gno new file mode 100644 index 00000000..708c55e2 --- /dev/null +++ b/_deploy/r/gnoswap/common/address_and_username.gno @@ -0,0 +1,29 @@ +package common + +import ( + "std" + + pusers "gno.land/p/demo/users" + "gno.land/r/demo/users" +) + +// AddrToUser converts a type from address to AddressOrName. +// It panics if the address is invalid. +func AddrToUser(addr std.Address) pusers.AddressOrName { + assertValidAddr(addr) + return pusers.AddressOrName(addr) +} + +// UserToAddr converts a type from AddressOrName to address. +// by resolving the user through the users realms. +func UserToAddr(user pusers.AddressOrName) std.Address { + return users.Resolve(user) +} + +// assertValidAddr checks if the given address is valid. +// It panics with a detailed error message if the address is invalid. +func assertValidAddr(addr std.Address) { + if !addr.IsValid() { + panic(addDetailToError(errInvalidAddr, addr.String())) + } +} diff --git a/_deploy/r/gnoswap/common/address_and_username_test.gno b/_deploy/r/gnoswap/common/address_and_username_test.gno new file mode 100644 index 00000000..9f62bd8c --- /dev/null +++ b/_deploy/r/gnoswap/common/address_and_username_test.gno @@ -0,0 +1,115 @@ +package common + +import ( + "std" + "testing" + + "gno.land/p/demo/uassert" + pusers "gno.land/p/demo/users" +) + +func TestAddrToUser(t *testing.T) { + tests := []struct { + name string + addr std.Address + want pusers.AddressOrName + shouldPanic bool + panicMsg string + }{ + { + name: "valid address", + addr: std.Address("g1jg8mtutu9khhfwc4nxmuhcpftf0pajdhfvsqf5"), + want: pusers.AddressOrName("g1jg8mtutu9khhfwc4nxmuhcpftf0pajdhfvsqf5"), + }, + { + name: "empty address", + addr: std.Address(""), + shouldPanic: true, + panicMsg: `[GNOSWAP-COMMON-005] invalid address || `, + }, + { + name: "invalid address", + addr: std.Address("invalid"), + shouldPanic: true, + panicMsg: `[GNOSWAP-COMMON-005] invalid address || invalid`, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if tt.shouldPanic { + uassert.PanicsWithMessage(t, tt.panicMsg, func() { + AddrToUser(tt.addr) + }) + } else { + got := AddrToUser(tt.addr) + if got != tt.want { + t.Errorf("AddrToUser() = %v, want %v", got, tt.want) + } + } + }) + } +} + +func TestUserToAddr(t *testing.T) { + tests := []struct { + name string + user pusers.AddressOrName + want std.Address + }{ + { + name: "address string with user type", + user: pusers.AddressOrName("g1jg8mtutu9khhfwc4nxmuhcpftf0pajdhfvsqf5"), + want: std.Address("g1jg8mtutu9khhfwc4nxmuhcpftf0pajdhfvsqf5"), + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got := UserToAddr(tt.user) + if got != tt.want { + t.Errorf("UserToAddr() = %v, want %v", got, tt.want) + } + }) + } +} + +func TestAssertValidAddr(t *testing.T) { + tests := []struct { + name string + addr std.Address + shouldPanic bool + panicMsg string + }{ + { + name: "valid address", + addr: std.Address("g1jg8mtutu9khhfwc4nxmuhcpftf0pajdhfvsqf5"), + }, + { + name: "empty address", + addr: std.Address(""), + shouldPanic: true, + panicMsg: `[GNOSWAP-COMMON-005] invalid address || `, + }, + { + name: "invalid address", + addr: std.Address("invalid"), + shouldPanic: true, + panicMsg: `[GNOSWAP-COMMON-005] invalid address || invalid`, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if tt.shouldPanic { + uassert.PanicsWithMessage(t, tt.panicMsg, func() { + assertValidAddr(tt.addr) + }) + } else { + uassert.NotPanics(t, func() { + assertValidAddr(tt.addr) + }) + } + }) + } +} From 420d1daeb7042e28344359eb69a1d000482533b7 Mon Sep 17 00:00:00 2001 From: n3wbie Date: Tue, 17 Dec 2024 22:09:44 +0900 Subject: [PATCH 3/7] chore: more error code --- _deploy/r/gnoswap/common/errors.gno | 1 + 1 file changed, 1 insertion(+) diff --git a/_deploy/r/gnoswap/common/errors.gno b/_deploy/r/gnoswap/common/errors.gno index 43bd3133..84790e2a 100644 --- a/_deploy/r/gnoswap/common/errors.gno +++ b/_deploy/r/gnoswap/common/errors.gno @@ -11,6 +11,7 @@ var ( 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") + errInvalidAddr = errors.New("[GNOSWAP-COMMON-005] invalid address") ) func addDetailToError(err error, detail string) string { From 52e4eb7e34862dc6b89ba14a6d01d2bc47f4e780 Mon Sep 17 00:00:00 2001 From: n3wbie Date: Tue, 17 Dec 2024 22:09:53 +0900 Subject: [PATCH 4/7] fix: remove deprecated `require` from gno.mod --- _deploy/r/gnoswap/common/gno.mod | 8 -------- 1 file changed, 8 deletions(-) diff --git a/_deploy/r/gnoswap/common/gno.mod b/_deploy/r/gnoswap/common/gno.mod index 944098d5..ad0e3a33 100644 --- a/_deploy/r/gnoswap/common/gno.mod +++ b/_deploy/r/gnoswap/common/gno.mod @@ -1,9 +1 @@ module gno.land/r/gnoswap/v1/common - -require ( - gno.land/p/demo/ufmt 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/consts v0.0.0-latest -) From deeb466e4526ac94c18e186febad1bda488b98fe Mon Sep 17 00:00:00 2001 From: n3wbie Date: Wed, 18 Dec 2024 12:47:27 +0900 Subject: [PATCH 5/7] feat: uint256 safe convert --- _deploy/r/gnoswap/common/errors.gno | 1 + _deploy/r/gnoswap/common/math.gno | 30 +++++++++++++ _deploy/r/gnoswap/common/math_test.gno | 62 ++++++++++++++++++++++++++ 3 files changed, 93 insertions(+) diff --git a/_deploy/r/gnoswap/common/errors.gno b/_deploy/r/gnoswap/common/errors.gno index 84790e2a..ec038821 100644 --- a/_deploy/r/gnoswap/common/errors.gno +++ b/_deploy/r/gnoswap/common/errors.gno @@ -12,6 +12,7 @@ var ( errOutOfRange = errors.New("[GNOSWAP-COMMON-003] value out of range") errNotRegistered = errors.New("[GNOSWAP-COMMON-004] token is not registered") errInvalidAddr = errors.New("[GNOSWAP-COMMON-005] invalid address") + errOverflow = errors.New("[GNOSWAP-COMMON-006] overflow") ) func addDetailToError(err error, detail string) string { diff --git a/_deploy/r/gnoswap/common/math.gno b/_deploy/r/gnoswap/common/math.gno index ff69b1ba..ae626926 100644 --- a/_deploy/r/gnoswap/common/math.gno +++ b/_deploy/r/gnoswap/common/math.gno @@ -1,8 +1,12 @@ package common import ( + "gno.land/p/demo/ufmt" + i256 "gno.land/p/gnoswap/int256" u256 "gno.land/p/gnoswap/uint256" + + "gno.land/r/gnoswap/v1/consts" ) // I64Min returns the minimum of two int64 values @@ -68,3 +72,29 @@ func U256Max(x, y *u256.Uint) *u256.Uint { } return y } + +// SafeConvertUint256ToInt256 converts a uint256.Uint to int256.Int and returns it. +// If the value is greater than the maximum int256 value, it panics. +func SafeConvertUint256ToInt256(x *u256.Uint) *i256.Int { + if x.Gt(u256.MustFromDecimal(consts.MAX_INT256)) { + panic(addDetailToError( + errOverflow, + ufmt.Sprintf("can not convert %s to int256", x.ToString()), + )) + } + return i256.FromUint256(x) +} + +// SafeConvertUint256ToUint64 converts a uint256.Uint to uint64 and returns it. +// If the value is greater than the maximum uint64 value, it panics. +func SafeConvertUint256ToUint64(x *u256.Uint) uint64 { + value, overflow := x.Uint64WithOverflow() + if overflow { + panic(addDetailToError( + errOverflow, + ufmt.Sprintf("can not convert %s to uint64", x.ToString()), + )) + } + + return value +} diff --git a/_deploy/r/gnoswap/common/math_test.gno b/_deploy/r/gnoswap/common/math_test.gno index 8481d84e..8c9088a9 100644 --- a/_deploy/r/gnoswap/common/math_test.gno +++ b/_deploy/r/gnoswap/common/math_test.gno @@ -170,3 +170,65 @@ func TestU256Max(t *testing.T) { } } } + +func TestSafeConvertUint256ToInt256(t *testing.T) { + tests := []struct { + x, want string + shouldPanic bool + }{ + {"0", "0", false}, + {"1", "1", false}, + // max int256 + {"57896044618658097711785492504343953926634992332820282019728792003956564819968", "57896044618658097711785492504343953926634992332820282019728792003956564819968", false}, + // max int256 + 1 (overflow) + {"57896044618658097711785492504343953926634992332820282019728792003956564819969", "", true}, + } + + for _, tt := range tests { + if tt.shouldPanic { + defer func() { + if r := recover(); r == nil { + t.Errorf("SafeConvertUint256ToInt256(%v) did not panic", tt.x) + } + }() + } + + x := u256.MustFromDecimal(tt.x) + got := SafeConvertUint256ToInt256(x) + want := i256.MustFromDecimal(tt.want) + if got.Cmp(want) != 0 { + t.Errorf("SafeConvertUint256ToInt256(%v) = %v, want %v", tt.x, got, want) + } + } +} + +func TestSafeConvertUint256ToUint64(t *testing.T) { + tests := []struct { + x string + want uint64 + shouldPanic bool + }{ + {"0", 0, false}, + {"1", 1, false}, + // max uint64 + {"18446744073709551615", 18446744073709551615, false}, + // max uint64 + 1 (overflow) + {"18446744073709551616", 0, true}, + } + + for _, tt := range tests { + if tt.shouldPanic { + defer func() { + if r := recover(); r == nil { + t.Errorf("SafeConvertUint256ToUint64(%v) did not panic", tt.x) + } + }() + } + + x := u256.MustFromDecimal(tt.x) + got := SafeConvertUint256ToUint64(x) + if got != tt.want { + t.Errorf("SafeConvertUint256ToUint64(%v) = %v, want %v", tt.x, got, tt.want) + } + } +} From c56d54af92c134f33ade2d23586d7f5e6878d363 Mon Sep 17 00:00:00 2001 From: n3wbie Date: Wed, 18 Dec 2024 13:08:17 +0900 Subject: [PATCH 6/7] feat: token's method by using grc20reg --- _deploy/r/gnoswap/common/grc20reg_helper.gno | 19 +++++++++++++++- .../r/gnoswap/common/grc20reg_helper_test.gno | 22 ++++++++++++++++++- 2 files changed, 39 insertions(+), 2 deletions(-) diff --git a/_deploy/r/gnoswap/common/grc20reg_helper.gno b/_deploy/r/gnoswap/common/grc20reg_helper.gno index 50f945e0..992a0270 100644 --- a/_deploy/r/gnoswap/common/grc20reg_helper.gno +++ b/_deploy/r/gnoswap/common/grc20reg_helper.gno @@ -1,6 +1,8 @@ package common import ( + "std" + "gno.land/p/demo/grc/grc20" "gno.land/p/demo/ufmt" "gno.land/r/demo/grc20reg" @@ -51,7 +53,22 @@ func MustRegistered(path string) { if err := IsRegistered(path); err != nil { panic(addDetailToError( errNotRegistered, - ufmt.Sprintf("token(%s) is not registered", path), + ufmt.Sprintf("token(%s)", path), )) } } + +// TotalSupply returns the total supply of the token +func TotalSupply(path string) uint64 { + return GetToken(path).TotalSupply() +} + +// BalanceOf returns the balance of the token for the given address +func BalanceOf(path string, addr std.Address) uint64 { + return GetToken(path).BalanceOf(addr) +} + +// Allowance returns the allowance of the token for the given owner and spender +func Allowance(path string, owner, spender std.Address) uint64 { + return GetToken(path).Allowance(owner, spender) +} diff --git a/_deploy/r/gnoswap/common/grc20reg_helper_test.gno b/_deploy/r/gnoswap/common/grc20reg_helper_test.gno index 086320ed..0ae8b637 100644 --- a/_deploy/r/gnoswap/common/grc20reg_helper_test.gno +++ b/_deploy/r/gnoswap/common/grc20reg_helper_test.gno @@ -8,7 +8,7 @@ import ( "gno.land/p/demo/uassert" "gno.land/p/demo/ufmt" - _ "gno.land/r/demo/foo20" + "gno.land/r/demo/foo20" ) var ( @@ -152,3 +152,23 @@ func TestMustRegistered(t *testing.T) { MustRegistered("not_registered") }) } + +func TestTotalSupply(t *testing.T) { + // result from grc2reg and (direct import/call) should be the same + uassert.Equal(t, foo20.TotalSupply(), TotalSupply(tokenPath)) +} + +func TestBalanceOf(t *testing.T) { + defaultHolder := std.Address("g1manfred47kzduec920z88wfr64ylksmdcedlf5") + + // result from grc2reg and (direct import/call) should be the same + uassert.Equal(t, foo20.BalanceOf(AddrToUser(defaultHolder)), BalanceOf(tokenPath, defaultHolder)) +} + +func TestAllowance(t *testing.T) { + owner := std.Address("g1manfred47kzduec920z88wfr64ylksmdcedlf5") + spender := std.Address("g1pf6dv9fjk3rn0m4jjcne306ga4he3mzmupfjl6") + + // result from grc2reg and (direct import/call) should be the same + uassert.Equal(t, foo20.Allowance(AddrToUser(owner), AddrToUser(spender)), Allowance(tokenPath, owner, spender)) +} From 44b995edbd0af4864eb88459fd2d4f4d130bae06 Mon Sep 17 00:00:00 2001 From: n3wbie Date: Wed, 18 Dec 2024 13:46:09 +0900 Subject: [PATCH 7/7] feat: TokenIdFrom --- _deploy/r/gnoswap/common/errors.gno | 13 ++-- _deploy/r/gnoswap/common/grc721_token_id.gno | 41 ++++++++++++ .../r/gnoswap/common/grc721_token_id_test.gno | 64 +++++++++++++++++++ 3 files changed, 112 insertions(+), 6 deletions(-) create mode 100644 _deploy/r/gnoswap/common/grc721_token_id.gno create mode 100644 _deploy/r/gnoswap/common/grc721_token_id_test.gno diff --git a/_deploy/r/gnoswap/common/errors.gno b/_deploy/r/gnoswap/common/errors.gno index ec038821..168d8b85 100644 --- a/_deploy/r/gnoswap/common/errors.gno +++ b/_deploy/r/gnoswap/common/errors.gno @@ -7,12 +7,13 @@ 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") - errNotRegistered = errors.New("[GNOSWAP-COMMON-004] token is not registered") - errInvalidAddr = errors.New("[GNOSWAP-COMMON-005] invalid address") - errOverflow = errors.New("[GNOSWAP-COMMON-006] overflow") + 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") + errInvalidAddr = errors.New("[GNOSWAP-COMMON-005] invalid address") + errOverflow = errors.New("[GNOSWAP-COMMON-006] overflow") + errInvalidTokenId = errors.New("[GNOSWAP-COMMON-007] invalid tokenId") ) func addDetailToError(err error, detail string) string { diff --git a/_deploy/r/gnoswap/common/grc721_token_id.gno b/_deploy/r/gnoswap/common/grc721_token_id.gno new file mode 100644 index 00000000..2cc4c562 --- /dev/null +++ b/_deploy/r/gnoswap/common/grc721_token_id.gno @@ -0,0 +1,41 @@ +package common + +import ( + "strconv" + + "gno.land/p/demo/ufmt" + + "gno.land/p/demo/grc/grc721" +) + +// TokenIdFrom converts tokenId to grc721.TokenID type +// NOTE: input parameter tokenId can be string, int, uint64, or grc721.TokenID +// if tokenId is nil or not supported, it will panic +// if input type is not supported, it will panic +// input: tokenId interface{} +// output: grc721.TokenID +func TokenIdFrom(tokenId interface{}) grc721.TokenID { + if tokenId == nil { + panic(addDetailToError( + errInvalidTokenId, + "can not be nil", + )) + } + + switch tokenId.(type) { + case string: + return grc721.TokenID(tokenId.(string)) + case int: + return grc721.TokenID(strconv.Itoa(tokenId.(int))) + case uint64: + return grc721.TokenID(strconv.Itoa(int(tokenId.(uint64)))) + case grc721.TokenID: + return tokenId.(grc721.TokenID) + default: + estimatedType := ufmt.Sprintf("%T", tokenId) + panic(addDetailToError( + errInvalidTokenId, + ufmt.Sprintf("unsupported tokenId type: %s", estimatedType), + )) + } +} diff --git a/_deploy/r/gnoswap/common/grc721_token_id_test.gno b/_deploy/r/gnoswap/common/grc721_token_id_test.gno new file mode 100644 index 00000000..6f15e955 --- /dev/null +++ b/_deploy/r/gnoswap/common/grc721_token_id_test.gno @@ -0,0 +1,64 @@ +package common + +import ( + "testing" + + "gno.land/p/demo/grc/grc721" +) + +func TestTokenIdFrom(t *testing.T) { + tests := []struct { + name string + input interface{} + want grc721.TokenID + wantPanic bool + }{ + { + name: "string input", + input: "123", + want: grc721.TokenID("123"), + }, + { + name: "int input", + input: 123, + want: grc721.TokenID("123"), + }, + { + name: "uint64 input", + input: uint64(123), + want: grc721.TokenID("123"), + }, + { + name: "grc721.TokenID input", + input: grc721.TokenID("123"), + want: grc721.TokenID("123"), + }, + { + name: "nil input", + input: nil, + wantPanic: true, + }, + { + name: "unsupported type (byte)", + input: []byte("123"), + wantPanic: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if tt.wantPanic { + defer func() { + if r := recover(); r == nil { + t.Errorf("TokenIdFrom() should have panicked") + } + }() + } + + got := TokenIdFrom(tt.input) + if got != tt.want { + t.Errorf("TokenIdFrom() = %v, want %v", got, tt.want) + } + }) + } +}