Skip to content

Commit

Permalink
GSW-1968 refactor: commonly used functions (#444)
Browse files Browse the repository at this point in the history
* feat: min/max for int64,uint64,int256,uint256

* feat: std.Address <-> pusers.AddressOrName type conversion

* feat: uint256 safe convert

* feat: token's method by using grc20reg

* feat: TokenIdFrom, to convert type from various to grc721 tokenId
  • Loading branch information
r3v4s authored Dec 20, 2024
1 parent 808b22a commit cf9d52f
Show file tree
Hide file tree
Showing 9 changed files with 629 additions and 6 deletions.
29 changes: 29 additions & 0 deletions _deploy/r/gnoswap/common/address_and_username.gno
Original file line number Diff line number Diff line change
@@ -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()))
}
}
115 changes: 115 additions & 0 deletions _deploy/r/gnoswap/common/address_and_username_test.gno
Original file line number Diff line number Diff line change
@@ -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)
})
}
})
}
}
11 changes: 7 additions & 4 deletions _deploy/r/gnoswap/common/errors.gno
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +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")
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 {
Expand Down
19 changes: 18 additions & 1 deletion _deploy/r/gnoswap/common/grc20reg_helper.gno
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package common

import (
"std"

"gno.land/p/demo/grc/grc20"
"gno.land/p/demo/ufmt"
"gno.land/r/demo/grc20reg"
Expand Down Expand Up @@ -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)
}
22 changes: 21 additions & 1 deletion _deploy/r/gnoswap/common/grc20reg_helper_test.gno
Original file line number Diff line number Diff line change
Expand Up @@ -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 (
Expand Down Expand Up @@ -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))
}
41 changes: 41 additions & 0 deletions _deploy/r/gnoswap/common/grc721_token_id.gno
Original file line number Diff line number Diff line change
@@ -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),
))
}
}
64 changes: 64 additions & 0 deletions _deploy/r/gnoswap/common/grc721_token_id_test.gno
Original file line number Diff line number Diff line change
@@ -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)
}
})
}
}
Loading

0 comments on commit cf9d52f

Please sign in to comment.