hermez-node/eth/rollup.go
eth/rollup.goRaw
1package eth
2
3import (
4 "context"
5 "fmt"
6 "math/big"
7 "strconv"
8 "strings"
9
10 "github.com/ethereum/go-ethereum"
11 "github.com/ethereum/go-ethereum/accounts/abi"
12 "github.com/ethereum/go-ethereum/accounts/abi/bind"
13 ethCommon "github.com/ethereum/go-ethereum/common"
14 "github.com/ethereum/go-ethereum/core/types"
15 "github.com/ethereum/go-ethereum/crypto"
16 "github.com/ethereum/go-ethereum/ethclient"
17 "github.com/hermeznetwork/hermez-node/common"
18 Hermez "github.com/hermeznetwork/hermez-node/eth/contracts/hermez"
19 HEZ "github.com/hermeznetwork/hermez-node/eth/contracts/tokenHEZ"
20 "github.com/hermeznetwork/hermez-node/log"
21 "github.com/hermeznetwork/tracerr"
22 "github.com/iden3/go-iden3-crypto/babyjub"
23)
24
25// QueueStruct is the queue of L1Txs for a batch
26type QueueStruct struct {
27 L1TxQueue []common.L1Tx
28 TotalL1TxFee *big.Int
29}
30
31// NewQueueStruct creates a new clear QueueStruct.
32func NewQueueStruct() *QueueStruct {
33 return &QueueStruct{
34 L1TxQueue: make([]common.L1Tx, 0),
35 TotalL1TxFee: big.NewInt(0),
36 }
37}
38
39// RollupState represents the state of the Rollup in the Smart Contract
40type RollupState struct {
41 StateRoot *big.Int
42 ExitRoots []*big.Int
43 // ExitNullifierMap map[[256 / 8]byte]bool
44 ExitNullifierMap map[int64]map[int64]bool // batchNum -> idx -> bool
45 TokenList []ethCommon.Address
46 TokenMap map[ethCommon.Address]bool
47 MapL1TxQueue map[int64]*QueueStruct
48 LastL1L2Batch int64
49 CurrentToForgeL1TxsNum int64
50 LastToForgeL1TxsNum int64
51 CurrentIdx int64
52}
53
54// RollupEventInitialize is the InitializeHermezEvent event of the
55// Smart Contract
56type RollupEventInitialize struct {
57 ForgeL1L2BatchTimeout uint8
58 FeeAddToken *big.Int
59 WithdrawalDelay uint64
60}
61
62// RollupVariables returns the RollupVariables from the initialize event
63func (ei *RollupEventInitialize) RollupVariables() *common.RollupVariables {
64 var buckets [common.RollupConstNumBuckets]common.BucketParams
65 for i := range buckets {
66 buckets[i] = common.BucketParams{
67 CeilUSD: big.NewInt(0),
68 Withdrawals: big.NewInt(0),
69 BlockWithdrawalRate: big.NewInt(0),
70 MaxWithdrawals: big.NewInt(0),
71 }
72 }
73 return &common.RollupVariables{
74 EthBlockNum: 0,
75 FeeAddToken: ei.FeeAddToken,
76 ForgeL1L2BatchTimeout: int64(ei.ForgeL1L2BatchTimeout),
77 WithdrawalDelay: ei.WithdrawalDelay,
78 Buckets: buckets,
79 SafeMode: false,
80 }
81}
82
83// RollupEventL1UserTx is an event of the Rollup Smart Contract
84type RollupEventL1UserTx struct {
85 // ToForgeL1TxsNum int64 // QueueIndex *big.Int
86 // Position int // TransactionIndex *big.Int
87 L1UserTx common.L1Tx
88}
89
90// RollupEventL1UserTxAux is an event of the Rollup Smart Contract
91type rollupEventL1UserTxAux struct {
92 ToForgeL1TxsNum uint64 // QueueIndex *big.Int
93 Position uint8 // TransactionIndex *big.Int
94 L1UserTx []byte
95}
96
97// RollupEventAddToken is an event of the Rollup Smart Contract
98type RollupEventAddToken struct {
99 TokenAddress ethCommon.Address
100 TokenID uint32
101}
102
103// RollupEventForgeBatch is an event of the Rollup Smart Contract
104type RollupEventForgeBatch struct {
105 BatchNum int64
106 // Sender ethCommon.Address
107 EthTxHash ethCommon.Hash
108 L1UserTxsLen uint16
109}
110
111// RollupEventUpdateForgeL1L2BatchTimeout is an event of the Rollup Smart Contract
112type RollupEventUpdateForgeL1L2BatchTimeout struct {
113 NewForgeL1L2BatchTimeout int64
114}
115
116// RollupEventUpdateFeeAddToken is an event of the Rollup Smart Contract
117type RollupEventUpdateFeeAddToken struct {
118 NewFeeAddToken *big.Int
119}
120
121// RollupEventWithdraw is an event of the Rollup Smart Contract
122type RollupEventWithdraw struct {
123 Idx uint64
124 NumExitRoot uint64
125 InstantWithdraw bool
126 TxHash ethCommon.Hash // Hash of the transaction that generated this event
127}
128
129type rollupEventUpdateBucketWithdrawAux struct {
130 NumBucket uint8
131 BlockStamp *big.Int
132 Withdrawals *big.Int
133}
134
135// RollupEventUpdateBucketWithdraw is an event of the Rollup Smart Contract
136type RollupEventUpdateBucketWithdraw struct {
137 NumBucket int
138 BlockStamp int64 // blockNum
139 Withdrawals *big.Int
140}
141
142// RollupEventUpdateWithdrawalDelay is an event of the Rollup Smart Contract
143type RollupEventUpdateWithdrawalDelay struct {
144 NewWithdrawalDelay uint64
145}
146
147// RollupUpdateBucketsParameters are the bucket parameters used in an update
148type RollupUpdateBucketsParameters struct {
149 CeilUSD *big.Int
150 Withdrawals *big.Int
151 BlockWithdrawalRate *big.Int
152 MaxWithdrawals *big.Int
153}
154
155type rollupEventUpdateBucketsParametersAux struct {
156 ArrayBuckets [common.RollupConstNumBuckets][4]*big.Int
157}
158
159// RollupEventUpdateBucketsParameters is an event of the Rollup Smart Contract
160type RollupEventUpdateBucketsParameters struct {
161 // ArrayBuckets [common.RollupConstNumBuckets][4]*big.Int
162 ArrayBuckets [common.RollupConstNumBuckets]RollupUpdateBucketsParameters
163 SafeMode bool
164}
165
166// RollupEventUpdateTokenExchange is an event of the Rollup Smart Contract
167type RollupEventUpdateTokenExchange struct {
168 AddressArray []ethCommon.Address
169 ValueArray []uint64
170}
171
172// RollupEventSafeMode is an event of the Rollup Smart Contract
173type RollupEventSafeMode struct {
174}
175
176// RollupEvents is the list of events in a block of the Rollup Smart Contract
177type RollupEvents struct {
178 L1UserTx []RollupEventL1UserTx
179 AddToken []RollupEventAddToken
180 ForgeBatch []RollupEventForgeBatch
181 UpdateForgeL1L2BatchTimeout []RollupEventUpdateForgeL1L2BatchTimeout
182 UpdateFeeAddToken []RollupEventUpdateFeeAddToken
183 Withdraw []RollupEventWithdraw
184 UpdateWithdrawalDelay []RollupEventUpdateWithdrawalDelay
185 UpdateBucketWithdraw []RollupEventUpdateBucketWithdraw
186 UpdateBucketsParameters []RollupEventUpdateBucketsParameters
187 UpdateTokenExchange []RollupEventUpdateTokenExchange
188 SafeMode []RollupEventSafeMode
189}
190
191// NewRollupEvents creates an empty RollupEvents with the slices initialized.
192func NewRollupEvents() RollupEvents {
193 return RollupEvents{
194 L1UserTx: make([]RollupEventL1UserTx, 0),
195 AddToken: make([]RollupEventAddToken, 0),
196 ForgeBatch: make([]RollupEventForgeBatch, 0),
197 UpdateForgeL1L2BatchTimeout: make([]RollupEventUpdateForgeL1L2BatchTimeout, 0),
198 UpdateFeeAddToken: make([]RollupEventUpdateFeeAddToken, 0),
199 Withdraw: make([]RollupEventWithdraw, 0),
200 }
201}
202
203// RollupForgeBatchArgs are the arguments to the ForgeBatch function in the Rollup Smart Contract
204type RollupForgeBatchArgs struct {
205 NewLastIdx int64
206 NewStRoot *big.Int
207 NewExitRoot *big.Int
208 L1UserTxs []common.L1Tx
209 L1CoordinatorTxs []common.L1Tx
210 L1CoordinatorTxsAuths [][]byte // Authorization for accountCreations for each L1CoordinatorTx
211 L2TxsData []common.L2Tx
212 FeeIdxCoordinator []common.Idx
213 // Circuit selector
214 VerifierIdx uint8
215 L1Batch bool
216 ProofA [2]*big.Int
217 ProofB [2][2]*big.Int
218 ProofC [2]*big.Int
219}
220
221// RollupForgeBatchArgsAux are the arguments to the ForgeBatch function in the Rollup Smart Contract
222type rollupForgeBatchArgsAux struct {
223 NewLastIdx *big.Int
224 NewStRoot *big.Int
225 NewExitRoot *big.Int
226 EncodedL1CoordinatorTx []byte
227 L1L2TxsData []byte
228 FeeIdxCoordinator []byte
229 // Circuit selector
230 VerifierIdx uint8
231 L1Batch bool
232 ProofA [2]*big.Int
233 ProofB [2][2]*big.Int
234 ProofC [2]*big.Int
235}
236
237// RollupInterface is the inteface to to Rollup Smart Contract
238type RollupInterface interface {
239 //
240 // Smart Contract Methods
241 //
242
243 // Public Functions
244
245 RollupForgeBatch(*RollupForgeBatchArgs) (*types.Transaction, error)
246 RollupAddToken(tokenAddress ethCommon.Address, feeAddToken, deadline *big.Int) (*types.Transaction, error)
247
248 RollupWithdrawMerkleProof(babyPubKey babyjub.PublicKeyComp, tokenID uint32, numExitRoot, idx int64, amount *big.Int, siblings []*big.Int, instantWithdraw bool) (*types.Transaction, error)
249 RollupWithdrawCircuit(proofA, proofC [2]*big.Int, proofB [2][2]*big.Int, tokenID uint32, numExitRoot, idx int64, amount *big.Int, instantWithdraw bool) (*types.Transaction, error)
250
251 RollupL1UserTxERC20ETH(fromBJJ babyjub.PublicKeyComp, fromIdx int64, depositAmount *big.Int, amount *big.Int, tokenID uint32, toIdx int64) (*types.Transaction, error)
252 RollupL1UserTxERC20Permit(fromBJJ babyjub.PublicKeyComp, fromIdx int64, depositAmount *big.Int, amount *big.Int, tokenID uint32, toIdx int64, deadline *big.Int) (tx *types.Transaction, err error)
253
254 // Governance Public Functions
255 RollupUpdateForgeL1L2BatchTimeout(newForgeL1L2BatchTimeout int64) (*types.Transaction, error)
256 RollupUpdateFeeAddToken(newFeeAddToken *big.Int) (*types.Transaction, error)
257
258 // Viewers
259 RollupRegisterTokensCount() (*big.Int, error)
260 RollupLastForgedBatch() (int64, error)
261
262 //
263 // Smart Contract Status
264 //
265
266 RollupConstants() (*common.RollupConstants, error)
267 RollupEventsByBlock(blockNum int64) (*RollupEvents, *ethCommon.Hash, error)
268 RollupForgeBatchArgs(ethCommon.Hash, uint16) (*RollupForgeBatchArgs, *ethCommon.Address, error)
269 RollupEventInit() (*RollupEventInitialize, int64, error)
270}
271
272//
273// Implementation
274//
275
276// RollupClient is the implementation of the interface to the Rollup Smart Contract in ethereum.
277type RollupClient struct {
278 client *EthereumClient
279 chainID *big.Int
280 address ethCommon.Address
281 tokenHEZCfg TokenConfig
282 hermez *Hermez.Hermez
283 tokenHEZ *HEZ.HEZ
284 contractAbi abi.ABI
285 opts *bind.CallOpts
286}
287
288// NewRollupClient creates a new RollupClient
289func NewRollupClient(client *EthereumClient, address ethCommon.Address, tokenHEZCfg TokenConfig) (*RollupClient, error) {
290 contractAbi, err := abi.JSON(strings.NewReader(string(Hermez.HermezABI)))
291 if err != nil {
292 return nil, tracerr.Wrap(err)
293 }
294 hermez, err := Hermez.NewHermez(address, client.Client())
295 if err != nil {
296 return nil, tracerr.Wrap(err)
297 }
298 tokenHEZ, err := HEZ.NewHEZ(tokenHEZCfg.Address, client.Client())
299 if err != nil {
300 return nil, tracerr.Wrap(err)
301 }
302 chainID, err := client.client.ChainID(context.Background())
303 if err != nil {
304 return nil, tracerr.Wrap(err)
305 }
306 return &RollupClient{
307 client: client,
308 chainID: chainID,
309 address: address,
310 tokenHEZCfg: tokenHEZCfg,
311 hermez: hermez,
312 tokenHEZ: tokenHEZ,
313 contractAbi: contractAbi,
314 opts: newCallOpts(),
315 }, nil
316}
317
318// RollupForgeBatch is the interface to call the smart contract function
319func (c *RollupClient) RollupForgeBatch(args *RollupForgeBatchArgs) (tx *types.Transaction, err error) {
320 if tx, err = c.client.CallAuth(
321 1000000, //nolint:gomnd
322 func(ec *ethclient.Client, auth *bind.TransactOpts) (*types.Transaction, error) {
323 rollupConst, err := c.RollupConstants()
324 if err != nil {
325 return nil, tracerr.Wrap(err)
326 }
327 nLevels := rollupConst.Verifiers[args.VerifierIdx].NLevels
328 lenBytes := nLevels / 8 //nolint:gomnd
329 newLastIdx := big.NewInt(int64(args.NewLastIdx))
330 // L1CoordinatorBytes
331 var l1CoordinatorBytes []byte
332 for i := 0; i < len(args.L1CoordinatorTxs); i++ {
333 l1 := args.L1CoordinatorTxs[i]
334 bytesl1, err := l1.BytesCoordinatorTx(args.L1CoordinatorTxsAuths[i])
335 if err != nil {
336 return nil, tracerr.Wrap(err)
337 }
338 l1CoordinatorBytes = append(l1CoordinatorBytes, bytesl1[:]...)
339 }
340 // L1L2TxData
341 var l1l2TxData []byte
342 for i := 0; i < len(args.L1UserTxs); i++ {
343 l1User := args.L1UserTxs[i]
344 bytesl1User, err := l1User.BytesDataAvailability(uint32(nLevels))
345 if err != nil {
346 return nil, tracerr.Wrap(err)
347 }
348 l1l2TxData = append(l1l2TxData, bytesl1User[:]...)
349 }
350 for i := 0; i < len(args.L1CoordinatorTxs); i++ {
351 l1Coord := args.L1CoordinatorTxs[i]
352 bytesl1Coord, err := l1Coord.BytesDataAvailability(uint32(nLevels))
353 if err != nil {
354 return nil, tracerr.Wrap(err)
355 }
356 l1l2TxData = append(l1l2TxData, bytesl1Coord[:]...)
357 }
358 for i := 0; i < len(args.L2TxsData); i++ {
359 l2 := args.L2TxsData[i]
360 bytesl2, err := l2.BytesDataAvailability(uint32(nLevels))
361 if err != nil {
362 return nil, tracerr.Wrap(err)
363 }
364 l1l2TxData = append(l1l2TxData, bytesl2[:]...)
365 }
366 // FeeIdxCoordinator
367 var feeIdxCoordinator []byte
368 if len(args.FeeIdxCoordinator) > common.RollupConstMaxFeeIdxCoordinator {
369 return nil, tracerr.Wrap(fmt.Errorf("len(args.FeeIdxCoordinator) > %v",
370 common.RollupConstMaxFeeIdxCoordinator))
371 }
372 for i := 0; i < common.RollupConstMaxFeeIdxCoordinator; i++ {
373 feeIdx := common.Idx(0)
374 if i < len(args.FeeIdxCoordinator) {
375 feeIdx = args.FeeIdxCoordinator[i]
376 }
377 bytesFeeIdx, err := feeIdx.Bytes()
378 if err != nil {
379 return nil, tracerr.Wrap(err)
380 }
381 feeIdxCoordinator = append(feeIdxCoordinator, bytesFeeIdx[len(bytesFeeIdx)-int(lenBytes):]...)
382 }
383 return c.hermez.ForgeBatch(auth, newLastIdx, args.NewStRoot, args.NewExitRoot, l1CoordinatorBytes, l1l2TxData, feeIdxCoordinator, args.VerifierIdx, args.L1Batch, args.ProofA, args.ProofB, args.ProofC)
384 },
385 ); err != nil {
386 return nil, tracerr.Wrap(fmt.Errorf("Failed forge batch: %w", err))
387 }
388 return tx, nil
389}
390
391// RollupAddToken is the interface to call the smart contract function.
392// `feeAddToken` is the amount of HEZ tokens that will be paid to add the
393// token. `feeAddToken` must match the public value of the smart contract.
394func (c *RollupClient) RollupAddToken(tokenAddress ethCommon.Address, feeAddToken, deadline *big.Int) (tx *types.Transaction, err error) {
395 if tx, err = c.client.CallAuth(
396 0,
397 func(ec *ethclient.Client, auth *bind.TransactOpts) (*types.Transaction, error) {
398 owner := c.client.account.Address
399 spender := c.address
400 nonce, err := c.tokenHEZ.Nonces(c.opts, owner)
401 if err != nil {
402 return nil, tracerr.Wrap(err)
403 }
404 tokenName := c.tokenHEZCfg.Name
405 tokenAddr := c.tokenHEZCfg.Address
406 digest, _ := createPermitDigest(tokenAddr, owner, spender, c.chainID, feeAddToken, nonce, deadline, tokenName)
407 signature, _ := c.client.ks.SignHash(*c.client.account, digest)
408 permit := createPermit(owner, spender, feeAddToken, deadline, digest, signature)
409
410 return c.hermez.AddToken(auth, tokenAddress, permit)
411 },
412 ); err != nil {
413 return nil, tracerr.Wrap(fmt.Errorf("Failed add Token %w", err))
414 }
415 return tx, nil
416}
417
418// RollupWithdrawMerkleProof is the interface to call the smart contract function
419func (c *RollupClient) RollupWithdrawMerkleProof(fromBJJ babyjub.PublicKeyComp, tokenID uint32, numExitRoot, idx int64, amount *big.Int, siblings []*big.Int, instantWithdraw bool) (tx *types.Transaction, err error) {
420 if tx, err = c.client.CallAuth(
421 0,
422 func(ec *ethclient.Client, auth *bind.TransactOpts) (*types.Transaction, error) {
423 pkCompB := common.SwapEndianness(fromBJJ[:])
424 babyPubKey := new(big.Int).SetBytes(pkCompB)
425 numExitRootB := uint32(numExitRoot)
426 idxBig := big.NewInt(idx)
427 return c.hermez.WithdrawMerkleProof(auth, tokenID, amount, babyPubKey, numExitRootB, siblings, idxBig, instantWithdraw)
428 },
429 ); err != nil {
430 return nil, tracerr.Wrap(fmt.Errorf("Failed update WithdrawMerkleProof: %w", err))
431 }
432 return tx, nil
433}
434
435// RollupWithdrawCircuit is the interface to call the smart contract function
436func (c *RollupClient) RollupWithdrawCircuit(proofA, proofC [2]*big.Int, proofB [2][2]*big.Int, tokenID uint32, numExitRoot, idx int64, amount *big.Int, instantWithdraw bool) (*types.Transaction, error) {
437 log.Error("TODO")
438 return nil, tracerr.Wrap(errTODO)
439}
440
441// RollupL1UserTxERC20ETH is the interface to call the smart contract function
442func (c *RollupClient) RollupL1UserTxERC20ETH(fromBJJ babyjub.PublicKeyComp, fromIdx int64, depositAmount *big.Int, amount *big.Int, tokenID uint32, toIdx int64) (tx *types.Transaction, err error) {
443 if tx, err = c.client.CallAuth(
444 0,
445 func(ec *ethclient.Client, auth *bind.TransactOpts) (*types.Transaction, error) {
446 var babyPubKey *big.Int
447 if fromBJJ != common.EmptyBJJComp {
448 pkCompB := common.SwapEndianness(fromBJJ[:])
449 babyPubKey = new(big.Int).SetBytes(pkCompB)
450 } else {
451 babyPubKey = big.NewInt(0)
452 }
453 fromIdxBig := big.NewInt(fromIdx)
454 toIdxBig := big.NewInt(toIdx)
455 depositAmountF, err := common.NewFloat16(depositAmount)
456 if err != nil {
457 return nil, tracerr.Wrap(err)
458 }
459 amountF, err := common.NewFloat16(amount)
460 if err != nil {
461 return nil, tracerr.Wrap(err)
462 }
463 if tokenID == 0 {
464 auth.Value = depositAmount
465 }
466 var permit []byte
467 return c.hermez.AddL1Transaction(auth, babyPubKey, fromIdxBig, uint16(depositAmountF),
468 uint16(amountF), tokenID, toIdxBig, permit)
469 },
470 ); err != nil {
471 return nil, tracerr.Wrap(fmt.Errorf("Failed add L1 Tx ERC20/ETH: %w", err))
472 }
473 return tx, nil
474}
475
476// RollupL1UserTxERC20Permit is the interface to call the smart contract function
477func (c *RollupClient) RollupL1UserTxERC20Permit(fromBJJ babyjub.PublicKeyComp, fromIdx int64, depositAmount *big.Int, amount *big.Int, tokenID uint32, toIdx int64, deadline *big.Int) (tx *types.Transaction, err error) {
478 if tx, err = c.client.CallAuth(
479 0,
480 func(ec *ethclient.Client, auth *bind.TransactOpts) (*types.Transaction, error) {
481 var babyPubKey *big.Int
482 if fromBJJ != common.EmptyBJJComp {
483 pkCompB := common.SwapEndianness(fromBJJ[:])
484 babyPubKey = new(big.Int).SetBytes(pkCompB)
485 } else {
486 babyPubKey = big.NewInt(0)
487 }
488 fromIdxBig := big.NewInt(fromIdx)
489 toIdxBig := big.NewInt(toIdx)
490 depositAmountF, err := common.NewFloat16(depositAmount)
491 if err != nil {
492 return nil, tracerr.Wrap(err)
493 }
494 amountF, err := common.NewFloat16(amount)
495 if err != nil {
496 return nil, tracerr.Wrap(err)
497 }
498 if tokenID == 0 {
499 auth.Value = depositAmount
500 }
501 owner := c.client.account.Address
502 spender := c.address
503 nonce, err := c.tokenHEZ.Nonces(c.opts, owner)
504 if err != nil {
505 return nil, tracerr.Wrap(err)
506 }
507 tokenName := c.tokenHEZCfg.Name
508 tokenAddr := c.tokenHEZCfg.Address
509 digest, _ := createPermitDigest(tokenAddr, owner, spender, c.chainID, amount, nonce, deadline, tokenName)
510 signature, _ := c.client.ks.SignHash(*c.client.account, digest)
511 permit := createPermit(owner, spender, amount, deadline, digest, signature)
512 return c.hermez.AddL1Transaction(auth, babyPubKey, fromIdxBig, uint16(depositAmountF),
513 uint16(amountF), tokenID, toIdxBig, permit)
514 },
515 ); err != nil {
516 return nil, tracerr.Wrap(fmt.Errorf("Failed add L1 Tx ERC20Permit: %w", err))
517 }
518 return tx, nil
519}
520
521// RollupRegisterTokensCount is the interface to call the smart contract function
522func (c *RollupClient) RollupRegisterTokensCount() (registerTokensCount *big.Int, err error) {
523 if err := c.client.Call(func(ec *ethclient.Client) error {
524 registerTokensCount, err = c.hermez.RegisterTokensCount(c.opts)
525 return tracerr.Wrap(err)
526 }); err != nil {
527 return nil, tracerr.Wrap(err)
528 }
529 return registerTokensCount, nil
530}
531
532// RollupLastForgedBatch is the interface to call the smart contract function
533func (c *RollupClient) RollupLastForgedBatch() (lastForgedBatch int64, err error) {
534 if err := c.client.Call(func(ec *ethclient.Client) error {
535 _lastForgedBatch, err := c.hermez.LastForgedBatch(c.opts)
536 lastForgedBatch = int64(_lastForgedBatch)
537 return tracerr.Wrap(err)
538 }); err != nil {
539 return 0, tracerr.Wrap(err)
540 }
541 return lastForgedBatch, nil
542}
543
544// RollupUpdateForgeL1L2BatchTimeout is the interface to call the smart contract function
545func (c *RollupClient) RollupUpdateForgeL1L2BatchTimeout(newForgeL1L2BatchTimeout int64) (tx *types.Transaction, err error) {
546 if tx, err = c.client.CallAuth(
547 0,
548 func(ec *ethclient.Client, auth *bind.TransactOpts) (*types.Transaction, error) {
549 return c.hermez.UpdateForgeL1L2BatchTimeout(auth, uint8(newForgeL1L2BatchTimeout))
550 },
551 ); err != nil {
552 return nil, tracerr.Wrap(fmt.Errorf("Failed update ForgeL1L2BatchTimeout: %w", err))
553 }
554 return tx, nil
555}
556
557// RollupUpdateFeeAddToken is the interface to call the smart contract function
558func (c *RollupClient) RollupUpdateFeeAddToken(newFeeAddToken *big.Int) (tx *types.Transaction, err error) {
559 if tx, err = c.client.CallAuth(
560 0,
561 func(ec *ethclient.Client, auth *bind.TransactOpts) (*types.Transaction, error) {
562 return c.hermez.UpdateFeeAddToken(auth, newFeeAddToken)
563 },
564 ); err != nil {
565 return nil, tracerr.Wrap(fmt.Errorf("Failed update FeeAddToken: %w", err))
566 }
567 return tx, nil
568}
569
570// RollupUpdateBucketsParameters is the interface to call the smart contract function
571func (c *RollupClient) RollupUpdateBucketsParameters(
572 arrayBuckets [common.RollupConstNumBuckets]RollupUpdateBucketsParameters,
573) (tx *types.Transaction, err error) {
574 params := [common.RollupConstNumBuckets][4]*big.Int{}
575 for i, bucket := range arrayBuckets {
576 params[i][0] = bucket.CeilUSD
577 params[i][1] = bucket.Withdrawals
578 params[i][2] = bucket.BlockWithdrawalRate
579 params[i][3] = bucket.MaxWithdrawals
580 }
581 if tx, err = c.client.CallAuth(
582 12500000, //nolint:gomnd
583 func(ec *ethclient.Client, auth *bind.TransactOpts) (*types.Transaction, error) {
584 return c.hermez.UpdateBucketsParameters(auth, params)
585 },
586 ); err != nil {
587 return nil, tracerr.Wrap(fmt.Errorf("Failed update Buckets Parameters: %w", err))
588 }
589 return tx, nil
590}
591
592// RollupUpdateTokenExchange is the interface to call the smart contract function
593func (c *RollupClient) RollupUpdateTokenExchange(addressArray []ethCommon.Address, valueArray []uint64) (tx *types.Transaction, err error) {
594 if tx, err = c.client.CallAuth(
595 0,
596 func(ec *ethclient.Client, auth *bind.TransactOpts) (*types.Transaction, error) {
597 return c.hermez.UpdateTokenExchange(auth, addressArray, valueArray)
598 },
599 ); err != nil {
600 return nil, tracerr.Wrap(fmt.Errorf("Failed update Token Exchange: %w", err))
601 }
602 return tx, nil
603}
604
605// RollupUpdateWithdrawalDelay is the interface to call the smart contract function
606func (c *RollupClient) RollupUpdateWithdrawalDelay(newWithdrawalDelay int64) (tx *types.Transaction, err error) {
607 if tx, err = c.client.CallAuth(
608 0,
609 func(ec *ethclient.Client, auth *bind.TransactOpts) (*types.Transaction, error) {
610 return c.hermez.UpdateWithdrawalDelay(auth, uint64(newWithdrawalDelay))
611 },
612 ); err != nil {
613 return nil, tracerr.Wrap(fmt.Errorf("Failed update WithdrawalDelay: %w", err))
614 }
615 return tx, nil
616}
617
618// RollupSafeMode is the interface to call the smart contract function
619func (c *RollupClient) RollupSafeMode() (tx *types.Transaction, err error) {
620 if tx, err = c.client.CallAuth(
621 0,
622 func(ec *ethclient.Client, auth *bind.TransactOpts) (*types.Transaction, error) {
623 return c.hermez.SafeMode(auth)
624 },
625 ); err != nil {
626 return nil, tracerr.Wrap(fmt.Errorf("Failed update Safe Mode: %w", err))
627 }
628 return tx, nil
629}
630
631// RollupInstantWithdrawalViewer is the interface to call the smart contract function
632func (c *RollupClient) RollupInstantWithdrawalViewer(tokenAddress ethCommon.Address, amount *big.Int) (instantAllowed bool, err error) {
633 if err := c.client.Call(func(ec *ethclient.Client) error {
634 instantAllowed, err = c.hermez.InstantWithdrawalViewer(c.opts, tokenAddress, amount)
635 return tracerr.Wrap(err)
636 }); err != nil {
637 return false, tracerr.Wrap(err)
638 }
639 return instantAllowed, nil
640}
641
642// RollupConstants returns the Constants of the Rollup Smart Contract
643func (c *RollupClient) RollupConstants() (rollupConstants *common.RollupConstants, err error) {
644 rollupConstants = new(common.RollupConstants)
645 if err := c.client.Call(func(ec *ethclient.Client) error {
646 absoluteMaxL1L2BatchTimeout, err := c.hermez.ABSOLUTEMAXL1L2BATCHTIMEOUT(c.opts)
647 if err != nil {
648 return tracerr.Wrap(err)
649 }
650 rollupConstants.AbsoluteMaxL1L2BatchTimeout = int64(absoluteMaxL1L2BatchTimeout)
651 rollupConstants.TokenHEZ, err = c.hermez.TokenHEZ(c.opts)
652 if err != nil {
653 return tracerr.Wrap(err)
654 }
655 for i := int64(0); i < int64(common.LenVerifiers); i++ {
656 var newRollupVerifier common.RollupVerifierStruct
657 rollupVerifier, err := c.hermez.RollupVerifiers(c.opts, big.NewInt(i))
658 if err != nil {
659 return tracerr.Wrap(err)
660 }
661 newRollupVerifier.MaxTx = rollupVerifier.MaxTx.Int64()
662 newRollupVerifier.NLevels = rollupVerifier.NLevels.Int64()
663 rollupConstants.Verifiers = append(rollupConstants.Verifiers, newRollupVerifier)
664 }
665 rollupConstants.HermezAuctionContract, err = c.hermez.HermezAuctionContract(c.opts)
666 if err != nil {
667 return tracerr.Wrap(err)
668 }
669 rollupConstants.HermezGovernanceAddress, err = c.hermez.HermezGovernanceAddress(c.opts)
670 if err != nil {
671 return tracerr.Wrap(err)
672 }
673 rollupConstants.WithdrawDelayerContract, err = c.hermez.WithdrawDelayerContract(c.opts)
674 return tracerr.Wrap(err)
675 }); err != nil {
676 return nil, tracerr.Wrap(err)
677 }
678 return rollupConstants, nil
679}
680
681var (
682 logHermezL1UserTxEvent = crypto.Keccak256Hash([]byte("L1UserTxEvent(uint32,uint8,bytes)"))
683 logHermezAddToken = crypto.Keccak256Hash([]byte("AddToken(address,uint32)"))
684 logHermezForgeBatch = crypto.Keccak256Hash([]byte("ForgeBatch(uint32,uint16)"))
685 logHermezUpdateForgeL1L2BatchTimeout = crypto.Keccak256Hash([]byte("UpdateForgeL1L2BatchTimeout(uint8)"))
686 logHermezUpdateFeeAddToken = crypto.Keccak256Hash([]byte("UpdateFeeAddToken(uint256)"))
687 logHermezWithdrawEvent = crypto.Keccak256Hash([]byte("WithdrawEvent(uint48,uint32,bool)"))
688 logHermezUpdateBucketWithdraw = crypto.Keccak256Hash([]byte("UpdateBucketWithdraw(uint8,uint256,uint256)"))
689 logHermezUpdateWithdrawalDelay = crypto.Keccak256Hash([]byte("UpdateWithdrawalDelay(uint64)"))
690 logHermezUpdateBucketsParameters = crypto.Keccak256Hash([]byte("UpdateBucketsParameters(uint256[4][" +
691 strconv.Itoa(common.RollupConstNumBuckets) + "])"))
692 logHermezUpdateTokenExchange = crypto.Keccak256Hash([]byte("UpdateTokenExchange(address[],uint64[])"))
693 logHermezSafeMode = crypto.Keccak256Hash([]byte("SafeMode()"))
694 logHermezInitialize = crypto.Keccak256Hash([]byte("InitializeHermezEvent(uint8,uint256,uint64)"))
695)
696
697// RollupEventInit returns the initialize event with its corresponding block number
698func (c *RollupClient) RollupEventInit() (*RollupEventInitialize, int64, error) {
699 query := ethereum.FilterQuery{
700 Addresses: []ethCommon.Address{
701 c.address,
702 },
703 Topics: [][]ethCommon.Hash{{logHermezInitialize}},
704 }
705 logs, err := c.client.client.FilterLogs(context.Background(), query)
706 if err != nil {
707 return nil, 0, tracerr.Wrap(err)
708 }
709 if len(logs) != 1 {
710 return nil, 0, tracerr.Wrap(fmt.Errorf("no event of type InitializeHermezEvent found"))
711 }
712 vLog := logs[0]
713 if vLog.Topics[0] != logHermezInitialize {
714 return nil, 0, tracerr.Wrap(fmt.Errorf("event is not InitializeHermezEvent"))
715 }
716
717 var rollupInit RollupEventInitialize
718 if err := c.contractAbi.UnpackIntoInterface(&rollupInit, "InitializeHermezEvent", vLog.Data); err != nil {
719 return nil, 0, tracerr.Wrap(err)
720 }
721 return &rollupInit, int64(vLog.BlockNumber), tracerr.Wrap(err)
722}
723
724// RollupEventsByBlock returns the events in a block that happened in the Rollup Smart Contract
725func (c *RollupClient) RollupEventsByBlock(blockNum int64) (*RollupEvents, *ethCommon.Hash, error) {
726 var rollupEvents RollupEvents
727 var blockHash *ethCommon.Hash
728
729 query := ethereum.FilterQuery{
730 FromBlock: big.NewInt(blockNum),
731 ToBlock: big.NewInt(blockNum),
732 Addresses: []ethCommon.Address{
733 c.address,
734 },
735 BlockHash: nil,
736 Topics: [][]ethCommon.Hash{},
737 }
738 logs, err := c.client.client.FilterLogs(context.Background(), query)
739 if err != nil {
740 return nil, nil, tracerr.Wrap(err)
741 }
742 if len(logs) > 0 {
743 blockHash = &logs[0].BlockHash
744 }
745 for _, vLog := range logs {
746 if vLog.BlockHash != *blockHash {
747 log.Errorw("Block hash mismatch", "expected", blockHash.String(), "got", vLog.BlockHash.String())
748 return nil, nil, tracerr.Wrap(ErrBlockHashMismatchEvent)
749 }
750 switch vLog.Topics[0] {
751 case logHermezL1UserTxEvent:
752 var L1UserTxAux rollupEventL1UserTxAux
753 var L1UserTx RollupEventL1UserTx
754 err := c.contractAbi.UnpackIntoInterface(&L1UserTxAux, "L1UserTxEvent", vLog.Data)
755 if err != nil {
756 return nil, nil, tracerr.Wrap(err)
757 }
758 L1Tx, err := common.L1UserTxFromBytes(L1UserTxAux.L1UserTx)
759 if err != nil {
760 return nil, nil, tracerr.Wrap(err)
761 }
762 toForgeL1TxsNum := new(big.Int).SetBytes(vLog.Topics[1][:]).Int64()
763 L1Tx.ToForgeL1TxsNum = &toForgeL1TxsNum
764 L1Tx.Position = int(new(big.Int).SetBytes(vLog.Topics[2][:]).Int64())
765 L1Tx.UserOrigin = true
766 L1UserTx.L1UserTx = *L1Tx
767 rollupEvents.L1UserTx = append(rollupEvents.L1UserTx, L1UserTx)
768 case logHermezAddToken:
769 var addToken RollupEventAddToken
770 err := c.contractAbi.UnpackIntoInterface(&addToken, "AddToken", vLog.Data)
771 if err != nil {
772 return nil, nil, tracerr.Wrap(err)
773 }
774 addToken.TokenAddress = ethCommon.BytesToAddress(vLog.Topics[1].Bytes())
775 rollupEvents.AddToken = append(rollupEvents.AddToken, addToken)
776 case logHermezForgeBatch:
777 var forgeBatch RollupEventForgeBatch
778 err := c.contractAbi.UnpackIntoInterface(&forgeBatch, "ForgeBatch", vLog.Data)
779 if err != nil {
780 return nil, nil, tracerr.Wrap(err)
781 }
782 forgeBatch.BatchNum = new(big.Int).SetBytes(vLog.Topics[1][:]).Int64()
783 forgeBatch.EthTxHash = vLog.TxHash
784 // forgeBatch.Sender = vLog.Address
785 rollupEvents.ForgeBatch = append(rollupEvents.ForgeBatch, forgeBatch)
786 case logHermezUpdateForgeL1L2BatchTimeout:
787 var updateForgeL1L2BatchTimeout struct {
788 NewForgeL1L2BatchTimeout uint8
789 }
790 err := c.contractAbi.UnpackIntoInterface(&updateForgeL1L2BatchTimeout, "UpdateForgeL1L2BatchTimeout", vLog.Data)
791 if err != nil {
792 return nil, nil, tracerr.Wrap(err)
793 }
794 rollupEvents.UpdateForgeL1L2BatchTimeout = append(rollupEvents.UpdateForgeL1L2BatchTimeout,
795 RollupEventUpdateForgeL1L2BatchTimeout{
796 NewForgeL1L2BatchTimeout: int64(updateForgeL1L2BatchTimeout.NewForgeL1L2BatchTimeout),
797 })
798 case logHermezUpdateFeeAddToken:
799 var updateFeeAddToken RollupEventUpdateFeeAddToken
800 err := c.contractAbi.UnpackIntoInterface(&updateFeeAddToken, "UpdateFeeAddToken", vLog.Data)
801 if err != nil {
802 return nil, nil, tracerr.Wrap(err)
803 }
804 rollupEvents.UpdateFeeAddToken = append(rollupEvents.UpdateFeeAddToken, updateFeeAddToken)
805 case logHermezWithdrawEvent:
806 var withdraw RollupEventWithdraw
807 withdraw.Idx = new(big.Int).SetBytes(vLog.Topics[1][:]).Uint64()
808 withdraw.NumExitRoot = new(big.Int).SetBytes(vLog.Topics[2][:]).Uint64()
809 instantWithdraw := new(big.Int).SetBytes(vLog.Topics[3][:]).Uint64()
810 if instantWithdraw == 1 {
811 withdraw.InstantWithdraw = true
812 }
813 withdraw.TxHash = vLog.TxHash
814 rollupEvents.Withdraw = append(rollupEvents.Withdraw, withdraw)
815 case logHermezUpdateBucketWithdraw:
816 var updateBucketWithdrawAux rollupEventUpdateBucketWithdrawAux
817 var updateBucketWithdraw RollupEventUpdateBucketWithdraw
818 err := c.contractAbi.UnpackIntoInterface(&updateBucketWithdrawAux, "UpdateBucketWithdraw", vLog.Data)
819 if err != nil {
820 return nil, nil, tracerr.Wrap(err)
821 }
822 updateBucketWithdraw.Withdrawals = updateBucketWithdrawAux.Withdrawals
823 updateBucketWithdraw.NumBucket = int(new(big.Int).SetBytes(vLog.Topics[1][:]).Int64())
824 updateBucketWithdraw.BlockStamp = new(big.Int).SetBytes(vLog.Topics[2][:]).Int64()
825 rollupEvents.UpdateBucketWithdraw = append(rollupEvents.UpdateBucketWithdraw, updateBucketWithdraw)
826
827 case logHermezUpdateWithdrawalDelay:
828 var withdrawalDelay RollupEventUpdateWithdrawalDelay
829 err := c.contractAbi.UnpackIntoInterface(&withdrawalDelay, "UpdateWithdrawalDelay", vLog.Data)
830 if err != nil {
831 return nil, nil, tracerr.Wrap(err)
832 }
833 rollupEvents.UpdateWithdrawalDelay = append(rollupEvents.UpdateWithdrawalDelay, withdrawalDelay)
834 case logHermezUpdateBucketsParameters:
835 var bucketsParametersAux rollupEventUpdateBucketsParametersAux
836 var bucketsParameters RollupEventUpdateBucketsParameters
837 err := c.contractAbi.UnpackIntoInterface(&bucketsParametersAux, "UpdateBucketsParameters", vLog.Data)
838 if err != nil {
839 return nil, nil, tracerr.Wrap(err)
840 }
841 for i, bucket := range bucketsParametersAux.ArrayBuckets {
842 bucketsParameters.ArrayBuckets[i].CeilUSD = bucket[0]
843 bucketsParameters.ArrayBuckets[i].Withdrawals = bucket[1]
844 bucketsParameters.ArrayBuckets[i].BlockWithdrawalRate = bucket[2]
845 bucketsParameters.ArrayBuckets[i].MaxWithdrawals = bucket[3]
846 }
847 rollupEvents.UpdateBucketsParameters = append(rollupEvents.UpdateBucketsParameters, bucketsParameters)
848 case logHermezUpdateTokenExchange:
849 var tokensExchange RollupEventUpdateTokenExchange
850 err := c.contractAbi.UnpackIntoInterface(&tokensExchange, "UpdateTokenExchange", vLog.Data)
851 if err != nil {
852 return nil, nil, tracerr.Wrap(err)
853 }
854 rollupEvents.UpdateTokenExchange = append(rollupEvents.UpdateTokenExchange, tokensExchange)
855 case logHermezSafeMode:
856 var safeMode RollupEventSafeMode
857 rollupEvents.SafeMode = append(rollupEvents.SafeMode, safeMode)
858 // Also add an UpdateBucketsParameter with
859 // SafeMode=true to keep the order between `safeMode`
860 // and `UpdateBucketsParameters`
861 bucketsParameters := RollupEventUpdateBucketsParameters{
862 SafeMode: true,
863 }
864 for i := range bucketsParameters.ArrayBuckets {
865 bucketsParameters.ArrayBuckets[i].CeilUSD = big.NewInt(0)
866 bucketsParameters.ArrayBuckets[i].Withdrawals = big.NewInt(0)
867 bucketsParameters.ArrayBuckets[i].BlockWithdrawalRate = big.NewInt(0)
868 bucketsParameters.ArrayBuckets[i].MaxWithdrawals = big.NewInt(0)
869 }
870 rollupEvents.UpdateBucketsParameters = append(rollupEvents.UpdateBucketsParameters,
871 bucketsParameters)
872 }
873 }
874 return &rollupEvents, blockHash, nil
875}
876
877// RollupForgeBatchArgs returns the arguments used in a ForgeBatch call in the
878// Rollup Smart Contract in the given transaction, and the sender address.
879func (c *RollupClient) RollupForgeBatchArgs(ethTxHash ethCommon.Hash, l1UserTxsLen uint16) (*RollupForgeBatchArgs, *ethCommon.Address, error) {
880 tx, _, err := c.client.client.TransactionByHash(context.Background(), ethTxHash)
881 if err != nil {
882 return nil, nil, tracerr.Wrap(err)
883 }
884 txData := tx.Data()
885
886 method, err := c.contractAbi.MethodById(txData[:4])
887 if err != nil {
888 return nil, nil, tracerr.Wrap(err)
889 }
890 receipt, err := c.client.client.TransactionReceipt(context.Background(), ethTxHash)
891 if err != nil {
892 return nil, nil, tracerr.Wrap(err)
893 }
894 sender, err := c.client.client.TransactionSender(context.Background(), tx, receipt.Logs[0].BlockHash, receipt.Logs[0].Index)
895 if err != nil {
896 return nil, nil, tracerr.Wrap(err)
897 }
898 var aux rollupForgeBatchArgsAux
899 if values, err := method.Inputs.Unpack(txData[4:]); err != nil {
900 return nil, nil, tracerr.Wrap(err)
901 } else if err := method.Inputs.Copy(&aux, values); err != nil {
902 return nil, nil, tracerr.Wrap(err)
903 }
904 rollupForgeBatchArgs := RollupForgeBatchArgs{
905 L1Batch: aux.L1Batch,
906 NewExitRoot: aux.NewExitRoot,
907 NewLastIdx: aux.NewLastIdx.Int64(),
908 NewStRoot: aux.NewStRoot,
909 ProofA: aux.ProofA,
910 ProofB: aux.ProofB,
911 ProofC: aux.ProofC,
912 VerifierIdx: aux.VerifierIdx,
913 L1CoordinatorTxs: []common.L1Tx{},
914 L1CoordinatorTxsAuths: [][]byte{},
915 L2TxsData: []common.L2Tx{},
916 FeeIdxCoordinator: []common.Idx{},
917 }
918 rollupConsts, err := c.RollupConstants()
919 if err != nil {
920 return nil, nil, tracerr.Wrap(err)
921 }
922 nLevels := rollupConsts.Verifiers[rollupForgeBatchArgs.VerifierIdx].NLevels
923 lenL1L2TxsBytes := int((nLevels/8)*2 + 2 + 1)
924 numBytesL1TxUser := int(l1UserTxsLen) * lenL1L2TxsBytes
925 numTxsL1Coord := len(aux.EncodedL1CoordinatorTx) / common.L1CoordinatorTxBytesLen
926 numBytesL1TxCoord := numTxsL1Coord * lenL1L2TxsBytes
927 numBeginL2Tx := numBytesL1TxCoord + numBytesL1TxUser
928 l1UserTxsData := []byte{}
929 if l1UserTxsLen > 0 {
930 l1UserTxsData = aux.L1L2TxsData[:numBytesL1TxUser]
931 }
932 for i := 0; i < int(l1UserTxsLen); i++ {
933 l1Tx, err := common.L1TxFromDataAvailability(l1UserTxsData[i*lenL1L2TxsBytes:(i+1)*lenL1L2TxsBytes], uint32(nLevels))
934 if err != nil {
935 return nil, nil, tracerr.Wrap(err)
936 }
937 rollupForgeBatchArgs.L1UserTxs = append(rollupForgeBatchArgs.L1UserTxs, *l1Tx)
938 }
939 l2TxsData := []byte{}
940 if numBeginL2Tx < len(aux.L1L2TxsData) {
941 l2TxsData = aux.L1L2TxsData[numBeginL2Tx:]
942 }
943 numTxsL2 := len(l2TxsData) / lenL1L2TxsBytes
944 for i := 0; i < numTxsL2; i++ {
945 l2Tx, err := common.L2TxFromBytesDataAvailability(l2TxsData[i*lenL1L2TxsBytes:(i+1)*lenL1L2TxsBytes], int(nLevels))
946 if err != nil {
947 return nil, nil, tracerr.Wrap(err)
948 }
949 rollupForgeBatchArgs.L2TxsData = append(rollupForgeBatchArgs.L2TxsData, *l2Tx)
950 }
951 for i := 0; i < numTxsL1Coord; i++ {
952 bytesL1Coordinator := aux.EncodedL1CoordinatorTx[i*common.L1CoordinatorTxBytesLen : (i+1)*common.L1CoordinatorTxBytesLen]
953 var signature []byte
954 v := bytesL1Coordinator[0]
955 s := bytesL1Coordinator[1:33]
956 r := bytesL1Coordinator[33:65]
957 signature = append(signature, r[:]...)
958 signature = append(signature, s[:]...)
959 signature = append(signature, v)
960 l1Tx, err := common.L1CoordinatorTxFromBytes(bytesL1Coordinator, c.chainID, c.address)
961 if err != nil {
962 return nil, nil, tracerr.Wrap(err)
963 }
964 rollupForgeBatchArgs.L1CoordinatorTxs = append(rollupForgeBatchArgs.L1CoordinatorTxs, *l1Tx)
965 rollupForgeBatchArgs.L1CoordinatorTxsAuths = append(rollupForgeBatchArgs.L1CoordinatorTxsAuths, signature)
966 }
967 lenFeeIdxCoordinatorBytes := int(nLevels / 8) //nolint:gomnd
968 numFeeIdxCoordinator := len(aux.FeeIdxCoordinator) / lenFeeIdxCoordinatorBytes
969 for i := 0; i < numFeeIdxCoordinator; i++ {
970 var paddedFeeIdx [6]byte
971 // TODO: This check is not necessary: the first case will always work. Test it before removing the if.
972 if lenFeeIdxCoordinatorBytes < common.IdxBytesLen {
973 copy(paddedFeeIdx[6-lenFeeIdxCoordinatorBytes:], aux.FeeIdxCoordinator[i*lenFeeIdxCoordinatorBytes:(i+1)*lenFeeIdxCoordinatorBytes])
974 } else {
975 copy(paddedFeeIdx[:], aux.FeeIdxCoordinator[i*lenFeeIdxCoordinatorBytes:(i+1)*lenFeeIdxCoordinatorBytes])
976 }
977 feeIdxCoordinator, err := common.IdxFromBytes(paddedFeeIdx[:])
978 if err != nil {
979 return nil, nil, tracerr.Wrap(err)
980 }
981 if feeIdxCoordinator != common.Idx(0) {
982 rollupForgeBatchArgs.FeeIdxCoordinator = append(rollupForgeBatchArgs.FeeIdxCoordinator, feeIdxCoordinator)
983 }
984 }
985 return &rollupForgeBatchArgs, &sender, nil
986}