Skip to content

Commit b2ccd70

Browse files
committed
multicall: type-safe output helpers
1 parent ca75869 commit b2ccd70

2 files changed

Lines changed: 50 additions & 42 deletions

File tree

ethrpc/multicall/multicall.go

Lines changed: 28 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ package multicall
55
import (
66
"fmt"
77
"math/big"
8+
"reflect"
89

910
"github.com/0xsequence/ethkit/go-ethereum/accounts/abi"
1011
"github.com/0xsequence/ethkit/go-ethereum/common"
@@ -18,19 +19,34 @@ type Call struct {
1819

1920
type Output struct {
2021
Type abi.Argument
21-
// Output may be nil to ignore the decoded value. When set, it must be a
22-
// non-nil pointer to the exact Go type returned by ABI decoding.
22+
// Output may be nil (or a nil pointer) to ignore the decoded value. When set,
23+
// it must be a non-nil pointer to the exact Go type returned by ABI decoding.
2324
Output any
2425
}
2526

27+
func Address(output *common.Address) Output {
28+
type_, _ := abi.NewType("address", "", nil)
29+
return Output{Type: abi.Argument{Type: type_}, Output: output}
30+
}
31+
32+
func Bytes32(output *common.Hash) Output {
33+
type_, _ := abi.NewType("bytes32", "", nil)
34+
return Output{Type: abi.Argument{Type: type_}, Output: output}
35+
}
36+
37+
func Uint256(output **big.Int) Output {
38+
type_, _ := abi.NewType("uint256", "", nil)
39+
return Output{Type: abi.Argument{Type: type_}, Output: output}
40+
}
41+
2642
func UnpackOutputs(data []byte, outputs ...Output) error {
2743
if len(outputs) == 0 {
2844
return nil
2945
}
3046

3147
needsAssign := false
3248
for _, output := range outputs {
33-
if output.Output != nil {
49+
if !isNilOutput(output.Output) {
3450
needsAssign = true
3551
break
3652
}
@@ -53,7 +69,7 @@ func UnpackOutputs(data []byte, outputs ...Output) error {
5369
}
5470

5571
for i, output := range outputs {
56-
if output.Output == nil {
72+
if isNilOutput(output.Output) {
5773
continue
5874
}
5975
args := abi.Arguments{output.Type}
@@ -65,6 +81,14 @@ func UnpackOutputs(data []byte, outputs ...Output) error {
6581
return nil
6682
}
6783

84+
func isNilOutput(output any) bool {
85+
if output == nil {
86+
return true
87+
}
88+
value := reflect.ValueOf(output)
89+
return value.Kind() == reflect.Ptr && value.IsNil()
90+
}
91+
6892
func BaseFee() []byte {
6993
abi, _ := IMulticall3MetaData.GetAbi()
7094
data, _ := abi.Pack("getBasefee")

ethrpc/multicall_test.go

Lines changed: 22 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ import (
88

99
"github.com/0xsequence/ethkit/ethrpc"
1010
"github.com/0xsequence/ethkit/ethrpc/multicall"
11-
"github.com/0xsequence/ethkit/go-ethereum/accounts/abi"
1211
"github.com/0xsequence/ethkit/go-ethereum/common"
1312
"github.com/davecgh/go-spew/spew"
1413
"github.com/stretchr/testify/assert"
@@ -45,79 +44,79 @@ func TestMulticall(t *testing.T) {
4544
CallData: multicall.BaseFee(),
4645
AllowFailure: true,
4746
},
48-
Outputs: []multicall.Output{{Type: uint256(), Output: &baseFee}},
47+
Outputs: []multicall.Output{multicall.Uint256(&baseFee)},
4948
},
5049
{
5150
Multicall3Call3Value: multicall.Multicall3Call3Value{
5251
Target: multicallAddress,
5352
CallData: multicall.BlockHash(big.NewInt(int64(height) - 100)),
5453
AllowFailure: true,
5554
},
56-
Outputs: []multicall.Output{{Type: bytes32(), Output: &blockHash}},
55+
Outputs: []multicall.Output{multicall.Bytes32(&blockHash)},
5756
},
5857
{
5958
Multicall3Call3Value: multicall.Multicall3Call3Value{
6059
Target: multicallAddress,
6160
CallData: multicall.BlockNumber(),
6261
AllowFailure: true,
6362
},
64-
Outputs: []multicall.Output{{Type: uint256(), Output: &blockNumber}},
63+
Outputs: []multicall.Output{multicall.Uint256(&blockNumber)},
6564
},
6665
{
6766
Multicall3Call3Value: multicall.Multicall3Call3Value{
6867
Target: multicallAddress,
6968
CallData: multicall.ChainID(),
7069
AllowFailure: true,
7170
},
72-
Outputs: []multicall.Output{{Type: uint256(), Output: &chainID}},
71+
Outputs: []multicall.Output{multicall.Uint256(&chainID)},
7372
},
7473
{
7574
Multicall3Call3Value: multicall.Multicall3Call3Value{
7675
Target: multicallAddress,
7776
CallData: multicall.BlockCoinbase(),
7877
AllowFailure: true,
7978
},
80-
Outputs: []multicall.Output{{Type: address(), Output: &coinbase}},
79+
Outputs: []multicall.Output{multicall.Address(&coinbase)},
8180
},
8281
{
8382
Multicall3Call3Value: multicall.Multicall3Call3Value{
8483
Target: multicallAddress,
8584
CallData: multicall.BlockDifficulty(),
8685
AllowFailure: true,
8786
},
88-
Outputs: []multicall.Output{{Type: uint256(), Output: &blockDifficulty}},
87+
Outputs: []multicall.Output{multicall.Uint256(&blockDifficulty)},
8988
},
9089
{
9190
Multicall3Call3Value: multicall.Multicall3Call3Value{
9291
Target: multicallAddress,
9392
CallData: multicall.BlockGasLimit(),
9493
AllowFailure: true,
9594
},
96-
Outputs: []multicall.Output{{Type: uint256(), Output: &blockGasLimit}},
95+
Outputs: []multicall.Output{multicall.Uint256(&blockGasLimit)},
9796
},
9897
{
9998
Multicall3Call3Value: multicall.Multicall3Call3Value{
10099
Target: multicallAddress,
101100
CallData: multicall.BlockTimestamp(),
102101
AllowFailure: true,
103102
},
104-
Outputs: []multicall.Output{{Type: uint256(), Output: &blockTimestamp}},
103+
Outputs: []multicall.Output{multicall.Uint256(&blockTimestamp)},
105104
},
106105
{
107106
Multicall3Call3Value: multicall.Multicall3Call3Value{
108107
Target: multicallAddress,
109108
CallData: multicall.Balance(common.HexToAddress("0xc06145782F31030dB1C40B203bE6B0fD53410B6d")),
110109
AllowFailure: true,
111110
},
112-
Outputs: []multicall.Output{{Type: uint256(), Output: &balance}},
111+
Outputs: []multicall.Output{multicall.Uint256(&balance)},
113112
},
114113
{
115114
Multicall3Call3Value: multicall.Multicall3Call3Value{
116115
Target: multicallAddress,
117116
CallData: multicall.LastBlockHash(),
118117
AllowFailure: true,
119118
},
120-
Outputs: []multicall.Output{{Type: bytes32(), Output: &lastBlockHash}},
119+
Outputs: []multicall.Output{multicall.Bytes32(&lastBlockHash)},
121120
},
122121
})
123122
assert.NoError(t, err)
@@ -168,79 +167,79 @@ func TestBatchCall(t *testing.T) {
168167
CallData: multicall.BaseFee(),
169168
AllowFailure: true,
170169
},
171-
Outputs: []multicall.Output{{Type: uint256(), Output: &baseFee}},
170+
Outputs: []multicall.Output{multicall.Uint256(&baseFee)},
172171
},
173172
{
174173
Multicall3Call3Value: multicall.Multicall3Call3Value{
175174
Target: multicallAddress,
176175
CallData: multicall.BlockHash(big.NewInt(int64(height) - 100)),
177176
AllowFailure: true,
178177
},
179-
Outputs: []multicall.Output{{Type: bytes32(), Output: &blockHash}},
178+
Outputs: []multicall.Output{multicall.Bytes32(&blockHash)},
180179
},
181180
{
182181
Multicall3Call3Value: multicall.Multicall3Call3Value{
183182
Target: multicallAddress,
184183
CallData: multicall.BlockNumber(),
185184
AllowFailure: true,
186185
},
187-
Outputs: []multicall.Output{{Type: uint256(), Output: &blockNumber}},
186+
Outputs: []multicall.Output{multicall.Uint256(&blockNumber)},
188187
},
189188
{
190189
Multicall3Call3Value: multicall.Multicall3Call3Value{
191190
Target: multicallAddress,
192191
CallData: multicall.ChainID(),
193192
AllowFailure: true,
194193
},
195-
Outputs: []multicall.Output{{Type: uint256(), Output: &chainID}},
194+
Outputs: []multicall.Output{multicall.Uint256(&chainID)},
196195
},
197196
{
198197
Multicall3Call3Value: multicall.Multicall3Call3Value{
199198
Target: multicallAddress,
200199
CallData: multicall.BlockCoinbase(),
201200
AllowFailure: true,
202201
},
203-
Outputs: []multicall.Output{{Type: address(), Output: &coinbase}},
202+
Outputs: []multicall.Output{multicall.Address(&coinbase)},
204203
},
205204
{
206205
Multicall3Call3Value: multicall.Multicall3Call3Value{
207206
Target: multicallAddress,
208207
CallData: multicall.BlockDifficulty(),
209208
AllowFailure: true,
210209
},
211-
Outputs: []multicall.Output{{Type: uint256(), Output: &blockDifficulty}},
210+
Outputs: []multicall.Output{multicall.Uint256(&blockDifficulty)},
212211
},
213212
{
214213
Multicall3Call3Value: multicall.Multicall3Call3Value{
215214
Target: multicallAddress,
216215
CallData: multicall.BlockGasLimit(),
217216
AllowFailure: true,
218217
},
219-
Outputs: []multicall.Output{{Type: uint256(), Output: &blockGasLimit}},
218+
Outputs: []multicall.Output{multicall.Uint256(&blockGasLimit)},
220219
},
221220
{
222221
Multicall3Call3Value: multicall.Multicall3Call3Value{
223222
Target: multicallAddress,
224223
CallData: multicall.BlockTimestamp(),
225224
AllowFailure: true,
226225
},
227-
Outputs: []multicall.Output{{Type: uint256(), Output: &blockTimestamp}},
226+
Outputs: []multicall.Output{multicall.Uint256(&blockTimestamp)},
228227
},
229228
{
230229
Multicall3Call3Value: multicall.Multicall3Call3Value{
231230
Target: multicallAddress,
232231
CallData: multicall.Balance(common.HexToAddress("0xc06145782F31030dB1C40B203bE6B0fD53410B6d")),
233232
AllowFailure: true,
234233
},
235-
Outputs: []multicall.Output{{Type: uint256(), Output: &balance}},
234+
Outputs: []multicall.Output{multicall.Uint256(&balance)},
236235
},
237236
{
238237
Multicall3Call3Value: multicall.Multicall3Call3Value{
239238
Target: multicallAddress,
240239
CallData: multicall.LastBlockHash(),
241240
AllowFailure: true,
242241
},
243-
Outputs: []multicall.Output{{Type: bytes32(), Output: &lastBlockHash}},
242+
Outputs: []multicall.Output{multicall.Bytes32(&lastBlockHash)},
244243
},
245244
})
246245
assert.NoError(t, err)
@@ -280,7 +279,7 @@ func TestMulticallBig(t *testing.T) {
280279
CallData: multicall.Balance(common.HexToAddress("0xc06145782F31030dB1C40B203bE6B0fD53410B6d")),
281280
AllowFailure: true,
282281
},
283-
Outputs: []multicall.Output{{Type: uint256(), Output: &balance}},
282+
Outputs: []multicall.Output{multicall.Uint256(&balance)},
284283
})
285284
}
286285

@@ -313,7 +312,7 @@ func TestBatchCallBig(t *testing.T) {
313312
CallData: multicall.Balance(common.HexToAddress("0xc06145782F31030dB1C40B203bE6B0fD53410B6d")),
314313
AllowFailure: true,
315314
},
316-
Outputs: []multicall.Output{{Type: uint256(), Output: &balance}},
315+
Outputs: []multicall.Output{multicall.Uint256(&balance)},
317316
})
318317
}
319318

@@ -327,18 +326,3 @@ func TestBatchCallBig(t *testing.T) {
327326

328327
fmt.Println("balance:", balance)
329328
}
330-
331-
func address() abi.Argument {
332-
type_, _ := abi.NewType("address", "", nil)
333-
return abi.Argument{Type: type_}
334-
}
335-
336-
func bytes32() abi.Argument {
337-
type_, _ := abi.NewType("bytes32", "", nil)
338-
return abi.Argument{Type: type_}
339-
}
340-
341-
func uint256() abi.Argument {
342-
type_, _ := abi.NewType("uint256", "", nil)
343-
return abi.Argument{Type: type_}
344-
}

0 commit comments

Comments
 (0)