Skip to content

Commit c8b7c01

Browse files
yihuangclaudehappy-otter
committed
feat: migrate bank precompile integration tests to go-abi
Migrate bank precompile tests from go-ethereum ABI to custom go-abi library: - Updated ContractData struct to remove precompileABI field - Replaced UnpackIntoInterface/Unpack calls with manual decoding helpers - Updated getTxAndCallArgs to use EncodeWithSelector() for direct precompile calls - Rewrote test_query.go unit tests to use typed call structs (BalancesCall, SupplyOfCall, TotalSupplyCall) - Created decoder helper functions for type-safe result decoding - Tests now use fully type-safe generated types instead of reflection Created comprehensive MIGRATION_GUIDE.md with step-by-step instructions for migrating other precompile tests to go-abi. Co-Authored-By: Claude <[email protected]> Co-Authored-By: Happy <[email protected]>
1 parent a8214b2 commit c8b7c01

File tree

8 files changed

+381
-143
lines changed

8 files changed

+381
-143
lines changed

MIGRATION_GUIDE.md

Lines changed: 247 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,247 @@
1+
# Precompile Test Migration Guide to go-abi
2+
3+
## Overview
4+
5+
This guide explains how to migrate precompile integration tests from the old go-ethereum ABI API to the new custom `go-abi` library (github.com/yihuang/go-abi).
6+
7+
## Background
8+
9+
The precompiles have been refactored to use a custom `go-abi` library instead of the standard `go-ethereum/accounts/abi` package. This change requires updates to the test code to work with the new API.
10+
11+
## Key Differences
12+
13+
### Old API (go-ethereum/accounts/abi)
14+
- Used `abi.ABI.Pack()` to encode method calls
15+
- Used `UnpackIntoInterface()` and `Unpack()` to decode results
16+
- Relied on reflection for encoding/decoding
17+
- Tests accessed `precompile.ABI` field
18+
19+
### New API (go-abi)
20+
- Uses generated types with methods like `EncodeWithSelector()`
21+
- Uses generated `Decode()` methods on result types
22+
- No reflection, fully type-safe
23+
- No `ABI` field on precompile struct
24+
25+
## Migration Steps
26+
27+
### 1. Update ContractData Struct
28+
29+
**Before:**
30+
```go
31+
type ContractData struct {
32+
ownerPriv cryptotypes.PrivKey
33+
contractAddr common.Address
34+
contractABI abi.ABI
35+
precompileAddr common.Address
36+
precompileABI abi.ABI // ← Remove this
37+
}
38+
```
39+
40+
**After:**
41+
```go
42+
type ContractData struct {
43+
ownerPriv cryptotypes.PrivKey
44+
contractAddr common.Address
45+
contractABI abi.ABI
46+
precompileAddr common.Address
47+
// precompileABI removed
48+
}
49+
```
50+
51+
### 2. Remove precompileABI from initialization
52+
53+
**Before:**
54+
```go
55+
contractData = ContractData{
56+
ownerPriv: sender.Priv,
57+
precompileAddr: is.precompile.Address(),
58+
precompileABI: is.precompile.ABI, // ← Remove this
59+
contractAddr: bankCallerContractAddr,
60+
contractABI: bankCallerContract.ABI,
61+
}
62+
```
63+
64+
**After:**
65+
```go
66+
contractData = ContractData{
67+
ownerPriv: sender.Priv,
68+
precompileAddr: is.precompile.Address(),
69+
contractAddr: bankCallerContractAddr,
70+
contractABI: bankCallerContract.ABI,
71+
}
72+
```
73+
74+
### 3. Create Helper Functions
75+
76+
Add these helper functions to your test_utils.go:
77+
78+
```go
79+
// decodeBalancesResult decodes the result from a balances query
80+
func decodeBalancesResult(data []byte) ([]bank.Balance, error) {
81+
var result bank.BalancesReturn
82+
_, err := result.Decode(data)
83+
if err != nil {
84+
return nil, err
85+
}
86+
return result.Balances, nil
87+
}
88+
89+
// decodeTotalSupplyResult decodes the result from a totalSupply query
90+
func decodeTotalSupplyResult(data []byte) ([]bank.Balance, error) {
91+
var result bank.TotalSupplyReturn
92+
_, err := result.Decode(data)
93+
if err != nil {
94+
return nil, err
95+
}
96+
return result.TotalSupply, nil
97+
}
98+
99+
// decodeSupplyOfResult decodes the result from a supplyOf query
100+
func decodeSupplyOfResult(data []byte) (*big.Int, error) {
101+
var result bank.SupplyOfReturn
102+
_, err := result.Decode(data)
103+
if err != nil {
104+
return nil, err
105+
}
106+
return result.TotalSupply, nil
107+
}
108+
```
109+
110+
### 4. Update getTxAndCallArgs Function
111+
112+
This function handles encoding for direct precompile calls. Replace manual encoding with `EncodeWithSelector()`:
113+
114+
```go
115+
func getTxAndCallArgs(
116+
callType int,
117+
contractData ContractData,
118+
methodName string,
119+
args ...interface{},
120+
) (evmtypes.EvmTxArgs, testutiltypes.CallArgs) {
121+
txArgs := evmtypes.EvmTxArgs{}
122+
callArgs := testutiltypes.CallArgs{}
123+
124+
switch callType {
125+
case directCall:
126+
var input []byte
127+
switch methodName {
128+
case bank.BalancesMethod:
129+
addr := args[0].(common.Address)
130+
call := bank.BalancesCall{Account: addr}
131+
input, _ = call.EncodeWithSelector() // Use built-in method
132+
case bank.TotalSupplyMethod:
133+
var call bank.TotalSupplyCall
134+
input, _ = call.EncodeWithSelector()
135+
case bank.SupplyOfMethod:
136+
addr := args[0].(common.Address)
137+
call := bank.SupplyOfCall{Erc20Address: addr}
138+
input, _ = call.EncodeWithSelector()
139+
default:
140+
panic(fmt.Sprintf("unknown method: %s", methodName))
141+
}
142+
txArgs.To = &contractData.precompileAddr
143+
txArgs.Input = input
144+
callArgs.ContractABI = abi.ABI{}
145+
case contractCall:
146+
txArgs.To = &contractData.contractAddr
147+
callArgs.ContractABI = contractData.contractABI
148+
}
149+
150+
callArgs.MethodName = methodName
151+
callArgs.Args = args
152+
return txArgs, callArgs
153+
}
154+
```
155+
156+
### 5. Replace UnpackIntoInterface Calls
157+
158+
**Before:**
159+
```go
160+
var balances []bank.Balance
161+
err = is.precompile.UnpackIntoInterface(&balances, bank2.BalancesMethod, ethRes.Ret)
162+
Expect(err).ToNot(HaveOccurred(), "failed to unpack balances")
163+
```
164+
165+
**After:**
166+
```go
167+
balances, err := decodeBalancesResult(ethRes.Ret)
168+
Expect(err).ToNot(HaveOccurred(), "failed to unpack balances")
169+
```
170+
171+
### 6. Replace Unpack Calls
172+
173+
**Before:**
174+
```go
175+
out, err := is.precompile.Unpack(bank2.SupplyOfMethod, ethRes.Ret)
176+
Expect(err).ToNot(HaveOccurred(), "failed to unpack balances")
177+
Expect(out[0].(*big.Int).String()).To(Equal(expectedValue.String()))
178+
```
179+
180+
**After:**
181+
```go
182+
supply, err := decodeSupplyOfResult(ethRes.Ret)
183+
Expect(err).ToNot(HaveOccurred(), "failed to unpack balances")
184+
Expect(supply.String()).To(Equal(expectedValue.String()))
185+
```
186+
187+
### 7. Update Unit Tests (test_query.go)
188+
189+
**Before:**
190+
```go
191+
func (s *PrecompileTestSuite) TestBalances() {
192+
s.SetupTest()
193+
method := s.precompile.Methods[bank.BalancesMethod] // ← Methods field doesn't exist
194+
195+
// Test cases using method variable...
196+
bz, err := s.precompile.Balances(ctx, &method, args)
197+
var balances []bank.Balance
198+
err = s.precompile.UnpackIntoInterface(&balances, method.Name, bz)
199+
}
200+
```
201+
202+
**After:**
203+
```go
204+
func (s *PrecompileTestSuite) TestBalances() {
205+
s.SetupTest()
206+
207+
// Test cases use typed call structs directly
208+
call := &bank.BalancesCall{Account: addr}
209+
result, err := s.precompile.Balances(ctx, call)
210+
211+
balances := result.Balances // Direct access to result fields
212+
}
213+
```
214+
215+
## Generated Types
216+
217+
The `go-abi` tool generates these types for each method:
218+
219+
- `{MethodName}Call` - Input parameters struct
220+
- `{MethodName}Return` - Output results struct
221+
- `{MethodName}Selector` - Method selector constant
222+
- `{MethodName}ID` - Method ID constant
223+
- `{MethodName}Method` - Method name constant
224+
225+
## Example for Other Precompiles
226+
227+
For each precompile (bech32, distribution, erc20, gov, etc.), you need to:
228+
229+
1. Check the generated types in `{precompile}.abi.go`
230+
2. Create appropriate decode helper functions
231+
3. Update `getTxAndCallArgs` to handle the precompile's methods
232+
4. Replace all `UnpackIntoInterface` and `Unpack` calls
233+
5. Update unit tests to use typed call structs
234+
235+
## Verification
236+
237+
After migration, verify the tests build successfully:
238+
239+
```bash
240+
go build -tags=tests ./tests/integration/precompiles/{precompile_name}/...
241+
```
242+
243+
## References
244+
245+
- Bank precompile migration: `tests/integration/precompiles/bank/`
246+
- go-abi library: github.com/yihuang/go-abi
247+
- Generated types example: `precompiles/bank/bank.abi.go`

precompiles/bech32/methods.go

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,4 +61,3 @@ func (p Precompile) Bech32ToHex(
6161

6262
return &Bech32ToHexReturn{Addr: common.BytesToAddress(addressBz)}, nil
6363
}
64-

precompiles/distribution/types_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ func TestNewMsgSetWithdrawAddress(t *testing.T) {
3434
{
3535
name: "valid with bech32 withdrawer",
3636
args: SetWithdrawAddressCall{
37-
DelegatorAddress: delegatorAddr,
37+
DelegatorAddress: delegatorAddr,
3838
WithdrawerAddress: withdrawerBech32,
3939
},
4040
wantErr: false,

precompiles/gov/types_test.go

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -50,9 +50,9 @@ func TestNewMsgDeposit(t *testing.T) {
5050
ProposalId: proposalID,
5151
Amount: validCoins,
5252
},
53-
wantErr: true,
54-
errMsg: fmt.Sprintf(ErrInvalidDepositor, common.Address{}),
55-
wantDepositor: expectedDepositorAddr,
53+
wantErr: true,
54+
errMsg: fmt.Sprintf(ErrInvalidDepositor, common.Address{}),
55+
wantDepositor: expectedDepositorAddr,
5656
wantProposalID: proposalID,
5757
},
5858
}
@@ -110,9 +110,9 @@ func TestNewMsgCancelProposal(t *testing.T) {
110110
Proposer: common.Address{},
111111
ProposalId: proposalID,
112112
},
113-
wantErr: true,
114-
errMsg: fmt.Sprintf(ErrInvalidProposer, common.Address{}),
115-
wantProposer: expectedProposerAddr,
113+
wantErr: true,
114+
errMsg: fmt.Sprintf(ErrInvalidProposer, common.Address{}),
115+
wantProposer: expectedProposerAddr,
116116
wantProposalID: proposalID,
117117
},
118118
}
@@ -179,12 +179,12 @@ func TestNewMsgVote(t *testing.T) {
179179
Option: option,
180180
Metadata: metadata,
181181
},
182-
wantErr: true,
183-
errMsg: fmt.Sprintf(ErrInvalidVoter, common.Address{}),
184-
wantVoter: expectedVoterAddr,
182+
wantErr: true,
183+
errMsg: fmt.Sprintf(ErrInvalidVoter, common.Address{}),
184+
wantVoter: expectedVoterAddr,
185185
wantProposalID: proposalID,
186-
wantOption: option,
187-
wantMetadata: metadata,
186+
wantOption: option,
187+
wantMetadata: metadata,
188188
},
189189
}
190190

precompiles/staking/types_test.go

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -53,12 +53,12 @@ func TestNewMsgCreateValidator(t *testing.T) {
5353
{
5454
name: "valid",
5555
args: CreateValidatorCall{
56-
Description: description,
57-
CommissionRates: commission,
58-
MinSelfDelegation: minSelfDelegation,
59-
ValidatorAddress: validatorHexAddr,
60-
Pubkey: pubkey,
61-
Value: value,
56+
Description: description,
57+
CommissionRates: commission,
58+
MinSelfDelegation: minSelfDelegation,
59+
ValidatorAddress: validatorHexAddr,
60+
Pubkey: pubkey,
61+
Value: value,
6262
},
6363
wantErr: false,
6464
wantDelegatorAddr: expectedValidatorAddr,

0 commit comments

Comments
 (0)