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	"strings"
  8
  9	"github.com/ethereum/go-ethereum"
 10	"github.com/ethereum/go-ethereum/accounts/abi"
 11	"github.com/ethereum/go-ethereum/accounts/abi/bind"
 12	ethCommon "github.com/ethereum/go-ethereum/common"
 13	"github.com/ethereum/go-ethereum/core/types"
 14	"github.com/ethereum/go-ethereum/crypto"
 15	"github.com/ethereum/go-ethereum/ethclient"
 16	"github.com/hermeznetwork/hermez-node/common"
 17	Hermez "github.com/hermeznetwork/hermez-node/eth/contracts/hermez"
 18	HEZ "github.com/hermeznetwork/hermez-node/eth/contracts/tokenHEZ"
 19	"github.com/hermeznetwork/hermez-node/log"
 20	"github.com/iden3/go-iden3-crypto/babyjub"
 21)
 22
 23// QueueStruct is the queue of L1Txs for a batch
 24type QueueStruct struct {
 25	L1TxQueue    []common.L1Tx
 26	TotalL1TxFee *big.Int
 27}
 28
 29// NewQueueStruct creates a new clear QueueStruct.
 30func NewQueueStruct() *QueueStruct {
 31	return &QueueStruct{
 32		L1TxQueue:    make([]common.L1Tx, 0),
 33		TotalL1TxFee: big.NewInt(0),
 34	}
 35}
 36
 37// RollupState represents the state of the Rollup in the Smart Contract
 38type RollupState struct {
 39	StateRoot *big.Int
 40	ExitRoots []*big.Int
 41	// ExitNullifierMap       map[[256 / 8]byte]bool
 42	ExitNullifierMap       map[int64]map[int64]bool // batchNum -> idx -> bool
 43	TokenList              []ethCommon.Address
 44	TokenMap               map[ethCommon.Address]bool
 45	MapL1TxQueue           map[int64]*QueueStruct
 46	LastL1L2Batch          int64
 47	CurrentToForgeL1TxsNum int64
 48	LastToForgeL1TxsNum    int64
 49	CurrentIdx             int64
 50}
 51
 52// RollupEventL1UserTx is an event of the Rollup Smart Contract
 53type RollupEventL1UserTx struct {
 54	// ToForgeL1TxsNum int64 // QueueIndex       *big.Int
 55	// Position        int   // TransactionIndex *big.Int
 56	L1UserTx common.L1Tx
 57}
 58
 59// RollupEventL1UserTxAux is an event of the Rollup Smart Contract
 60type RollupEventL1UserTxAux struct {
 61	ToForgeL1TxsNum uint64 // QueueIndex       *big.Int
 62	Position        uint8  // TransactionIndex *big.Int
 63	L1UserTx        []byte
 64}
 65
 66// RollupEventAddToken is an event of the Rollup Smart Contract
 67type RollupEventAddToken struct {
 68	TokenAddress ethCommon.Address
 69	TokenID      uint32
 70}
 71
 72// RollupEventForgeBatch is an event of the Rollup Smart Contract
 73type RollupEventForgeBatch struct {
 74	BatchNum int64
 75	// Sender    ethCommon.Address
 76	EthTxHash ethCommon.Hash
 77}
 78
 79// RollupEventUpdateForgeL1L2BatchTimeout is an event of the Rollup Smart Contract
 80type RollupEventUpdateForgeL1L2BatchTimeout struct {
 81	NewForgeL1L2BatchTimeout int64
 82}
 83
 84// RollupEventUpdateFeeAddToken is an event of the Rollup Smart Contract
 85type RollupEventUpdateFeeAddToken struct {
 86	NewFeeAddToken *big.Int
 87}
 88
 89// RollupEventWithdraw is an event of the Rollup Smart Contract
 90type RollupEventWithdraw struct {
 91	Idx             uint64
 92	NumExitRoot     uint64
 93	InstantWithdraw bool
 94	TxHash          ethCommon.Hash // Hash of the transaction that generated this event
 95}
 96
 97// RollupEvents is the list of events in a block of the Rollup Smart Contract
 98type RollupEvents struct {
 99	L1UserTx                    []RollupEventL1UserTx
100	AddToken                    []RollupEventAddToken
101	ForgeBatch                  []RollupEventForgeBatch
102	UpdateForgeL1L2BatchTimeout []RollupEventUpdateForgeL1L2BatchTimeout
103	UpdateFeeAddToken           []RollupEventUpdateFeeAddToken
104	Withdraw                    []RollupEventWithdraw
105}
106
107// NewRollupEvents creates an empty RollupEvents with the slices initialized.
108func NewRollupEvents() RollupEvents {
109	return RollupEvents{
110		L1UserTx:                    make([]RollupEventL1UserTx, 0),
111		AddToken:                    make([]RollupEventAddToken, 0),
112		ForgeBatch:                  make([]RollupEventForgeBatch, 0),
113		UpdateForgeL1L2BatchTimeout: make([]RollupEventUpdateForgeL1L2BatchTimeout, 0),
114		UpdateFeeAddToken:           make([]RollupEventUpdateFeeAddToken, 0),
115		Withdraw:                    make([]RollupEventWithdraw, 0),
116	}
117}
118
119// RollupForgeBatchArgs are the arguments to the ForgeBatch function in the Rollup Smart Contract
120type RollupForgeBatchArgs struct {
121	NewLastIdx            int64
122	NewStRoot             *big.Int
123	NewExitRoot           *big.Int
124	L1CoordinatorTxs      []common.L1Tx
125	L1CoordinatorTxsAuths [][]byte // Authorization for accountCreations for each L1CoordinatorTx
126	L2TxsData             []common.L2Tx
127	FeeIdxCoordinator     []common.Idx
128	// Circuit selector
129	VerifierIdx uint8
130	L1Batch     bool
131	ProofA      [2]*big.Int
132	ProofB      [2][2]*big.Int
133	ProofC      [2]*big.Int
134}
135
136// RollupForgeBatchArgsAux are the arguments to the ForgeBatch function in the Rollup Smart Contract
137type RollupForgeBatchArgsAux struct {
138	NewLastIdx             *big.Int
139	NewStRoot              *big.Int
140	NewExitRoot            *big.Int
141	EncodedL1CoordinatorTx []byte
142	L2TxsData              []byte
143	FeeIdxCoordinator      []byte
144	// Circuit selector
145	VerifierIdx uint8
146	L1Batch     bool
147	ProofA      [2]*big.Int
148	ProofB      [2][2]*big.Int
149	ProofC      [2]*big.Int
150}
151
152// RollupInterface is the inteface to to Rollup Smart Contract
153type RollupInterface interface {
154	//
155	// Smart Contract Methods
156	//
157
158	// Public Functions
159
160	RollupForgeBatch(*RollupForgeBatchArgs) (*types.Transaction, error)
161	RollupAddToken(tokenAddress ethCommon.Address, feeAddToken, deadline *big.Int) (*types.Transaction, error)
162
163	RollupWithdrawMerkleProof(babyPubKey *babyjub.PublicKey, tokenID uint32, numExitRoot, idx int64, amount *big.Int, siblings []*big.Int, instantWithdraw bool) (*types.Transaction, error)
164	RollupWithdrawCircuit(proofA, proofC [2]*big.Int, proofB [2][2]*big.Int, tokenID uint32, numExitRoot, idx int64, amount *big.Int, instantWithdraw bool) (*types.Transaction, error)
165
166	RollupL1UserTxERC20ETH(fromBJJ *babyjub.PublicKey, fromIdx int64, loadAmount *big.Int, amount *big.Int, tokenID uint32, toIdx int64) (*types.Transaction, error)
167	RollupL1UserTxERC20Permit(fromBJJ *babyjub.PublicKey, fromIdx int64, loadAmount *big.Int, amount *big.Int, tokenID uint32, toIdx int64, deadline *big.Int) (tx *types.Transaction, err error)
168
169	// Governance Public Functions
170	RollupUpdateForgeL1L2BatchTimeout(newForgeL1L2BatchTimeout int64) (*types.Transaction, error)
171	RollupUpdateFeeAddToken(newFeeAddToken *big.Int) (*types.Transaction, error)
172
173	// Viewers
174	RollupRegisterTokensCount() (*big.Int, error)
175	RollupLastForgedBatch() (int64, error)
176
177	//
178	// Smart Contract Status
179	//
180
181	RollupConstants() (*common.RollupConstants, error)
182	RollupEventsByBlock(blockNum int64) (*RollupEvents, *ethCommon.Hash, error)
183	RollupForgeBatchArgs(ethCommon.Hash) (*RollupForgeBatchArgs, *ethCommon.Address, error)
184}
185
186//
187// Implementation
188//
189
190// RollupClient is the implementation of the interface to the Rollup Smart Contract in ethereum.
191type RollupClient struct {
192	client      *EthereumClient
193	chainID     *big.Int
194	address     ethCommon.Address
195	tokenHEZCfg TokenConfig
196	hermez      *Hermez.Hermez
197	tokenHEZ    *HEZ.HEZ
198	contractAbi abi.ABI
199}
200
201// NewRollupClient creates a new RollupClient
202func NewRollupClient(client *EthereumClient, address ethCommon.Address, tokenHEZCfg TokenConfig) (*RollupClient, error) {
203	contractAbi, err := abi.JSON(strings.NewReader(string(Hermez.HermezABI)))
204	if err != nil {
205		return nil, err
206	}
207	hermez, err := Hermez.NewHermez(address, client.Client())
208	if err != nil {
209		return nil, err
210	}
211	tokenHEZ, err := HEZ.NewHEZ(tokenHEZCfg.Address, client.Client())
212	if err != nil {
213		return nil, err
214	}
215	chainID, err := client.client.ChainID(context.Background())
216	if err != nil {
217		return nil, err
218	}
219	return &RollupClient{
220		client:      client,
221		chainID:     chainID,
222		address:     address,
223		tokenHEZCfg: tokenHEZCfg,
224		hermez:      hermez,
225		tokenHEZ:    tokenHEZ,
226		contractAbi: contractAbi,
227	}, nil
228}
229
230// RollupForgeBatch is the interface to call the smart contract function
231func (c *RollupClient) RollupForgeBatch(args *RollupForgeBatchArgs) (tx *types.Transaction, err error) {
232	if tx, err = c.client.CallAuth(
233		1000000, //nolint:gomnd
234		func(ec *ethclient.Client, auth *bind.TransactOpts) (*types.Transaction, error) {
235			rollupConst, err := c.RollupConstants()
236			if err != nil {
237				return nil, err
238			}
239			nLevels := rollupConst.Verifiers[args.VerifierIdx].NLevels
240			lenBytes := nLevels / 8 //nolint:gomnd
241			newLastIdx := big.NewInt(int64(args.NewLastIdx))
242			var l1CoordinatorBytes []byte
243			for i := 0; i < len(args.L1CoordinatorTxs); i++ {
244				l1 := args.L1CoordinatorTxs[i]
245				bytesl1, err := l1.BytesCoordinatorTx(args.L1CoordinatorTxsAuths[i])
246				if err != nil {
247					return nil, err
248				}
249				l1CoordinatorBytes = append(l1CoordinatorBytes, bytesl1[:]...)
250			}
251			var l2DataBytes []byte
252			for i := 0; i < len(args.L2TxsData); i++ {
253				l2 := args.L2TxsData[i]
254				bytesl2, err := l2.Bytes(uint32(nLevels))
255				if err != nil {
256					return nil, err
257				}
258				l2DataBytes = append(l2DataBytes, bytesl2[:]...)
259			}
260			var feeIdxCoordinator []byte
261			if len(args.FeeIdxCoordinator) > common.RollupConstMaxFeeIdxCoordinator {
262				return nil, fmt.Errorf("len(args.FeeIdxCoordinator) > %v",
263					common.RollupConstMaxFeeIdxCoordinator)
264			}
265			for i := 0; i < common.RollupConstMaxFeeIdxCoordinator; i++ {
266				feeIdx := common.Idx(0)
267				if i < len(args.FeeIdxCoordinator) {
268					feeIdx = args.FeeIdxCoordinator[i]
269				}
270				bytesFeeIdx, err := feeIdx.Bytes()
271				if err != nil {
272					return nil, err
273				}
274				feeIdxCoordinator = append(feeIdxCoordinator, bytesFeeIdx[len(bytesFeeIdx)-int(lenBytes):]...)
275			}
276			return c.hermez.ForgeBatch(auth, newLastIdx, args.NewStRoot, args.NewExitRoot, l1CoordinatorBytes, l2DataBytes, feeIdxCoordinator, args.VerifierIdx, args.L1Batch, args.ProofA, args.ProofB, args.ProofC)
277		},
278	); err != nil {
279		return nil, fmt.Errorf("Failed forge batch: %w", err)
280	}
281	return tx, nil
282}
283
284// RollupAddToken is the interface to call the smart contract function.
285// `feeAddToken` is the amount of HEZ tokens that will be paid to add the
286// token.  `feeAddToken` must match the public value of the smart contract.
287func (c *RollupClient) RollupAddToken(tokenAddress ethCommon.Address, feeAddToken, deadline *big.Int) (tx *types.Transaction, err error) {
288	if tx, err = c.client.CallAuth(
289		0,
290		func(ec *ethclient.Client, auth *bind.TransactOpts) (*types.Transaction, error) {
291			owner := c.client.account.Address
292			spender := c.address
293			nonce, err := c.tokenHEZ.Nonces(nil, owner)
294			if err != nil {
295				return nil, err
296			}
297			tokenName := c.tokenHEZCfg.Name
298			tokenAddr := c.tokenHEZCfg.Address
299			digest, _ := createPermitDigest(tokenAddr, owner, spender, c.chainID, feeAddToken, nonce, deadline, tokenName)
300			signature, _ := c.client.ks.SignHash(*c.client.account, digest)
301			permit := createPermit(owner, spender, feeAddToken, deadline, digest, signature)
302
303			return c.hermez.AddToken(auth, tokenAddress, permit)
304		},
305	); err != nil {
306		return nil, fmt.Errorf("Failed add Token %w", err)
307	}
308	return tx, nil
309}
310
311// RollupWithdrawMerkleProof is the interface to call the smart contract function
312func (c *RollupClient) RollupWithdrawMerkleProof(fromBJJ *babyjub.PublicKey, tokenID uint32, numExitRoot, idx int64, amount *big.Int, siblings []*big.Int, instantWithdraw bool) (tx *types.Transaction, err error) {
313	if tx, err = c.client.CallAuth(
314		0,
315		func(ec *ethclient.Client, auth *bind.TransactOpts) (*types.Transaction, error) {
316			pkCompL := fromBJJ.Compress()
317			pkCompB := common.SwapEndianness(pkCompL[:])
318			babyPubKey := new(big.Int).SetBytes(pkCompB)
319			numExitRootB := big.NewInt(numExitRoot)
320			idxBig := big.NewInt(idx)
321			return c.hermez.WithdrawMerkleProof(auth, tokenID, amount, babyPubKey, numExitRootB, siblings, idxBig, instantWithdraw)
322		},
323	); err != nil {
324		return nil, fmt.Errorf("Failed update WithdrawMerkleProof: %w", err)
325	}
326	return tx, nil
327}
328
329// RollupWithdrawCircuit is the interface to call the smart contract function
330func (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) {
331	log.Error("TODO")
332	return nil, errTODO
333}
334
335// RollupL1UserTxERC20ETH is the interface to call the smart contract function
336func (c *RollupClient) RollupL1UserTxERC20ETH(fromBJJ *babyjub.PublicKey, fromIdx int64, loadAmount *big.Int, amount *big.Int, tokenID uint32, toIdx int64) (tx *types.Transaction, err error) {
337	if tx, err = c.client.CallAuth(
338		0,
339		func(ec *ethclient.Client, auth *bind.TransactOpts) (*types.Transaction, error) {
340			pkCompL := fromBJJ.Compress()
341			pkCompB := common.SwapEndianness(pkCompL[:])
342			babyPubKey := new(big.Int).SetBytes(pkCompB)
343			fromIdxBig := big.NewInt(fromIdx)
344			toIdxBig := big.NewInt(toIdx)
345			loadAmountF, err := common.NewFloat16(loadAmount)
346			if err != nil {
347				return nil, err
348			}
349			amountF, err := common.NewFloat16(amount)
350			if err != nil {
351				return nil, err
352			}
353			if tokenID == 0 {
354				auth.Value = loadAmount
355			}
356			var permit []byte
357			return c.hermez.AddL1Transaction(auth, babyPubKey, fromIdxBig, uint16(loadAmountF),
358				uint16(amountF), tokenID, toIdxBig, permit)
359		},
360	); err != nil {
361		return nil, fmt.Errorf("Failed add L1 Tx ERC20/ETH: %w", err)
362	}
363	return tx, nil
364}
365
366// RollupL1UserTxERC20Permit is the interface to call the smart contract function
367func (c *RollupClient) RollupL1UserTxERC20Permit(fromBJJ *babyjub.PublicKey, fromIdx int64, loadAmount *big.Int, amount *big.Int, tokenID uint32, toIdx int64, deadline *big.Int) (tx *types.Transaction, err error) {
368	if tx, err = c.client.CallAuth(
369		0,
370		func(ec *ethclient.Client, auth *bind.TransactOpts) (*types.Transaction, error) {
371			pkCompL := fromBJJ.Compress()
372			pkCompB := common.SwapEndianness(pkCompL[:])
373			babyPubKey := new(big.Int).SetBytes(pkCompB)
374			fromIdxBig := big.NewInt(fromIdx)
375			toIdxBig := big.NewInt(toIdx)
376			loadAmountF, err := common.NewFloat16(loadAmount)
377			if err != nil {
378				return nil, err
379			}
380			amountF, err := common.NewFloat16(amount)
381			if err != nil {
382				return nil, err
383			}
384			if tokenID == 0 {
385				auth.Value = loadAmount
386			}
387			owner := c.client.account.Address
388			spender := c.address
389			nonce, err := c.tokenHEZ.Nonces(nil, owner)
390			if err != nil {
391				return nil, err
392			}
393			tokenName := c.tokenHEZCfg.Name
394			tokenAddr := c.tokenHEZCfg.Address
395			digest, _ := createPermitDigest(tokenAddr, owner, spender, c.chainID, amount, nonce, deadline, tokenName)
396			signature, _ := c.client.ks.SignHash(*c.client.account, digest)
397			permit := createPermit(owner, spender, amount, deadline, digest, signature)
398			return c.hermez.AddL1Transaction(auth, babyPubKey, fromIdxBig, uint16(loadAmountF),
399				uint16(amountF), tokenID, toIdxBig, permit)
400		},
401	); err != nil {
402		return nil, fmt.Errorf("Failed add L1 Tx ERC20Permit: %w", err)
403	}
404	return tx, nil
405}
406
407// RollupRegisterTokensCount is the interface to call the smart contract function
408func (c *RollupClient) RollupRegisterTokensCount() (registerTokensCount *big.Int, err error) {
409	if err := c.client.Call(func(ec *ethclient.Client) error {
410		registerTokensCount, err = c.hermez.RegisterTokensCount(nil)
411		return err
412	}); err != nil {
413		return nil, err
414	}
415	return registerTokensCount, nil
416}
417
418// RollupLastForgedBatch is the interface to call the smart contract function
419func (c *RollupClient) RollupLastForgedBatch() (lastForgedBatch int64, err error) {
420	if err := c.client.Call(func(ec *ethclient.Client) error {
421		_lastForgedBatch, err := c.hermez.LastForgedBatch(nil)
422		lastForgedBatch = int64(_lastForgedBatch)
423		return err
424	}); err != nil {
425		return 0, err
426	}
427	return lastForgedBatch, nil
428}
429
430// RollupUpdateForgeL1L2BatchTimeout is the interface to call the smart contract function
431func (c *RollupClient) RollupUpdateForgeL1L2BatchTimeout(newForgeL1L2BatchTimeout int64) (tx *types.Transaction, err error) {
432	if tx, err = c.client.CallAuth(
433		0,
434		func(ec *ethclient.Client, auth *bind.TransactOpts) (*types.Transaction, error) {
435			return c.hermez.UpdateForgeL1L2BatchTimeout(auth, uint8(newForgeL1L2BatchTimeout))
436		},
437	); err != nil {
438		return nil, fmt.Errorf("Failed update ForgeL1L2BatchTimeout: %w", err)
439	}
440	return tx, nil
441}
442
443// RollupUpdateFeeAddToken is the interface to call the smart contract function
444func (c *RollupClient) RollupUpdateFeeAddToken(newFeeAddToken *big.Int) (tx *types.Transaction, err error) {
445	if tx, err = c.client.CallAuth(
446		0,
447		func(ec *ethclient.Client, auth *bind.TransactOpts) (*types.Transaction, error) {
448			return c.hermez.UpdateFeeAddToken(auth, newFeeAddToken)
449		},
450	); err != nil {
451		return nil, fmt.Errorf("Failed update FeeAddToken: %w", err)
452	}
453	return tx, nil
454}
455
456// RollupConstants returns the Constants of the Rollup Smart Contract
457func (c *RollupClient) RollupConstants() (rollupConstants *common.RollupConstants, err error) {
458	rollupConstants = new(common.RollupConstants)
459	if err := c.client.Call(func(ec *ethclient.Client) error {
460		absoluteMaxL1L2BatchTimeout, err := c.hermez.ABSOLUTEMAXL1L2BATCHTIMEOUT(nil)
461		if err != nil {
462			return err
463		}
464		rollupConstants.AbsoluteMaxL1L2BatchTimeout = int64(absoluteMaxL1L2BatchTimeout)
465		rollupConstants.TokenHEZ, err = c.hermez.TokenHEZ(nil)
466		if err != nil {
467			return err
468		}
469		for i := int64(0); i < int64(common.LenVerifiers); i++ {
470			var newRollupVerifier common.RollupVerifierStruct
471			rollupVerifier, err := c.hermez.RollupVerifiers(nil, big.NewInt(i))
472			if err != nil {
473				return err
474			}
475			newRollupVerifier.MaxTx = rollupVerifier.MaxTx.Int64()
476			newRollupVerifier.NLevels = rollupVerifier.NLevels.Int64()
477			rollupConstants.Verifiers = append(rollupConstants.Verifiers, newRollupVerifier)
478		}
479		rollupConstants.HermezAuctionContract, err = c.hermez.HermezAuctionContract(nil)
480		if err != nil {
481			return err
482		}
483		rollupConstants.HermezGovernanceDAOAddress, err = c.hermez.HermezGovernanceDAOAddress(nil)
484		if err != nil {
485			return err
486		}
487		rollupConstants.SafetyAddress, err = c.hermez.SafetyAddress(nil)
488		if err != nil {
489			return err
490		}
491		rollupConstants.WithdrawDelayerContract, err = c.hermez.WithdrawDelayerContract(nil)
492		return err
493	}); err != nil {
494		return nil, err
495	}
496	return rollupConstants, nil
497}
498
499var (
500	logHermezL1UserTxEvent               = crypto.Keccak256Hash([]byte("L1UserTxEvent(uint64,uint8,bytes)"))
501	logHermezAddToken                    = crypto.Keccak256Hash([]byte("AddToken(address,uint32)"))
502	logHermezForgeBatch                  = crypto.Keccak256Hash([]byte("ForgeBatch(uint64)"))
503	logHermezUpdateForgeL1L2BatchTimeout = crypto.Keccak256Hash([]byte("UpdateForgeL1L2BatchTimeout(uint8)"))
504	logHermezUpdateFeeAddToken           = crypto.Keccak256Hash([]byte("UpdateFeeAddToken(uint256)"))
505	logHermezWithdrawEvent               = crypto.Keccak256Hash([]byte("WithdrawEvent(uint48,uint48,bool)"))
506)
507
508// RollupEventsByBlock returns the events in a block that happened in the Rollup Smart Contract
509func (c *RollupClient) RollupEventsByBlock(blockNum int64) (*RollupEvents, *ethCommon.Hash, error) {
510	var rollupEvents RollupEvents
511	var blockHash *ethCommon.Hash
512
513	query := ethereum.FilterQuery{
514		FromBlock: big.NewInt(blockNum),
515		ToBlock:   big.NewInt(blockNum),
516		Addresses: []ethCommon.Address{
517			c.address,
518		},
519		BlockHash: nil,
520		Topics:    [][]ethCommon.Hash{},
521	}
522	logs, err := c.client.client.FilterLogs(context.Background(), query)
523	if err != nil {
524		return nil, nil, err
525	}
526	if len(logs) > 0 {
527		blockHash = &logs[0].BlockHash
528	}
529	for _, vLog := range logs {
530		if vLog.BlockHash != *blockHash {
531			log.Errorw("Block hash mismatch", "expected", blockHash.String(), "got", vLog.BlockHash.String())
532			return nil, nil, ErrBlockHashMismatchEvent
533		}
534		switch vLog.Topics[0] {
535		case logHermezL1UserTxEvent:
536			var L1UserTxAux RollupEventL1UserTxAux
537			var L1UserTx RollupEventL1UserTx
538			err := c.contractAbi.Unpack(&L1UserTxAux, "L1UserTxEvent", vLog.Data)
539			if err != nil {
540				return nil, nil, err
541			}
542			L1Tx, err := common.L1UserTxFromBytes(L1UserTxAux.L1UserTx)
543			if err != nil {
544				return nil, nil, err
545			}
546			toForgeL1TxsNum := new(big.Int).SetBytes(vLog.Topics[1][:]).Int64()
547			L1Tx.ToForgeL1TxsNum = &toForgeL1TxsNum
548			L1Tx.Position = int(new(big.Int).SetBytes(vLog.Topics[2][:]).Int64())
549			L1Tx.UserOrigin = true
550			L1UserTx.L1UserTx = *L1Tx
551			rollupEvents.L1UserTx = append(rollupEvents.L1UserTx, L1UserTx)
552		case logHermezAddToken:
553			var addToken RollupEventAddToken
554			err := c.contractAbi.Unpack(&addToken, "AddToken", vLog.Data)
555			if err != nil {
556				return nil, nil, err
557			}
558			addToken.TokenAddress = ethCommon.BytesToAddress(vLog.Topics[1].Bytes())
559			rollupEvents.AddToken = append(rollupEvents.AddToken, addToken)
560		case logHermezForgeBatch:
561			var forgeBatch RollupEventForgeBatch
562			forgeBatch.BatchNum = new(big.Int).SetBytes(vLog.Topics[1][:]).Int64()
563			forgeBatch.EthTxHash = vLog.TxHash
564			// forgeBatch.Sender = vLog.Address
565			rollupEvents.ForgeBatch = append(rollupEvents.ForgeBatch, forgeBatch)
566		case logHermezUpdateForgeL1L2BatchTimeout:
567			var updateForgeL1L2BatchTimeout struct {
568				NewForgeL1L2BatchTimeout uint8
569			}
570			err := c.contractAbi.Unpack(&updateForgeL1L2BatchTimeout, "UpdateForgeL1L2BatchTimeout", vLog.Data)
571			if err != nil {
572				return nil, nil, err
573			}
574			rollupEvents.UpdateForgeL1L2BatchTimeout = append(rollupEvents.UpdateForgeL1L2BatchTimeout,
575				RollupEventUpdateForgeL1L2BatchTimeout{
576					NewForgeL1L2BatchTimeout: int64(updateForgeL1L2BatchTimeout.NewForgeL1L2BatchTimeout),
577				})
578		case logHermezUpdateFeeAddToken:
579			var updateFeeAddToken RollupEventUpdateFeeAddToken
580			err := c.contractAbi.Unpack(&updateFeeAddToken, "UpdateFeeAddToken", vLog.Data)
581			if err != nil {
582				return nil, nil, err
583			}
584			rollupEvents.UpdateFeeAddToken = append(rollupEvents.UpdateFeeAddToken, updateFeeAddToken)
585		case logHermezWithdrawEvent:
586			var withdraw RollupEventWithdraw
587			withdraw.Idx = new(big.Int).SetBytes(vLog.Topics[1][:]).Uint64()
588			withdraw.NumExitRoot = new(big.Int).SetBytes(vLog.Topics[2][:]).Uint64()
589			instantWithdraw := new(big.Int).SetBytes(vLog.Topics[3][:]).Uint64()
590			if instantWithdraw == 1 {
591				withdraw.InstantWithdraw = true
592			}
593			withdraw.TxHash = vLog.TxHash
594			rollupEvents.Withdraw = append(rollupEvents.Withdraw, withdraw)
595		}
596	}
597	return &rollupEvents, blockHash, nil
598}
599
600// RollupForgeBatchArgs returns the arguments used in a ForgeBatch call in the
601// Rollup Smart Contract in the given transaction, and the sender address.
602func (c *RollupClient) RollupForgeBatchArgs(ethTxHash ethCommon.Hash) (*RollupForgeBatchArgs, *ethCommon.Address, error) {
603	tx, _, err := c.client.client.TransactionByHash(context.Background(), ethTxHash)
604	if err != nil {
605		return nil, nil, err
606	}
607	txData := tx.Data()
608
609	method, err := c.contractAbi.MethodById(txData[:4])
610	if err != nil {
611		return nil, nil, err
612	}
613	receipt, err := c.client.client.TransactionReceipt(context.Background(), ethTxHash)
614	if err != nil {
615		return nil, nil, err
616	}
617	sender, err := c.client.client.TransactionSender(context.Background(), tx, receipt.Logs[0].BlockHash, receipt.Logs[0].Index)
618	if err != nil {
619		return nil, nil, err
620	}
621	var aux RollupForgeBatchArgsAux
622	if err := method.Inputs.Unpack(&aux, txData[4:]); err != nil {
623		return nil, nil, err
624	}
625	rollupForgeBatchArgs := RollupForgeBatchArgs{
626		L1Batch:               aux.L1Batch,
627		NewExitRoot:           aux.NewExitRoot,
628		NewLastIdx:            aux.NewLastIdx.Int64(),
629		NewStRoot:             aux.NewStRoot,
630		ProofA:                aux.ProofA,
631		ProofB:                aux.ProofB,
632		ProofC:                aux.ProofC,
633		VerifierIdx:           aux.VerifierIdx,
634		L1CoordinatorTxs:      []common.L1Tx{},
635		L1CoordinatorTxsAuths: [][]byte{},
636		L2TxsData:             []common.L2Tx{},
637		FeeIdxCoordinator:     []common.Idx{},
638	}
639	numTxsL1 := len(aux.EncodedL1CoordinatorTx) / common.L1CoordinatorTxBytesLen
640	for i := 0; i < numTxsL1; i++ {
641		bytesL1Coordinator := aux.EncodedL1CoordinatorTx[i*common.L1CoordinatorTxBytesLen : (i+1)*common.L1CoordinatorTxBytesLen]
642		var signature []byte
643		v := bytesL1Coordinator[0]
644		s := bytesL1Coordinator[1:33]
645		r := bytesL1Coordinator[33:65]
646		signature = append(signature, r[:]...)
647		signature = append(signature, s[:]...)
648		signature = append(signature, v)
649		l1Tx, err := common.L1CoordinatorTxFromBytes(bytesL1Coordinator, c.chainID, c.address)
650		if err != nil {
651			return nil, nil, err
652		}
653		rollupForgeBatchArgs.L1CoordinatorTxs = append(rollupForgeBatchArgs.L1CoordinatorTxs, *l1Tx)
654		rollupForgeBatchArgs.L1CoordinatorTxsAuths = append(rollupForgeBatchArgs.L1CoordinatorTxsAuths, signature)
655	}
656	rollupConsts, err := c.RollupConstants()
657	if err != nil {
658		return nil, nil, err
659	}
660	nLevels := rollupConsts.Verifiers[rollupForgeBatchArgs.VerifierIdx].NLevels
661	lenL2TxsBytes := int((nLevels/8)*2 + 2 + 1)
662	numTxsL2 := len(aux.L2TxsData) / lenL2TxsBytes
663	for i := 0; i < numTxsL2; i++ {
664		l2Tx, err := common.L2TxFromBytes(aux.L2TxsData[i*lenL2TxsBytes:(i+1)*lenL2TxsBytes], int(nLevels))
665		if err != nil {
666			return nil, nil, err
667		}
668		rollupForgeBatchArgs.L2TxsData = append(rollupForgeBatchArgs.L2TxsData, *l2Tx)
669	}
670	lenFeeIdxCoordinatorBytes := int(nLevels / 8) //nolint:gomnd
671	numFeeIdxCoordinator := len(aux.FeeIdxCoordinator) / lenFeeIdxCoordinatorBytes
672	for i := 0; i < numFeeIdxCoordinator; i++ {
673		var paddedFeeIdx [6]byte
674		// TODO: This check is not necessary: the first case will always work.  Test it before removing the if.
675		if lenFeeIdxCoordinatorBytes < common.IdxBytesLen {
676			copy(paddedFeeIdx[6-lenFeeIdxCoordinatorBytes:], aux.FeeIdxCoordinator[i*lenFeeIdxCoordinatorBytes:(i+1)*lenFeeIdxCoordinatorBytes])
677		} else {
678			copy(paddedFeeIdx[:], aux.FeeIdxCoordinator[i*lenFeeIdxCoordinatorBytes:(i+1)*lenFeeIdxCoordinatorBytes])
679		}
680		feeIdxCoordinator, err := common.IdxFromBytes(paddedFeeIdx[:])
681		if err != nil {
682			return nil, nil, err
683		}
684		if feeIdxCoordinator != common.Idx(0) {
685			rollupForgeBatchArgs.FeeIdxCoordinator = append(rollupForgeBatchArgs.FeeIdxCoordinator, feeIdxCoordinator)
686		}
687	}
688	return &rollupForgeBatchArgs, &sender, nil
689}