Skip to content

Commit 56669b7

Browse files
committed
Feat: Add Helpers For Batched Calls
1 parent 2c9081b commit 56669b7

2 files changed

Lines changed: 135 additions & 0 deletions

File tree

pkg/batching/batching.go

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
package batching
2+
3+
import (
4+
"errors"
5+
"fmt"
6+
"math/big"
7+
"strings"
8+
"sync"
9+
10+
"github.com/ethereum/go-ethereum/accounts/abi"
11+
"github.com/ethereum/go-ethereum/common"
12+
)
13+
14+
const executeBatchABIJSON = `[
15+
{
16+
"type": "function",
17+
"name": "executeBatch",
18+
"stateMutability": "payable",
19+
"inputs": [
20+
{
21+
"name": "calls",
22+
"type": "tuple[]",
23+
"components": [
24+
{"name": "target", "type": "address"},
25+
{"name": "value", "type": "uint256"},
26+
{"name": "data", "type": "bytes"}
27+
]
28+
}
29+
],
30+
"outputs": []
31+
}
32+
]`
33+
34+
type executeCall struct {
35+
Target common.Address `abi:"target"`
36+
Value *big.Int `abi:"value"`
37+
Data []byte `abi:"data"`
38+
}
39+
40+
// Call represents one low-level call executed by a batch delegate contract.
41+
type Call struct {
42+
Target common.Address
43+
Value *big.Int
44+
Data []byte
45+
}
46+
47+
var (
48+
onceBatchABI sync.Once
49+
batchABI abi.ABI
50+
batchABIErr error
51+
)
52+
53+
func getExecuteBatchABI() (abi.ABI, error) {
54+
onceBatchABI.Do(func() {
55+
batchABI, batchABIErr = abi.JSON(strings.NewReader(executeBatchABIJSON))
56+
})
57+
return batchABI, batchABIErr
58+
}
59+
60+
// EncodeExecuteBatch encodes calldata for executeBatch((address,uint256,bytes)[] calls).
61+
func EncodeExecuteBatch(calls []Call) ([]byte, error) {
62+
if len(calls) == 0 {
63+
return nil, errors.New("calls must not be empty")
64+
}
65+
execCalls := make([]executeCall, len(calls))
66+
for i, c := range calls {
67+
value := c.Value
68+
if value == nil {
69+
value = big.NewInt(0)
70+
}
71+
execCalls[i] = executeCall{
72+
Target: c.Target,
73+
Value: value,
74+
Data: c.Data,
75+
}
76+
}
77+
78+
parsedABI, err := getExecuteBatchABI()
79+
if err != nil {
80+
return nil, fmt.Errorf("parse executeBatch ABI: %w", err)
81+
}
82+
data, err := parsedABI.Pack("executeBatch", execCalls)
83+
if err != nil {
84+
return nil, fmt.Errorf("pack executeBatch calldata: %w", err)
85+
}
86+
return data, nil
87+
}
88+
89+
// EncodeFunctionCall is a small helper for example programs.
90+
func EncodeFunctionCall(abiJSON string, method string, args ...any) ([]byte, error) {
91+
parsedABI, err := abi.JSON(strings.NewReader(abiJSON))
92+
if err != nil {
93+
return nil, fmt.Errorf("parse ABI: %w", err)
94+
}
95+
out, err := parsedABI.Pack(method, args...)
96+
if err != nil {
97+
return nil, fmt.Errorf("pack %s: %w", method, err)
98+
}
99+
return out, nil
100+
}

pkg/batching/batching_test.go

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
package batching_test
2+
3+
import (
4+
"bytes"
5+
"math/big"
6+
"testing"
7+
8+
"github.com/eipcodelab/eip7702-go/pkg/batching"
9+
"github.com/ethereum/go-ethereum/common"
10+
"github.com/ethereum/go-ethereum/crypto"
11+
)
12+
13+
func TestEncodeExecuteBatch(t *testing.T) {
14+
calls := []batching.Call{
15+
{Target: common.HexToAddress("0x0000000000000000000000000000000000000001"), Value: big.NewInt(0), Data: []byte{0x01}},
16+
{Target: common.HexToAddress("0x0000000000000000000000000000000000000002"), Value: big.NewInt(1), Data: []byte{0x02}},
17+
}
18+
calldata, err := batching.EncodeExecuteBatch(calls)
19+
if err != nil {
20+
t.Fatalf("encode: %v", err)
21+
}
22+
if len(calldata) < 4 {
23+
t.Fatal("calldata is too short")
24+
}
25+
methodID := crypto.Keccak256([]byte("executeBatch((address,uint256,bytes)[])"))[:4]
26+
if !bytes.Equal(methodID, calldata[:4]) {
27+
t.Fatalf("unexpected selector: got %x want %x", calldata[:4], methodID)
28+
}
29+
}
30+
31+
func TestEncodeExecuteBatchRejectsEmptyCalls(t *testing.T) {
32+
if _, err := batching.EncodeExecuteBatch(nil); err == nil {
33+
t.Fatal("expected error for empty call list")
34+
}
35+
}

0 commit comments

Comments
 (0)