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, *bind.TransactOpts) (*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, blockHash *ethCommon.Hash) (*RollupEvents, 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 consts *common.RollupConstants
287}
288
289// NewRollupClient creates a new RollupClient
290func NewRollupClient(client *EthereumClient, address ethCommon.Address, tokenHEZCfg TokenConfig) (*RollupClient, error) {
291 contractAbi, err := abi.JSON(strings.NewReader(string(Hermez.HermezABI)))
292 if err != nil {
293 return nil, tracerr.Wrap(err)
294 }
295 hermez, err := Hermez.NewHermez(address, client.Client())
296 if err != nil {
297 return nil, tracerr.Wrap(err)
298 }
299 tokenHEZ, err := HEZ.NewHEZ(tokenHEZCfg.Address, client.Client())
300 if err != nil {
301 return nil, tracerr.Wrap(err)
302 }
303 chainID, err := client.EthChainID()
304 if err != nil {
305 return nil, tracerr.Wrap(err)
306 }
307 c := &RollupClient{
308 client: client,
309 chainID: chainID,
310 address: address,
311 tokenHEZCfg: tokenHEZCfg,
312 hermez: hermez,
313 tokenHEZ: tokenHEZ,
314 contractAbi: contractAbi,
315 opts: newCallOpts(),
316 }
317 consts, err := c.RollupConstants()
318 if err != nil {
319 return nil, tracerr.Wrap(err)
320 }
321 c.consts = consts
322 return c, nil
323}
324
325// RollupForgeBatch is the interface to call the smart contract function
326func (c *RollupClient) RollupForgeBatch(args *RollupForgeBatchArgs, auth *bind.TransactOpts) (tx *types.Transaction, err error) {
327 if auth == nil {
328 auth, err = c.client.NewAuth()
329 if err != nil {
330 return nil, err
331 }
332 auth.GasLimit = 1000000
333 }
334
335 nLevels := c.consts.Verifiers[args.VerifierIdx].NLevels
336 lenBytes := nLevels / 8 //nolint:gomnd
337 newLastIdx := big.NewInt(int64(args.NewLastIdx))
338 // L1CoordinatorBytes
339 var l1CoordinatorBytes []byte
340 for i := 0; i < len(args.L1CoordinatorTxs); i++ {
341 l1 := args.L1CoordinatorTxs[i]
342 bytesl1, err := l1.BytesCoordinatorTx(args.L1CoordinatorTxsAuths[i])
343 if err != nil {
344 return nil, tracerr.Wrap(err)
345 }
346 l1CoordinatorBytes = append(l1CoordinatorBytes, bytesl1[:]...)
347 }
348 // L1L2TxData
349 var l1l2TxData []byte
350 for i := 0; i < len(args.L1UserTxs); i++ {
351 l1User := args.L1UserTxs[i]
352 bytesl1User, err := l1User.BytesDataAvailability(uint32(nLevels))
353 if err != nil {
354 return nil, tracerr.Wrap(err)
355 }
356 l1l2TxData = append(l1l2TxData, bytesl1User[:]...)
357 }
358 for i := 0; i < len(args.L1CoordinatorTxs); i++ {
359 l1Coord := args.L1CoordinatorTxs[i]
360 bytesl1Coord, err := l1Coord.BytesDataAvailability(uint32(nLevels))
361 if err != nil {
362 return nil, tracerr.Wrap(err)
363 }
364 l1l2TxData = append(l1l2TxData, bytesl1Coord[:]...)
365 }
366 for i := 0; i < len(args.L2TxsData); i++ {
367 l2 := args.L2TxsData[i]
368 bytesl2, err := l2.BytesDataAvailability(uint32(nLevels))
369 if err != nil {
370 return nil, tracerr.Wrap(err)
371 }
372 l1l2TxData = append(l1l2TxData, bytesl2[:]...)
373 }
374 // FeeIdxCoordinator
375 var feeIdxCoordinator []byte
376 if len(args.FeeIdxCoordinator) > common.RollupConstMaxFeeIdxCoordinator {
377 return nil, tracerr.Wrap(fmt.Errorf("len(args.FeeIdxCoordinator) > %v",
378 common.RollupConstMaxFeeIdxCoordinator))
379 }
380 for i := 0; i < common.RollupConstMaxFeeIdxCoordinator; i++ {
381 feeIdx := common.Idx(0)
382 if i < len(args.FeeIdxCoordinator) {
383 feeIdx = args.FeeIdxCoordinator[i]
384 }
385 bytesFeeIdx, err := feeIdx.Bytes()
386 if err != nil {
387 return nil, tracerr.Wrap(err)
388 }
389 feeIdxCoordinator = append(feeIdxCoordinator,
390 bytesFeeIdx[len(bytesFeeIdx)-int(lenBytes):]...)
391 }
392 tx, err = c.hermez.ForgeBatch(auth, newLastIdx, args.NewStRoot, args.NewExitRoot,
393 l1CoordinatorBytes, l1l2TxData, feeIdxCoordinator, args.VerifierIdx, args.L1Batch,
394 args.ProofA, args.ProofB, args.ProofC)
395 if err != nil {
396 return nil, tracerr.Wrap(fmt.Errorf("Failed Hermez.ForgeBatch: %w", err))
397 }
398 return tx, nil
399}
400
401// RollupAddToken is the interface to call the smart contract function.
402// `feeAddToken` is the amount of HEZ tokens that will be paid to add the
403// token. `feeAddToken` must match the public value of the smart contract.
404func (c *RollupClient) RollupAddToken(tokenAddress ethCommon.Address, feeAddToken, deadline *big.Int) (tx *types.Transaction, err error) {
405 if tx, err = c.client.CallAuth(
406 0,
407 func(ec *ethclient.Client, auth *bind.TransactOpts) (*types.Transaction, error) {
408 owner := c.client.account.Address
409 spender := c.address
410 nonce, err := c.tokenHEZ.Nonces(c.opts, owner)
411 if err != nil {
412 return nil, tracerr.Wrap(err)
413 }
414 tokenName := c.tokenHEZCfg.Name
415 tokenAddr := c.tokenHEZCfg.Address
416 digest, _ := createPermitDigest(tokenAddr, owner, spender, c.chainID, feeAddToken, nonce, deadline, tokenName)
417 signature, _ := c.client.ks.SignHash(*c.client.account, digest)
418 permit := createPermit(owner, spender, feeAddToken, deadline, digest, signature)
419
420 return c.hermez.AddToken(auth, tokenAddress, permit)
421 },
422 ); err != nil {
423 return nil, tracerr.Wrap(fmt.Errorf("Failed add Token %w", err))
424 }
425 return tx, nil
426}
427
428// RollupWithdrawMerkleProof is the interface to call the smart contract function
429func (c *RollupClient) RollupWithdrawMerkleProof(fromBJJ babyjub.PublicKeyComp, tokenID uint32, numExitRoot, idx int64, amount *big.Int, siblings []*big.Int, instantWithdraw bool) (tx *types.Transaction, err error) {
430 if tx, err = c.client.CallAuth(
431 0,
432 func(ec *ethclient.Client, auth *bind.TransactOpts) (*types.Transaction, error) {
433 pkCompB := common.SwapEndianness(fromBJJ[:])
434 babyPubKey := new(big.Int).SetBytes(pkCompB)
435 numExitRootB := uint32(numExitRoot)
436 idxBig := big.NewInt(idx)
437 return c.hermez.WithdrawMerkleProof(auth, tokenID, amount, babyPubKey, numExitRootB, siblings, idxBig, instantWithdraw)
438 },
439 ); err != nil {
440 return nil, tracerr.Wrap(fmt.Errorf("Failed update WithdrawMerkleProof: %w", err))
441 }
442 return tx, nil
443}
444
445// RollupWithdrawCircuit is the interface to call the smart contract function
446func (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) {
447 log.Error("TODO")
448 return nil, tracerr.Wrap(errTODO)
449}
450
451// RollupL1UserTxERC20ETH is the interface to call the smart contract function
452func (c *RollupClient) RollupL1UserTxERC20ETH(fromBJJ babyjub.PublicKeyComp, fromIdx int64, depositAmount *big.Int, amount *big.Int, tokenID uint32, toIdx int64) (tx *types.Transaction, err error) {
453 if tx, err = c.client.CallAuth(
454 0,
455 func(ec *ethclient.Client, auth *bind.TransactOpts) (*types.Transaction, error) {
456 var babyPubKey *big.Int
457 if fromBJJ != common.EmptyBJJComp {
458 pkCompB := common.SwapEndianness(fromBJJ[:])
459 babyPubKey = new(big.Int).SetBytes(pkCompB)
460 } else {
461 babyPubKey = big.NewInt(0)
462 }
463 fromIdxBig := big.NewInt(fromIdx)
464 toIdxBig := big.NewInt(toIdx)
465 depositAmountF, err := common.NewFloat16(depositAmount)
466 if err != nil {
467 return nil, tracerr.Wrap(err)
468 }
469 amountF, err := common.NewFloat16(amount)
470 if err != nil {
471 return nil, tracerr.Wrap(err)
472 }
473 if tokenID == 0 {
474 auth.Value = depositAmount
475 }
476 var permit []byte
477 return c.hermez.AddL1Transaction(auth, babyPubKey, fromIdxBig, uint16(depositAmountF),
478 uint16(amountF), tokenID, toIdxBig, permit)
479 },
480 ); err != nil {
481 return nil, tracerr.Wrap(fmt.Errorf("Failed add L1 Tx ERC20/ETH: %w", err))
482 }
483 return tx, nil
484}
485
486// RollupL1UserTxERC20Permit is the interface to call the smart contract function
487func (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) {
488 if tx, err = c.client.CallAuth(
489 0,
490 func(ec *ethclient.Client, auth *bind.TransactOpts) (*types.Transaction, error) {
491 var babyPubKey *big.Int
492 if fromBJJ != common.EmptyBJJComp {
493 pkCompB := common.SwapEndianness(fromBJJ[:])
494 babyPubKey = new(big.Int).SetBytes(pkCompB)
495 } else {
496 babyPubKey = big.NewInt(0)
497 }
498 fromIdxBig := big.NewInt(fromIdx)
499 toIdxBig := big.NewInt(toIdx)
500 depositAmountF, err := common.NewFloat16(depositAmount)
501 if err != nil {
502 return nil, tracerr.Wrap(err)
503 }
504 amountF, err := common.NewFloat16(amount)
505 if err != nil {
506 return nil, tracerr.Wrap(err)
507 }
508 if tokenID == 0 {
509 auth.Value = depositAmount
510 }
511 owner := c.client.account.Address
512 spender := c.address
513 nonce, err := c.tokenHEZ.Nonces(c.opts, owner)
514 if err != nil {
515 return nil, tracerr.Wrap(err)
516 }
517 tokenName := c.tokenHEZCfg.Name
518 tokenAddr := c.tokenHEZCfg.Address
519 digest, _ := createPermitDigest(tokenAddr, owner, spender, c.chainID, amount, nonce, deadline, tokenName)
520 signature, _ := c.client.ks.SignHash(*c.client.account, digest)
521 permit := createPermit(owner, spender, amount, deadline, digest, signature)
522 return c.hermez.AddL1Transaction(auth, babyPubKey, fromIdxBig, uint16(depositAmountF),
523 uint16(amountF), tokenID, toIdxBig, permit)
524 },
525 ); err != nil {
526 return nil, tracerr.Wrap(fmt.Errorf("Failed add L1 Tx ERC20Permit: %w", err))
527 }
528 return tx, nil
529}
530
531// RollupRegisterTokensCount is the interface to call the smart contract function
532func (c *RollupClient) RollupRegisterTokensCount() (registerTokensCount *big.Int, err error) {
533 if err := c.client.Call(func(ec *ethclient.Client) error {
534 registerTokensCount, err = c.hermez.RegisterTokensCount(c.opts)
535 return tracerr.Wrap(err)
536 }); err != nil {
537 return nil, tracerr.Wrap(err)
538 }
539 return registerTokensCount, nil
540}
541
542// RollupLastForgedBatch is the interface to call the smart contract function
543func (c *RollupClient) RollupLastForgedBatch() (lastForgedBatch int64, err error) {
544 if err := c.client.Call(func(ec *ethclient.Client) error {
545 _lastForgedBatch, err := c.hermez.LastForgedBatch(c.opts)
546 lastForgedBatch = int64(_lastForgedBatch)
547 return tracerr.Wrap(err)
548 }); err != nil {
549 return 0, tracerr.Wrap(err)
550 }
551 return lastForgedBatch, nil
552}
553
554// RollupUpdateForgeL1L2BatchTimeout is the interface to call the smart contract function
555func (c *RollupClient) RollupUpdateForgeL1L2BatchTimeout(newForgeL1L2BatchTimeout int64) (tx *types.Transaction, err error) {
556 if tx, err = c.client.CallAuth(
557 0,
558 func(ec *ethclient.Client, auth *bind.TransactOpts) (*types.Transaction, error) {
559 return c.hermez.UpdateForgeL1L2BatchTimeout(auth, uint8(newForgeL1L2BatchTimeout))
560 },
561 ); err != nil {
562 return nil, tracerr.Wrap(fmt.Errorf("Failed update ForgeL1L2BatchTimeout: %w", err))
563 }
564 return tx, nil
565}
566
567// RollupUpdateFeeAddToken is the interface to call the smart contract function
568func (c *RollupClient) RollupUpdateFeeAddToken(newFeeAddToken *big.Int) (tx *types.Transaction, err error) {
569 if tx, err = c.client.CallAuth(
570 0,
571 func(ec *ethclient.Client, auth *bind.TransactOpts) (*types.Transaction, error) {
572 return c.hermez.UpdateFeeAddToken(auth, newFeeAddToken)
573 },
574 ); err != nil {
575 return nil, tracerr.Wrap(fmt.Errorf("Failed update FeeAddToken: %w", err))
576 }
577 return tx, nil
578}
579
580// RollupUpdateBucketsParameters is the interface to call the smart contract function
581func (c *RollupClient) RollupUpdateBucketsParameters(
582 arrayBuckets [common.RollupConstNumBuckets]RollupUpdateBucketsParameters,
583) (tx *types.Transaction, err error) {
584 params := [common.RollupConstNumBuckets][4]*big.Int{}
585 for i, bucket := range arrayBuckets {
586 params[i][0] = bucket.CeilUSD
587 params[i][1] = bucket.Withdrawals
588 params[i][2] = bucket.BlockWithdrawalRate
589 params[i][3] = bucket.MaxWithdrawals
590 }
591 if tx, err = c.client.CallAuth(
592 12500000, //nolint:gomnd
593 func(ec *ethclient.Client, auth *bind.TransactOpts) (*types.Transaction, error) {
594 return c.hermez.UpdateBucketsParameters(auth, params)
595 },
596 ); err != nil {
597 return nil, tracerr.Wrap(fmt.Errorf("Failed update Buckets Parameters: %w", err))
598 }
599 return tx, nil
600}
601
602// RollupUpdateTokenExchange is the interface to call the smart contract function
603func (c *RollupClient) RollupUpdateTokenExchange(addressArray []ethCommon.Address, valueArray []uint64) (tx *types.Transaction, err error) {
604 if tx, err = c.client.CallAuth(
605 0,
606 func(ec *ethclient.Client, auth *bind.TransactOpts) (*types.Transaction, error) {
607 return c.hermez.UpdateTokenExchange(auth, addressArray, valueArray)
608 },
609 ); err != nil {
610 return nil, tracerr.Wrap(fmt.Errorf("Failed update Token Exchange: %w", err))
611 }
612 return tx, nil
613}
614
615// RollupUpdateWithdrawalDelay is the interface to call the smart contract function
616func (c *RollupClient) RollupUpdateWithdrawalDelay(newWithdrawalDelay int64) (tx *types.Transaction, err error) {
617 if tx, err = c.client.CallAuth(
618 0,
619 func(ec *ethclient.Client, auth *bind.TransactOpts) (*types.Transaction, error) {
620 return c.hermez.UpdateWithdrawalDelay(auth, uint64(newWithdrawalDelay))
621 },
622 ); err != nil {
623 return nil, tracerr.Wrap(fmt.Errorf("Failed update WithdrawalDelay: %w", err))
624 }
625 return tx, nil
626}
627
628// RollupSafeMode is the interface to call the smart contract function
629func (c *RollupClient) RollupSafeMode() (tx *types.Transaction, err error) {
630 if tx, err = c.client.CallAuth(
631 0,
632 func(ec *ethclient.Client, auth *bind.TransactOpts) (*types.Transaction, error) {
633 return c.hermez.SafeMode(auth)
634 },
635 ); err != nil {
636 return nil, tracerr.Wrap(fmt.Errorf("Failed update Safe Mode: %w", err))
637 }
638 return tx, nil
639}
640
641// RollupInstantWithdrawalViewer is the interface to call the smart contract function
642func (c *RollupClient) RollupInstantWithdrawalViewer(tokenAddress ethCommon.Address, amount *big.Int) (instantAllowed bool, err error) {
643 if err := c.client.Call(func(ec *ethclient.Client) error {
644 instantAllowed, err = c.hermez.InstantWithdrawalViewer(c.opts, tokenAddress, amount)
645 return tracerr.Wrap(err)
646 }); err != nil {
647 return false, tracerr.Wrap(err)
648 }
649 return instantAllowed, nil
650}
651
652// RollupConstants returns the Constants of the Rollup Smart Contract
653func (c *RollupClient) RollupConstants() (rollupConstants *common.RollupConstants, err error) {
654 rollupConstants = new(common.RollupConstants)
655 if err := c.client.Call(func(ec *ethclient.Client) error {
656 absoluteMaxL1L2BatchTimeout, err := c.hermez.ABSOLUTEMAXL1L2BATCHTIMEOUT(c.opts)
657 if err != nil {
658 return tracerr.Wrap(err)
659 }
660 rollupConstants.AbsoluteMaxL1L2BatchTimeout = int64(absoluteMaxL1L2BatchTimeout)
661 rollupConstants.TokenHEZ, err = c.hermez.TokenHEZ(c.opts)
662 if err != nil {
663 return tracerr.Wrap(err)
664 }
665 rollupVerifiersLength, err := c.hermez.RollupVerifiersLength(c.opts)
666 if err != nil {
667 return tracerr.Wrap(err)
668 }
669 for i := int64(0); i < rollupVerifiersLength.Int64(); i++ {
670 var newRollupVerifier common.RollupVerifierStruct
671 rollupVerifier, err := c.hermez.RollupVerifiers(c.opts, big.NewInt(i))
672 if err != nil {
673 return tracerr.Wrap(err)
674 }
675 newRollupVerifier.MaxTx = rollupVerifier.MaxTx.Int64()
676 newRollupVerifier.NLevels = rollupVerifier.NLevels.Int64()
677 rollupConstants.Verifiers = append(rollupConstants.Verifiers, newRollupVerifier)
678 }
679 rollupConstants.HermezAuctionContract, err = c.hermez.HermezAuctionContract(c.opts)
680 if err != nil {
681 return tracerr.Wrap(err)
682 }
683 rollupConstants.HermezGovernanceAddress, err = c.hermez.HermezGovernanceAddress(c.opts)
684 if err != nil {
685 return tracerr.Wrap(err)
686 }
687 rollupConstants.WithdrawDelayerContract, err = c.hermez.WithdrawDelayerContract(c.opts)
688 return tracerr.Wrap(err)
689 }); err != nil {
690 return nil, tracerr.Wrap(err)
691 }
692 return rollupConstants, nil
693}
694
695var (
696 logHermezL1UserTxEvent = crypto.Keccak256Hash([]byte("L1UserTxEvent(uint32,uint8,bytes)"))
697 logHermezAddToken = crypto.Keccak256Hash([]byte("AddToken(address,uint32)"))
698 logHermezForgeBatch = crypto.Keccak256Hash([]byte("ForgeBatch(uint32,uint16)"))
699 logHermezUpdateForgeL1L2BatchTimeout = crypto.Keccak256Hash([]byte("UpdateForgeL1L2BatchTimeout(uint8)"))
700 logHermezUpdateFeeAddToken = crypto.Keccak256Hash([]byte("UpdateFeeAddToken(uint256)"))
701 logHermezWithdrawEvent = crypto.Keccak256Hash([]byte("WithdrawEvent(uint48,uint32,bool)"))
702 logHermezUpdateBucketWithdraw = crypto.Keccak256Hash([]byte("UpdateBucketWithdraw(uint8,uint256,uint256)"))
703 logHermezUpdateWithdrawalDelay = crypto.Keccak256Hash([]byte("UpdateWithdrawalDelay(uint64)"))
704 logHermezUpdateBucketsParameters = crypto.Keccak256Hash([]byte("UpdateBucketsParameters(uint256[4][" +
705 strconv.Itoa(common.RollupConstNumBuckets) + "])"))
706 logHermezUpdateTokenExchange = crypto.Keccak256Hash([]byte("UpdateTokenExchange(address[],uint64[])"))
707 logHermezSafeMode = crypto.Keccak256Hash([]byte("SafeMode()"))
708 logHermezInitialize = crypto.Keccak256Hash([]byte("InitializeHermezEvent(uint8,uint256,uint64)"))
709)
710
711// RollupEventInit returns the initialize event with its corresponding block number
712func (c *RollupClient) RollupEventInit() (*RollupEventInitialize, int64, error) {
713 query := ethereum.FilterQuery{
714 Addresses: []ethCommon.Address{
715 c.address,
716 },
717 Topics: [][]ethCommon.Hash{{logHermezInitialize}},
718 }
719 logs, err := c.client.client.FilterLogs(context.Background(), query)
720 if err != nil {
721 return nil, 0, tracerr.Wrap(err)
722 }
723 if len(logs) != 1 {
724 return nil, 0, tracerr.Wrap(fmt.Errorf("no event of type InitializeHermezEvent found"))
725 }
726 vLog := logs[0]
727 if vLog.Topics[0] != logHermezInitialize {
728 return nil, 0, tracerr.Wrap(fmt.Errorf("event is not InitializeHermezEvent"))
729 }
730
731 var rollupInit RollupEventInitialize
732 if err := c.contractAbi.UnpackIntoInterface(&rollupInit, "InitializeHermezEvent", vLog.Data); err != nil {
733 return nil, 0, tracerr.Wrap(err)
734 }
735 return &rollupInit, int64(vLog.BlockNumber), tracerr.Wrap(err)
736}
737
738// RollupEventsByBlock returns the events in a block that happened in the
739// Rollup Smart Contract.
740// To query by blockNum, set blockNum >= 0 and blockHash == nil.
741// To query by blockHash set blockHash != nil, and blockNum will be ignored.
742// If there are no events in that block the result is nil.
743func (c *RollupClient) RollupEventsByBlock(blockNum int64,
744 blockHash *ethCommon.Hash) (*RollupEvents, error) {
745 var rollupEvents RollupEvents
746
747 var blockNumBigInt *big.Int
748 if blockHash == nil {
749 blockNumBigInt = big.NewInt(blockNum)
750 }
751 query := ethereum.FilterQuery{
752 BlockHash: blockHash,
753 FromBlock: blockNumBigInt,
754 ToBlock: blockNumBigInt,
755 Addresses: []ethCommon.Address{
756 c.address,
757 },
758 Topics: [][]ethCommon.Hash{},
759 }
760 logs, err := c.client.client.FilterLogs(context.Background(), query)
761 if err != nil {
762 return nil, tracerr.Wrap(err)
763 }
764 if len(logs) == 0 {
765 return nil, nil
766 }
767
768 for _, vLog := range logs {
769 if blockHash != nil && vLog.BlockHash != *blockHash {
770 log.Errorw("Block hash mismatch", "expected", blockHash.String(), "got", vLog.BlockHash.String())
771 return nil, tracerr.Wrap(ErrBlockHashMismatchEvent)
772 }
773 switch vLog.Topics[0] {
774 case logHermezL1UserTxEvent:
775 var L1UserTxAux rollupEventL1UserTxAux
776 var L1UserTx RollupEventL1UserTx
777 err := c.contractAbi.UnpackIntoInterface(&L1UserTxAux, "L1UserTxEvent", vLog.Data)
778 if err != nil {
779 return nil, tracerr.Wrap(err)
780 }
781 L1Tx, err := common.L1UserTxFromBytes(L1UserTxAux.L1UserTx)
782 if err != nil {
783 return nil, tracerr.Wrap(err)
784 }
785 toForgeL1TxsNum := new(big.Int).SetBytes(vLog.Topics[1][:]).Int64()
786 L1Tx.ToForgeL1TxsNum = &toForgeL1TxsNum
787 L1Tx.Position = int(new(big.Int).SetBytes(vLog.Topics[2][:]).Int64())
788 L1Tx.UserOrigin = true
789 L1UserTx.L1UserTx = *L1Tx
790 rollupEvents.L1UserTx = append(rollupEvents.L1UserTx, L1UserTx)
791 case logHermezAddToken:
792 var addToken RollupEventAddToken
793 err := c.contractAbi.UnpackIntoInterface(&addToken, "AddToken", vLog.Data)
794 if err != nil {
795 return nil, tracerr.Wrap(err)
796 }
797 addToken.TokenAddress = ethCommon.BytesToAddress(vLog.Topics[1].Bytes())
798 rollupEvents.AddToken = append(rollupEvents.AddToken, addToken)
799 case logHermezForgeBatch:
800 var forgeBatch RollupEventForgeBatch
801 err := c.contractAbi.UnpackIntoInterface(&forgeBatch, "ForgeBatch", vLog.Data)
802 if err != nil {
803 return nil, tracerr.Wrap(err)
804 }
805 forgeBatch.BatchNum = new(big.Int).SetBytes(vLog.Topics[1][:]).Int64()
806 forgeBatch.EthTxHash = vLog.TxHash
807 // forgeBatch.Sender = vLog.Address
808 rollupEvents.ForgeBatch = append(rollupEvents.ForgeBatch, forgeBatch)
809 case logHermezUpdateForgeL1L2BatchTimeout:
810 var updateForgeL1L2BatchTimeout struct {
811 NewForgeL1L2BatchTimeout uint8
812 }
813 err := c.contractAbi.UnpackIntoInterface(&updateForgeL1L2BatchTimeout, "UpdateForgeL1L2BatchTimeout", vLog.Data)
814 if err != nil {
815 return nil, tracerr.Wrap(err)
816 }
817 rollupEvents.UpdateForgeL1L2BatchTimeout = append(rollupEvents.UpdateForgeL1L2BatchTimeout,
818 RollupEventUpdateForgeL1L2BatchTimeout{
819 NewForgeL1L2BatchTimeout: int64(updateForgeL1L2BatchTimeout.NewForgeL1L2BatchTimeout),
820 })
821 case logHermezUpdateFeeAddToken:
822 var updateFeeAddToken RollupEventUpdateFeeAddToken
823 err := c.contractAbi.UnpackIntoInterface(&updateFeeAddToken, "UpdateFeeAddToken", vLog.Data)
824 if err != nil {
825 return nil, tracerr.Wrap(err)
826 }
827 rollupEvents.UpdateFeeAddToken = append(rollupEvents.UpdateFeeAddToken, updateFeeAddToken)
828 case logHermezWithdrawEvent:
829 var withdraw RollupEventWithdraw
830 withdraw.Idx = new(big.Int).SetBytes(vLog.Topics[1][:]).Uint64()
831 withdraw.NumExitRoot = new(big.Int).SetBytes(vLog.Topics[2][:]).Uint64()
832 instantWithdraw := new(big.Int).SetBytes(vLog.Topics[3][:]).Uint64()
833 if instantWithdraw == 1 {
834 withdraw.InstantWithdraw = true
835 }
836 withdraw.TxHash = vLog.TxHash
837 rollupEvents.Withdraw = append(rollupEvents.Withdraw, withdraw)
838 case logHermezUpdateBucketWithdraw:
839 var updateBucketWithdrawAux rollupEventUpdateBucketWithdrawAux
840 var updateBucketWithdraw RollupEventUpdateBucketWithdraw
841 err := c.contractAbi.UnpackIntoInterface(&updateBucketWithdrawAux, "UpdateBucketWithdraw", vLog.Data)
842 if err != nil {
843 return nil, tracerr.Wrap(err)
844 }
845 updateBucketWithdraw.Withdrawals = updateBucketWithdrawAux.Withdrawals
846 updateBucketWithdraw.NumBucket = int(new(big.Int).SetBytes(vLog.Topics[1][:]).Int64())
847 updateBucketWithdraw.BlockStamp = new(big.Int).SetBytes(vLog.Topics[2][:]).Int64()
848 rollupEvents.UpdateBucketWithdraw = append(rollupEvents.UpdateBucketWithdraw, updateBucketWithdraw)
849
850 case logHermezUpdateWithdrawalDelay:
851 var withdrawalDelay RollupEventUpdateWithdrawalDelay
852 err := c.contractAbi.UnpackIntoInterface(&withdrawalDelay, "UpdateWithdrawalDelay", vLog.Data)
853 if err != nil {
854 return nil, tracerr.Wrap(err)
855 }
856 rollupEvents.UpdateWithdrawalDelay = append(rollupEvents.UpdateWithdrawalDelay, withdrawalDelay)
857 case logHermezUpdateBucketsParameters:
858 var bucketsParametersAux rollupEventUpdateBucketsParametersAux
859 var bucketsParameters RollupEventUpdateBucketsParameters
860 err := c.contractAbi.UnpackIntoInterface(&bucketsParametersAux, "UpdateBucketsParameters", vLog.Data)
861 if err != nil {
862 return nil, tracerr.Wrap(err)
863 }
864 for i, bucket := range bucketsParametersAux.ArrayBuckets {
865 bucketsParameters.ArrayBuckets[i].CeilUSD = bucket[0]
866 bucketsParameters.ArrayBuckets[i].Withdrawals = bucket[1]
867 bucketsParameters.ArrayBuckets[i].BlockWithdrawalRate = bucket[2]
868 bucketsParameters.ArrayBuckets[i].MaxWithdrawals = bucket[3]
869 }
870 rollupEvents.UpdateBucketsParameters = append(rollupEvents.UpdateBucketsParameters, bucketsParameters)
871 case logHermezUpdateTokenExchange:
872 var tokensExchange RollupEventUpdateTokenExchange
873 err := c.contractAbi.UnpackIntoInterface(&tokensExchange, "UpdateTokenExchange", vLog.Data)
874 if err != nil {
875 return nil, tracerr.Wrap(err)
876 }
877 rollupEvents.UpdateTokenExchange = append(rollupEvents.UpdateTokenExchange, tokensExchange)
878 case logHermezSafeMode:
879 var safeMode RollupEventSafeMode
880 rollupEvents.SafeMode = append(rollupEvents.SafeMode, safeMode)
881 // Also add an UpdateBucketsParameter with
882 // SafeMode=true to keep the order between `safeMode`
883 // and `UpdateBucketsParameters`
884 bucketsParameters := RollupEventUpdateBucketsParameters{
885 SafeMode: true,
886 }
887 for i := range bucketsParameters.ArrayBuckets {
888 bucketsParameters.ArrayBuckets[i].CeilUSD = big.NewInt(0)
889 bucketsParameters.ArrayBuckets[i].Withdrawals = big.NewInt(0)
890 bucketsParameters.ArrayBuckets[i].BlockWithdrawalRate = big.NewInt(0)
891 bucketsParameters.ArrayBuckets[i].MaxWithdrawals = big.NewInt(0)
892 }
893 rollupEvents.UpdateBucketsParameters = append(rollupEvents.UpdateBucketsParameters,
894 bucketsParameters)
895 }
896 }
897 return &rollupEvents, nil
898}
899
900// RollupForgeBatchArgs returns the arguments used in a ForgeBatch call in the
901// Rollup Smart Contract in the given transaction, and the sender address.
902func (c *RollupClient) RollupForgeBatchArgs(ethTxHash ethCommon.Hash, l1UserTxsLen uint16) (*RollupForgeBatchArgs, *ethCommon.Address, error) {
903 tx, _, err := c.client.client.TransactionByHash(context.Background(), ethTxHash)
904 if err != nil {
905 return nil, nil, tracerr.Wrap(fmt.Errorf("TransactionByHash: %w", err))
906 }
907 txData := tx.Data()
908
909 method, err := c.contractAbi.MethodById(txData[:4])
910 if err != nil {
911 return nil, nil, tracerr.Wrap(err)
912 }
913 receipt, err := c.client.client.TransactionReceipt(context.Background(), ethTxHash)
914 if err != nil {
915 return nil, nil, tracerr.Wrap(err)
916 }
917 sender, err := c.client.client.TransactionSender(context.Background(), tx, receipt.Logs[0].BlockHash, receipt.Logs[0].Index)
918 if err != nil {
919 return nil, nil, tracerr.Wrap(err)
920 }
921 var aux rollupForgeBatchArgsAux
922 if values, err := method.Inputs.Unpack(txData[4:]); err != nil {
923 return nil, nil, tracerr.Wrap(err)
924 } else if err := method.Inputs.Copy(&aux, values); err != nil {
925 return nil, nil, tracerr.Wrap(err)
926 }
927 rollupForgeBatchArgs := RollupForgeBatchArgs{
928 L1Batch: aux.L1Batch,
929 NewExitRoot: aux.NewExitRoot,
930 NewLastIdx: aux.NewLastIdx.Int64(),
931 NewStRoot: aux.NewStRoot,
932 ProofA: aux.ProofA,
933 ProofB: aux.ProofB,
934 ProofC: aux.ProofC,
935 VerifierIdx: aux.VerifierIdx,
936 L1CoordinatorTxs: []common.L1Tx{},
937 L1CoordinatorTxsAuths: [][]byte{},
938 L2TxsData: []common.L2Tx{},
939 FeeIdxCoordinator: []common.Idx{},
940 }
941 nLevels := c.consts.Verifiers[rollupForgeBatchArgs.VerifierIdx].NLevels
942 lenL1L2TxsBytes := int((nLevels/8)*2 + 2 + 1)
943 numBytesL1TxUser := int(l1UserTxsLen) * lenL1L2TxsBytes
944 numTxsL1Coord := len(aux.EncodedL1CoordinatorTx) / common.L1CoordinatorTxBytesLen
945 numBytesL1TxCoord := numTxsL1Coord * lenL1L2TxsBytes
946 numBeginL2Tx := numBytesL1TxCoord + numBytesL1TxUser
947 l1UserTxsData := []byte{}
948 if l1UserTxsLen > 0 {
949 l1UserTxsData = aux.L1L2TxsData[:numBytesL1TxUser]
950 }
951 for i := 0; i < int(l1UserTxsLen); i++ {
952 l1Tx, err := common.L1TxFromDataAvailability(l1UserTxsData[i*lenL1L2TxsBytes:(i+1)*lenL1L2TxsBytes], uint32(nLevels))
953 if err != nil {
954 return nil, nil, tracerr.Wrap(err)
955 }
956 rollupForgeBatchArgs.L1UserTxs = append(rollupForgeBatchArgs.L1UserTxs, *l1Tx)
957 }
958 l2TxsData := []byte{}
959 if numBeginL2Tx < len(aux.L1L2TxsData) {
960 l2TxsData = aux.L1L2TxsData[numBeginL2Tx:]
961 }
962 numTxsL2 := len(l2TxsData) / lenL1L2TxsBytes
963 for i := 0; i < numTxsL2; i++ {
964 l2Tx, err := common.L2TxFromBytesDataAvailability(l2TxsData[i*lenL1L2TxsBytes:(i+1)*lenL1L2TxsBytes], int(nLevels))
965 if err != nil {
966 return nil, nil, tracerr.Wrap(err)
967 }
968 rollupForgeBatchArgs.L2TxsData = append(rollupForgeBatchArgs.L2TxsData, *l2Tx)
969 }
970 for i := 0; i < numTxsL1Coord; i++ {
971 bytesL1Coordinator := aux.EncodedL1CoordinatorTx[i*common.L1CoordinatorTxBytesLen : (i+1)*common.L1CoordinatorTxBytesLen]
972 var signature []byte
973 v := bytesL1Coordinator[0]
974 s := bytesL1Coordinator[1:33]
975 r := bytesL1Coordinator[33:65]
976 signature = append(signature, r[:]...)
977 signature = append(signature, s[:]...)
978 signature = append(signature, v)
979 l1Tx, err := common.L1CoordinatorTxFromBytes(bytesL1Coordinator, c.chainID, c.address)
980 if err != nil {
981 return nil, nil, tracerr.Wrap(err)
982 }
983 rollupForgeBatchArgs.L1CoordinatorTxs = append(rollupForgeBatchArgs.L1CoordinatorTxs, *l1Tx)
984 rollupForgeBatchArgs.L1CoordinatorTxsAuths = append(rollupForgeBatchArgs.L1CoordinatorTxsAuths, signature)
985 }
986 lenFeeIdxCoordinatorBytes := int(nLevels / 8) //nolint:gomnd
987 numFeeIdxCoordinator := len(aux.FeeIdxCoordinator) / lenFeeIdxCoordinatorBytes
988 for i := 0; i < numFeeIdxCoordinator; i++ {
989 var paddedFeeIdx [6]byte
990 // TODO: This check is not necessary: the first case will always work. Test it before removing the if.
991 if lenFeeIdxCoordinatorBytes < common.IdxBytesLen {
992 copy(paddedFeeIdx[6-lenFeeIdxCoordinatorBytes:], aux.FeeIdxCoordinator[i*lenFeeIdxCoordinatorBytes:(i+1)*lenFeeIdxCoordinatorBytes])
993 } else {
994 copy(paddedFeeIdx[:], aux.FeeIdxCoordinator[i*lenFeeIdxCoordinatorBytes:(i+1)*lenFeeIdxCoordinatorBytes])
995 }
996 feeIdxCoordinator, err := common.IdxFromBytes(paddedFeeIdx[:])
997 if err != nil {
998 return nil, nil, tracerr.Wrap(err)
999 }
1000 if feeIdxCoordinator != common.Idx(0) {
1001 rollupForgeBatchArgs.FeeIdxCoordinator = append(rollupForgeBatchArgs.FeeIdxCoordinator, feeIdxCoordinator)
1002 }
1003 }
1004 return &rollupForgeBatchArgs, &sender, nil
1005}