arnaucube
arnaucube/hermez-nodePublic Self-hosted repository
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) (*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	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		for i := int64(0); i < int64(common.LenVerifiers); i++ {
666			var newRollupVerifier common.RollupVerifierStruct
667			rollupVerifier, err := c.hermez.RollupVerifiers(c.opts, big.NewInt(i))
668			if err != nil {
669				return tracerr.Wrap(err)
670			}
671			newRollupVerifier.MaxTx = rollupVerifier.MaxTx.Int64()
672			newRollupVerifier.NLevels = rollupVerifier.NLevels.Int64()
673			rollupConstants.Verifiers = append(rollupConstants.Verifiers, newRollupVerifier)
674		}
675		rollupConstants.HermezAuctionContract, err = c.hermez.HermezAuctionContract(c.opts)
676		if err != nil {
677			return tracerr.Wrap(err)
678		}
679		rollupConstants.HermezGovernanceAddress, err = c.hermez.HermezGovernanceAddress(c.opts)
680		if err != nil {
681			return tracerr.Wrap(err)
682		}
683		rollupConstants.WithdrawDelayerContract, err = c.hermez.WithdrawDelayerContract(c.opts)
684		return tracerr.Wrap(err)
685	}); err != nil {
686		return nil, tracerr.Wrap(err)
687	}
688	return rollupConstants, nil
689}
690
691var (
692	logHermezL1UserTxEvent               = crypto.Keccak256Hash([]byte("L1UserTxEvent(uint32,uint8,bytes)"))
693	logHermezAddToken                    = crypto.Keccak256Hash([]byte("AddToken(address,uint32)"))
694	logHermezForgeBatch                  = crypto.Keccak256Hash([]byte("ForgeBatch(uint32,uint16)"))
695	logHermezUpdateForgeL1L2BatchTimeout = crypto.Keccak256Hash([]byte("UpdateForgeL1L2BatchTimeout(uint8)"))
696	logHermezUpdateFeeAddToken           = crypto.Keccak256Hash([]byte("UpdateFeeAddToken(uint256)"))
697	logHermezWithdrawEvent               = crypto.Keccak256Hash([]byte("WithdrawEvent(uint48,uint32,bool)"))
698	logHermezUpdateBucketWithdraw        = crypto.Keccak256Hash([]byte("UpdateBucketWithdraw(uint8,uint256,uint256)"))
699	logHermezUpdateWithdrawalDelay       = crypto.Keccak256Hash([]byte("UpdateWithdrawalDelay(uint64)"))
700	logHermezUpdateBucketsParameters     = crypto.Keccak256Hash([]byte("UpdateBucketsParameters(uint256[4][" +
701		strconv.Itoa(common.RollupConstNumBuckets) + "])"))
702	logHermezUpdateTokenExchange = crypto.Keccak256Hash([]byte("UpdateTokenExchange(address[],uint64[])"))
703	logHermezSafeMode            = crypto.Keccak256Hash([]byte("SafeMode()"))
704	logHermezInitialize          = crypto.Keccak256Hash([]byte("InitializeHermezEvent(uint8,uint256,uint64)"))
705)
706
707// RollupEventInit returns the initialize event with its corresponding block number
708func (c *RollupClient) RollupEventInit() (*RollupEventInitialize, int64, error) {
709	query := ethereum.FilterQuery{
710		Addresses: []ethCommon.Address{
711			c.address,
712		},
713		Topics: [][]ethCommon.Hash{{logHermezInitialize}},
714	}
715	logs, err := c.client.client.FilterLogs(context.Background(), query)
716	if err != nil {
717		return nil, 0, tracerr.Wrap(err)
718	}
719	if len(logs) != 1 {
720		return nil, 0, tracerr.Wrap(fmt.Errorf("no event of type InitializeHermezEvent found"))
721	}
722	vLog := logs[0]
723	if vLog.Topics[0] != logHermezInitialize {
724		return nil, 0, tracerr.Wrap(fmt.Errorf("event is not InitializeHermezEvent"))
725	}
726
727	var rollupInit RollupEventInitialize
728	if err := c.contractAbi.UnpackIntoInterface(&rollupInit, "InitializeHermezEvent", vLog.Data); err != nil {
729		return nil, 0, tracerr.Wrap(err)
730	}
731	return &rollupInit, int64(vLog.BlockNumber), tracerr.Wrap(err)
732}
733
734// RollupEventsByBlock returns the events in a block that happened in the Rollup Smart Contract
735func (c *RollupClient) RollupEventsByBlock(blockNum int64) (*RollupEvents, *ethCommon.Hash, error) {
736	var rollupEvents RollupEvents
737	var blockHash *ethCommon.Hash
738
739	query := ethereum.FilterQuery{
740		FromBlock: big.NewInt(blockNum),
741		ToBlock:   big.NewInt(blockNum),
742		Addresses: []ethCommon.Address{
743			c.address,
744		},
745		BlockHash: nil,
746		Topics:    [][]ethCommon.Hash{},
747	}
748	logs, err := c.client.client.FilterLogs(context.Background(), query)
749	if err != nil {
750		return nil, nil, tracerr.Wrap(err)
751	}
752	if len(logs) > 0 {
753		blockHash = &logs[0].BlockHash
754	}
755	for _, vLog := range logs {
756		if vLog.BlockHash != *blockHash {
757			log.Errorw("Block hash mismatch", "expected", blockHash.String(), "got", vLog.BlockHash.String())
758			return nil, nil, tracerr.Wrap(ErrBlockHashMismatchEvent)
759		}
760		switch vLog.Topics[0] {
761		case logHermezL1UserTxEvent:
762			var L1UserTxAux rollupEventL1UserTxAux
763			var L1UserTx RollupEventL1UserTx
764			err := c.contractAbi.UnpackIntoInterface(&L1UserTxAux, "L1UserTxEvent", vLog.Data)
765			if err != nil {
766				return nil, nil, tracerr.Wrap(err)
767			}
768			L1Tx, err := common.L1UserTxFromBytes(L1UserTxAux.L1UserTx)
769			if err != nil {
770				return nil, nil, tracerr.Wrap(err)
771			}
772			toForgeL1TxsNum := new(big.Int).SetBytes(vLog.Topics[1][:]).Int64()
773			L1Tx.ToForgeL1TxsNum = &toForgeL1TxsNum
774			L1Tx.Position = int(new(big.Int).SetBytes(vLog.Topics[2][:]).Int64())
775			L1Tx.UserOrigin = true
776			L1UserTx.L1UserTx = *L1Tx
777			rollupEvents.L1UserTx = append(rollupEvents.L1UserTx, L1UserTx)
778		case logHermezAddToken:
779			var addToken RollupEventAddToken
780			err := c.contractAbi.UnpackIntoInterface(&addToken, "AddToken", vLog.Data)
781			if err != nil {
782				return nil, nil, tracerr.Wrap(err)
783			}
784			addToken.TokenAddress = ethCommon.BytesToAddress(vLog.Topics[1].Bytes())
785			rollupEvents.AddToken = append(rollupEvents.AddToken, addToken)
786		case logHermezForgeBatch:
787			var forgeBatch RollupEventForgeBatch
788			err := c.contractAbi.UnpackIntoInterface(&forgeBatch, "ForgeBatch", vLog.Data)
789			if err != nil {
790				return nil, nil, tracerr.Wrap(err)
791			}
792			forgeBatch.BatchNum = new(big.Int).SetBytes(vLog.Topics[1][:]).Int64()
793			forgeBatch.EthTxHash = vLog.TxHash
794			// forgeBatch.Sender = vLog.Address
795			rollupEvents.ForgeBatch = append(rollupEvents.ForgeBatch, forgeBatch)
796		case logHermezUpdateForgeL1L2BatchTimeout:
797			var updateForgeL1L2BatchTimeout struct {
798				NewForgeL1L2BatchTimeout uint8
799			}
800			err := c.contractAbi.UnpackIntoInterface(&updateForgeL1L2BatchTimeout, "UpdateForgeL1L2BatchTimeout", vLog.Data)
801			if err != nil {
802				return nil, nil, tracerr.Wrap(err)
803			}
804			rollupEvents.UpdateForgeL1L2BatchTimeout = append(rollupEvents.UpdateForgeL1L2BatchTimeout,
805				RollupEventUpdateForgeL1L2BatchTimeout{
806					NewForgeL1L2BatchTimeout: int64(updateForgeL1L2BatchTimeout.NewForgeL1L2BatchTimeout),
807				})
808		case logHermezUpdateFeeAddToken:
809			var updateFeeAddToken RollupEventUpdateFeeAddToken
810			err := c.contractAbi.UnpackIntoInterface(&updateFeeAddToken, "UpdateFeeAddToken", vLog.Data)
811			if err != nil {
812				return nil, nil, tracerr.Wrap(err)
813			}
814			rollupEvents.UpdateFeeAddToken = append(rollupEvents.UpdateFeeAddToken, updateFeeAddToken)
815		case logHermezWithdrawEvent:
816			var withdraw RollupEventWithdraw
817			withdraw.Idx = new(big.Int).SetBytes(vLog.Topics[1][:]).Uint64()
818			withdraw.NumExitRoot = new(big.Int).SetBytes(vLog.Topics[2][:]).Uint64()
819			instantWithdraw := new(big.Int).SetBytes(vLog.Topics[3][:]).Uint64()
820			if instantWithdraw == 1 {
821				withdraw.InstantWithdraw = true
822			}
823			withdraw.TxHash = vLog.TxHash
824			rollupEvents.Withdraw = append(rollupEvents.Withdraw, withdraw)
825		case logHermezUpdateBucketWithdraw:
826			var updateBucketWithdrawAux rollupEventUpdateBucketWithdrawAux
827			var updateBucketWithdraw RollupEventUpdateBucketWithdraw
828			err := c.contractAbi.UnpackIntoInterface(&updateBucketWithdrawAux, "UpdateBucketWithdraw", vLog.Data)
829			if err != nil {
830				return nil, nil, tracerr.Wrap(err)
831			}
832			updateBucketWithdraw.Withdrawals = updateBucketWithdrawAux.Withdrawals
833			updateBucketWithdraw.NumBucket = int(new(big.Int).SetBytes(vLog.Topics[1][:]).Int64())
834			updateBucketWithdraw.BlockStamp = new(big.Int).SetBytes(vLog.Topics[2][:]).Int64()
835			rollupEvents.UpdateBucketWithdraw = append(rollupEvents.UpdateBucketWithdraw, updateBucketWithdraw)
836
837		case logHermezUpdateWithdrawalDelay:
838			var withdrawalDelay RollupEventUpdateWithdrawalDelay
839			err := c.contractAbi.UnpackIntoInterface(&withdrawalDelay, "UpdateWithdrawalDelay", vLog.Data)
840			if err != nil {
841				return nil, nil, tracerr.Wrap(err)
842			}
843			rollupEvents.UpdateWithdrawalDelay = append(rollupEvents.UpdateWithdrawalDelay, withdrawalDelay)
844		case logHermezUpdateBucketsParameters:
845			var bucketsParametersAux rollupEventUpdateBucketsParametersAux
846			var bucketsParameters RollupEventUpdateBucketsParameters
847			err := c.contractAbi.UnpackIntoInterface(&bucketsParametersAux, "UpdateBucketsParameters", vLog.Data)
848			if err != nil {
849				return nil, nil, tracerr.Wrap(err)
850			}
851			for i, bucket := range bucketsParametersAux.ArrayBuckets {
852				bucketsParameters.ArrayBuckets[i].CeilUSD = bucket[0]
853				bucketsParameters.ArrayBuckets[i].Withdrawals = bucket[1]
854				bucketsParameters.ArrayBuckets[i].BlockWithdrawalRate = bucket[2]
855				bucketsParameters.ArrayBuckets[i].MaxWithdrawals = bucket[3]
856			}
857			rollupEvents.UpdateBucketsParameters = append(rollupEvents.UpdateBucketsParameters, bucketsParameters)
858		case logHermezUpdateTokenExchange:
859			var tokensExchange RollupEventUpdateTokenExchange
860			err := c.contractAbi.UnpackIntoInterface(&tokensExchange, "UpdateTokenExchange", vLog.Data)
861			if err != nil {
862				return nil, nil, tracerr.Wrap(err)
863			}
864			rollupEvents.UpdateTokenExchange = append(rollupEvents.UpdateTokenExchange, tokensExchange)
865		case logHermezSafeMode:
866			var safeMode RollupEventSafeMode
867			rollupEvents.SafeMode = append(rollupEvents.SafeMode, safeMode)
868			// Also add an UpdateBucketsParameter with
869			// SafeMode=true to keep the order between `safeMode`
870			// and `UpdateBucketsParameters`
871			bucketsParameters := RollupEventUpdateBucketsParameters{
872				SafeMode: true,
873			}
874			for i := range bucketsParameters.ArrayBuckets {
875				bucketsParameters.ArrayBuckets[i].CeilUSD = big.NewInt(0)
876				bucketsParameters.ArrayBuckets[i].Withdrawals = big.NewInt(0)
877				bucketsParameters.ArrayBuckets[i].BlockWithdrawalRate = big.NewInt(0)
878				bucketsParameters.ArrayBuckets[i].MaxWithdrawals = big.NewInt(0)
879			}
880			rollupEvents.UpdateBucketsParameters = append(rollupEvents.UpdateBucketsParameters,
881				bucketsParameters)
882		}
883	}
884	return &rollupEvents, blockHash, nil
885}
886
887// RollupForgeBatchArgs returns the arguments used in a ForgeBatch call in the
888// Rollup Smart Contract in the given transaction, and the sender address.
889func (c *RollupClient) RollupForgeBatchArgs(ethTxHash ethCommon.Hash, l1UserTxsLen uint16) (*RollupForgeBatchArgs, *ethCommon.Address, error) {
890	tx, _, err := c.client.client.TransactionByHash(context.Background(), ethTxHash)
891	if err != nil {
892		return nil, nil, tracerr.Wrap(err)
893	}
894	txData := tx.Data()
895
896	method, err := c.contractAbi.MethodById(txData[:4])
897	if err != nil {
898		return nil, nil, tracerr.Wrap(err)
899	}
900	receipt, err := c.client.client.TransactionReceipt(context.Background(), ethTxHash)
901	if err != nil {
902		return nil, nil, tracerr.Wrap(err)
903	}
904	sender, err := c.client.client.TransactionSender(context.Background(), tx, receipt.Logs[0].BlockHash, receipt.Logs[0].Index)
905	if err != nil {
906		return nil, nil, tracerr.Wrap(err)
907	}
908	var aux rollupForgeBatchArgsAux
909	if values, err := method.Inputs.Unpack(txData[4:]); err != nil {
910		return nil, nil, tracerr.Wrap(err)
911	} else if err := method.Inputs.Copy(&aux, values); err != nil {
912		return nil, nil, tracerr.Wrap(err)
913	}
914	rollupForgeBatchArgs := RollupForgeBatchArgs{
915		L1Batch:               aux.L1Batch,
916		NewExitRoot:           aux.NewExitRoot,
917		NewLastIdx:            aux.NewLastIdx.Int64(),
918		NewStRoot:             aux.NewStRoot,
919		ProofA:                aux.ProofA,
920		ProofB:                aux.ProofB,
921		ProofC:                aux.ProofC,
922		VerifierIdx:           aux.VerifierIdx,
923		L1CoordinatorTxs:      []common.L1Tx{},
924		L1CoordinatorTxsAuths: [][]byte{},
925		L2TxsData:             []common.L2Tx{},
926		FeeIdxCoordinator:     []common.Idx{},
927	}
928	nLevels := c.consts.Verifiers[rollupForgeBatchArgs.VerifierIdx].NLevels
929	lenL1L2TxsBytes := int((nLevels/8)*2 + 2 + 1)
930	numBytesL1TxUser := int(l1UserTxsLen) * lenL1L2TxsBytes
931	numTxsL1Coord := len(aux.EncodedL1CoordinatorTx) / common.L1CoordinatorTxBytesLen
932	numBytesL1TxCoord := numTxsL1Coord * lenL1L2TxsBytes
933	numBeginL2Tx := numBytesL1TxCoord + numBytesL1TxUser
934	l1UserTxsData := []byte{}
935	if l1UserTxsLen > 0 {
936		l1UserTxsData = aux.L1L2TxsData[:numBytesL1TxUser]
937	}
938	for i := 0; i < int(l1UserTxsLen); i++ {
939		l1Tx, err := common.L1TxFromDataAvailability(l1UserTxsData[i*lenL1L2TxsBytes:(i+1)*lenL1L2TxsBytes], uint32(nLevels))
940		if err != nil {
941			return nil, nil, tracerr.Wrap(err)
942		}
943		rollupForgeBatchArgs.L1UserTxs = append(rollupForgeBatchArgs.L1UserTxs, *l1Tx)
944	}
945	l2TxsData := []byte{}
946	if numBeginL2Tx < len(aux.L1L2TxsData) {
947		l2TxsData = aux.L1L2TxsData[numBeginL2Tx:]
948	}
949	numTxsL2 := len(l2TxsData) / lenL1L2TxsBytes
950	for i := 0; i < numTxsL2; i++ {
951		l2Tx, err := common.L2TxFromBytesDataAvailability(l2TxsData[i*lenL1L2TxsBytes:(i+1)*lenL1L2TxsBytes], int(nLevels))
952		if err != nil {
953			return nil, nil, tracerr.Wrap(err)
954		}
955		rollupForgeBatchArgs.L2TxsData = append(rollupForgeBatchArgs.L2TxsData, *l2Tx)
956	}
957	for i := 0; i < numTxsL1Coord; i++ {
958		bytesL1Coordinator := aux.EncodedL1CoordinatorTx[i*common.L1CoordinatorTxBytesLen : (i+1)*common.L1CoordinatorTxBytesLen]
959		var signature []byte
960		v := bytesL1Coordinator[0]
961		s := bytesL1Coordinator[1:33]
962		r := bytesL1Coordinator[33:65]
963		signature = append(signature, r[:]...)
964		signature = append(signature, s[:]...)
965		signature = append(signature, v)
966		l1Tx, err := common.L1CoordinatorTxFromBytes(bytesL1Coordinator, c.chainID, c.address)
967		if err != nil {
968			return nil, nil, tracerr.Wrap(err)
969		}
970		rollupForgeBatchArgs.L1CoordinatorTxs = append(rollupForgeBatchArgs.L1CoordinatorTxs, *l1Tx)
971		rollupForgeBatchArgs.L1CoordinatorTxsAuths = append(rollupForgeBatchArgs.L1CoordinatorTxsAuths, signature)
972	}
973	lenFeeIdxCoordinatorBytes := int(nLevels / 8) //nolint:gomnd
974	numFeeIdxCoordinator := len(aux.FeeIdxCoordinator) / lenFeeIdxCoordinatorBytes
975	for i := 0; i < numFeeIdxCoordinator; i++ {
976		var paddedFeeIdx [6]byte
977		// TODO: This check is not necessary: the first case will always work.  Test it before removing the if.
978		if lenFeeIdxCoordinatorBytes < common.IdxBytesLen {
979			copy(paddedFeeIdx[6-lenFeeIdxCoordinatorBytes:], aux.FeeIdxCoordinator[i*lenFeeIdxCoordinatorBytes:(i+1)*lenFeeIdxCoordinatorBytes])
980		} else {
981			copy(paddedFeeIdx[:], aux.FeeIdxCoordinator[i*lenFeeIdxCoordinatorBytes:(i+1)*lenFeeIdxCoordinatorBytes])
982		}
983		feeIdxCoordinator, err := common.IdxFromBytes(paddedFeeIdx[:])
984		if err != nil {
985			return nil, nil, tracerr.Wrap(err)
986		}
987		if feeIdxCoordinator != common.Idx(0) {
988			rollupForgeBatchArgs.FeeIdxCoordinator = append(rollupForgeBatchArgs.FeeIdxCoordinator, feeIdxCoordinator)
989		}
990	}
991	return &rollupForgeBatchArgs, &sender, nil
992}