Skip to content

Commit c58e08e

Browse files
authored
feat: add CLI and RPC endpoint from Pointee to Pointer mapping (#1834)
* feat: add CLI and RPC endpoint from Pointee to Pointer mapping --------- Co-authored-by: blindchaser <zengyiren@hotmail.com>
1 parent 7e79510 commit c58e08e

File tree

7 files changed

+859
-41
lines changed

7 files changed

+859
-41
lines changed

proto/evm/query.proto

+15
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,10 @@ service Query {
2727
rpc PointerVersion(QueryPointerVersionRequest) returns (QueryPointerVersionResponse) {
2828
option (google.api.http).get = "/sei-protocol/seichain/evm/pointer_version";
2929
}
30+
31+
rpc Pointee(QueryPointeeRequest) returns (QueryPointeeResponse) {
32+
option (google.api.http).get = "/sei-protocol/seichain/evm/pointee";
33+
}
3034
}
3135

3236
message QuerySeiAddressByEVMAddressRequest {
@@ -75,3 +79,14 @@ message QueryPointerVersionResponse {
7579
uint32 version = 1;
7680
uint64 cw_code_id = 2;
7781
}
82+
83+
message QueryPointeeRequest {
84+
PointerType pointer_type = 1;
85+
string pointer = 2;
86+
}
87+
88+
message QueryPointeeResponse {
89+
string pointee = 1;
90+
uint32 version = 2;
91+
bool exists = 3;
92+
}

x/evm/client/cli/query.go

+30
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ func GetQueryCmd(_ string) *cobra.Command {
4444
cmd.AddCommand(CmdQueryPayload())
4545
cmd.AddCommand(CmdQueryPointer())
4646
cmd.AddCommand(CmdQueryPointerVersion())
47+
cmd.AddCommand(CmdQueryPointee())
4748

4849
return cmd
4950
}
@@ -348,3 +349,32 @@ func CmdQueryPointerVersion() *cobra.Command {
348349

349350
return cmd
350351
}
352+
353+
func CmdQueryPointee() *cobra.Command {
354+
cmd := &cobra.Command{
355+
Use: "pointee [type] [pointer]",
356+
Short: "Get pointee address of the specified type (one of [NATIVE, CW20, CW721, ERC20, ERC721]) and pointer",
357+
Args: cobra.ExactArgs(2),
358+
RunE: func(cmd *cobra.Command, args []string) error {
359+
clientCtx, err := client.GetClientQueryContext(cmd)
360+
if err != nil {
361+
return err
362+
}
363+
queryClient := types.NewQueryClient(clientCtx)
364+
ctx := cmd.Context()
365+
366+
res, err := queryClient.Pointee(ctx, &types.QueryPointeeRequest{
367+
PointerType: types.PointerType(types.PointerType_value[args[0]]), Pointer: args[1],
368+
})
369+
if err != nil {
370+
return err
371+
}
372+
373+
return clientCtx.PrintProto(res)
374+
},
375+
}
376+
377+
flags.AddQueryFlagsToCmd(cmd)
378+
379+
return cmd
380+
}

x/evm/keeper/grpc_query.go

+43
Original file line numberDiff line numberDiff line change
@@ -146,3 +146,46 @@ func (q Querier) PointerVersion(c context.Context, req *types.QueryPointerVersio
146146
return nil, errors.ErrUnsupported
147147
}
148148
}
149+
150+
func (q Querier) Pointee(c context.Context, req *types.QueryPointeeRequest) (*types.QueryPointeeResponse, error) {
151+
ctx := sdk.UnwrapSDKContext(c)
152+
switch req.PointerType {
153+
case types.PointerType_NATIVE:
154+
p, v, e := q.Keeper.GetNativePointee(ctx, req.Pointer)
155+
return &types.QueryPointeeResponse{
156+
Pointee: p,
157+
Version: uint32(v),
158+
Exists: e,
159+
}, nil
160+
case types.PointerType_CW20:
161+
p, v, e := q.Keeper.GetCW20Pointee(ctx, common.HexToAddress(req.Pointer))
162+
return &types.QueryPointeeResponse{
163+
Pointee: p,
164+
Version: uint32(v),
165+
Exists: e,
166+
}, nil
167+
case types.PointerType_CW721:
168+
p, v, e := q.Keeper.GetCW721Pointee(ctx, common.HexToAddress(req.Pointer))
169+
return &types.QueryPointeeResponse{
170+
Pointee: p,
171+
Version: uint32(v),
172+
Exists: e,
173+
}, nil
174+
case types.PointerType_ERC20:
175+
p, v, e := q.Keeper.GetERC20Pointee(ctx, req.Pointer)
176+
return &types.QueryPointeeResponse{
177+
Pointee: p.Hex(),
178+
Version: uint32(v),
179+
Exists: e,
180+
}, nil
181+
case types.PointerType_ERC721:
182+
p, v, e := q.Keeper.GetERC721Pointee(ctx, req.Pointer)
183+
return &types.QueryPointeeResponse{
184+
Pointee: p.Hex(),
185+
Version: uint32(v),
186+
Exists: e,
187+
}, nil
188+
default:
189+
return nil, errors.ErrUnsupported
190+
}
191+
}

x/evm/keeper/grpc_query_test.go

+116
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package keeper_test
22

33
import (
4+
"errors"
45
"testing"
56
"time"
67

@@ -47,3 +48,118 @@ func TestQueryPointer(t *testing.T) {
4748
require.Nil(t, err)
4849
require.Equal(t, types.QueryPointerResponse{Pointer: seiAddr5.String(), Version: uint32(erc721.CurrentVersion), Exists: true}, *res)
4950
}
51+
52+
func TestQueryPointee(t *testing.T) {
53+
k, ctx := testkeeper.MockEVMKeeper()
54+
_, pointerAddr1 := testkeeper.MockAddressPair()
55+
seiAddr2, evmAddr2 := testkeeper.MockAddressPair()
56+
seiAddr3, evmAddr3 := testkeeper.MockAddressPair()
57+
seiAddr4, evmAddr4 := testkeeper.MockAddressPair()
58+
seiAddr5, evmAddr5 := testkeeper.MockAddressPair()
59+
goCtx := sdk.WrapSDKContext(ctx)
60+
61+
// Set up pointers for each type
62+
k.SetERC20NativePointer(ctx, "ufoo", pointerAddr1)
63+
k.SetERC20CW20Pointer(ctx, seiAddr2.String(), evmAddr2)
64+
k.SetERC721CW721Pointer(ctx, seiAddr3.String(), evmAddr3)
65+
k.SetCW20ERC20Pointer(ctx, evmAddr4, seiAddr4.String())
66+
k.SetCW721ERC721Pointer(ctx, evmAddr5, seiAddr5.String())
67+
68+
q := keeper.Querier{k}
69+
70+
// Test for Native Pointee
71+
res, err := q.Pointee(goCtx, &types.QueryPointeeRequest{PointerType: types.PointerType_NATIVE, Pointer: pointerAddr1.Hex()})
72+
require.Nil(t, err)
73+
require.Equal(t, types.QueryPointeeResponse{Pointee: "ufoo", Version: uint32(native.CurrentVersion), Exists: true}, *res)
74+
75+
// Test for CW20 Pointee
76+
res, err = q.Pointee(goCtx, &types.QueryPointeeRequest{PointerType: types.PointerType_CW20, Pointer: evmAddr2.Hex()})
77+
require.Nil(t, err)
78+
require.Equal(t, types.QueryPointeeResponse{Pointee: seiAddr2.String(), Version: uint32(cw20.CurrentVersion(ctx)), Exists: true}, *res)
79+
80+
// Test for CW721 Pointee
81+
res, err = q.Pointee(goCtx, &types.QueryPointeeRequest{PointerType: types.PointerType_CW721, Pointer: evmAddr3.Hex()})
82+
require.Nil(t, err)
83+
require.Equal(t, types.QueryPointeeResponse{Pointee: seiAddr3.String(), Version: uint32(cw721.CurrentVersion), Exists: true}, *res)
84+
85+
// Test for ERC20 Pointee
86+
res, err = q.Pointee(goCtx, &types.QueryPointeeRequest{PointerType: types.PointerType_ERC20, Pointer: seiAddr4.String()})
87+
require.Nil(t, err)
88+
require.Equal(t, types.QueryPointeeResponse{Pointee: evmAddr4.Hex(), Version: uint32(erc20.CurrentVersion), Exists: true}, *res)
89+
90+
// Test for ERC721 Pointee
91+
res, err = q.Pointee(goCtx, &types.QueryPointeeRequest{PointerType: types.PointerType_ERC721, Pointer: seiAddr5.String()})
92+
require.Nil(t, err)
93+
require.Equal(t, types.QueryPointeeResponse{Pointee: evmAddr5.Hex(), Version: uint32(erc721.CurrentVersion), Exists: true}, *res)
94+
95+
// Test for not registered Native Pointee
96+
res, err = q.Pointee(goCtx, &types.QueryPointeeRequest{PointerType: types.PointerType_NATIVE, Pointer: "0x1234567890123456789012345678901234567890"})
97+
require.Nil(t, err)
98+
require.Equal(t, types.QueryPointeeResponse{Pointee: "", Version: 0, Exists: false}, *res)
99+
100+
// Test for not registered CW20 Pointee
101+
res, err = q.Pointee(goCtx, &types.QueryPointeeRequest{PointerType: types.PointerType_CW20, Pointer: "0x1234567890123456789012345678901234567890"})
102+
require.Nil(t, err)
103+
require.Equal(t, types.QueryPointeeResponse{Pointee: "", Version: 0, Exists: false}, *res)
104+
105+
// Test for not registered CW721 Pointee
106+
res, err = q.Pointee(goCtx, &types.QueryPointeeRequest{PointerType: types.PointerType_CW721, Pointer: "0x1234567890123456789012345678901234567890"})
107+
require.Nil(t, err)
108+
require.Equal(t, types.QueryPointeeResponse{Pointee: "", Version: 0, Exists: false}, *res)
109+
110+
// Test for not registered ERC20 Pointee
111+
res, err = q.Pointee(goCtx, &types.QueryPointeeRequest{PointerType: types.PointerType_ERC20, Pointer: "sei1notregistered"})
112+
require.Nil(t, err)
113+
require.Equal(t, types.QueryPointeeResponse{Pointee: "0x0000000000000000000000000000000000000000", Version: 0, Exists: false}, *res)
114+
115+
// Test for not registered ERC721 Pointee
116+
res, err = q.Pointee(goCtx, &types.QueryPointeeRequest{PointerType: types.PointerType_ERC721, Pointer: "sei1notregistered"})
117+
require.Nil(t, err)
118+
require.Equal(t, types.QueryPointeeResponse{Pointee: "0x0000000000000000000000000000000000000000", Version: 0, Exists: false}, *res)
119+
120+
// Test cases for invalid inputs
121+
testCases := []struct {
122+
name string
123+
req *types.QueryPointeeRequest
124+
expectedRes *types.QueryPointeeResponse
125+
expectedErr error
126+
}{
127+
{
128+
name: "Invalid pointer type",
129+
req: &types.QueryPointeeRequest{PointerType: 999, Pointer: pointerAddr1.Hex()},
130+
expectedRes: nil,
131+
expectedErr: errors.ErrUnsupported,
132+
},
133+
{
134+
name: "Empty pointer",
135+
req: &types.QueryPointeeRequest{PointerType: types.PointerType_NATIVE, Pointer: ""},
136+
expectedRes: &types.QueryPointeeResponse{Pointee: "", Version: 0, Exists: false},
137+
expectedErr: nil,
138+
},
139+
{
140+
name: "Invalid hex address for EVM-based pointer types",
141+
req: &types.QueryPointeeRequest{PointerType: types.PointerType_CW20, Pointer: "not-a-hex-address"},
142+
expectedRes: &types.QueryPointeeResponse{Pointee: "", Version: 0, Exists: false},
143+
expectedErr: nil,
144+
},
145+
{
146+
name: "Invalid bech32 address for Cosmos-based pointer types",
147+
req: &types.QueryPointeeRequest{PointerType: types.PointerType_ERC20, Pointer: "not-a-bech32-address"},
148+
expectedRes: &types.QueryPointeeResponse{Pointee: "0x0000000000000000000000000000000000000000", Version: 0, Exists: false},
149+
expectedErr: nil,
150+
},
151+
}
152+
153+
for _, tc := range testCases {
154+
t.Run(tc.name, func(t *testing.T) {
155+
res, err := q.Pointee(goCtx, tc.req)
156+
if tc.expectedErr != nil {
157+
require.ErrorIs(t, err, tc.expectedErr)
158+
require.Nil(t, res)
159+
} else {
160+
require.NoError(t, err)
161+
require.Equal(t, tc.expectedRes, res)
162+
}
163+
})
164+
}
165+
}

x/evm/keeper/pointer.go

+42
Original file line numberDiff line numberDiff line change
@@ -254,3 +254,45 @@ func (k *Keeper) GetStoredPointerCodeID(ctx sdk.Context, pointerType types.Point
254254
}
255255
return binary.BigEndian.Uint64(bz)
256256
}
257+
258+
func (k *Keeper) GetCW20Pointee(ctx sdk.Context, erc20Address common.Address) (cw20Address string, version uint16, exists bool) {
259+
addrBz, version, exists := k.GetPointerInfo(ctx, types.PointerReverseRegistryKey(erc20Address))
260+
if exists {
261+
cw20Address = string(addrBz)
262+
}
263+
return
264+
}
265+
266+
func (k *Keeper) GetCW721Pointee(ctx sdk.Context, erc721Address common.Address) (cw721Address string, version uint16, exists bool) {
267+
addrBz, version, exists := k.GetPointerInfo(ctx, types.PointerReverseRegistryKey(erc721Address))
268+
if exists {
269+
cw721Address = string(addrBz)
270+
}
271+
return
272+
}
273+
274+
func (k *Keeper) GetERC20Pointee(ctx sdk.Context, cw20Address string) (erc20Address common.Address, version uint16, exists bool) {
275+
addrBz, version, exists := k.GetPointerInfo(ctx, types.PointerReverseRegistryKey(common.BytesToAddress([]byte(cw20Address))))
276+
if exists {
277+
erc20Address = common.BytesToAddress(addrBz)
278+
}
279+
return
280+
}
281+
282+
func (k *Keeper) GetERC721Pointee(ctx sdk.Context, cw721Address string) (erc721Address common.Address, version uint16, exists bool) {
283+
addrBz, version, exists := k.GetPointerInfo(ctx, types.PointerReverseRegistryKey(common.BytesToAddress([]byte(cw721Address))))
284+
if exists {
285+
erc721Address = common.BytesToAddress(addrBz)
286+
}
287+
return
288+
}
289+
290+
func (k *Keeper) GetNativePointee(ctx sdk.Context, erc20Address string) (token string, version uint16, exists bool) {
291+
// Ensure the key matches how it was set in SetERC20NativePointer
292+
key := types.PointerReverseRegistryKey(common.HexToAddress(erc20Address))
293+
addrBz, version, exists := k.GetPointerInfo(ctx, key)
294+
if exists {
295+
token = string(addrBz)
296+
}
297+
return
298+
}

0 commit comments

Comments
 (0)