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) (*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}