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