arnaucube
arnaucube/hermez-nodePublic Self-hosted repository
hermez-node/db/historydb/historydb.go
db/historydb/historydb.goRaw
   1package historydb
   2
   3import (
   4	"errors"
   5	"fmt"
   6	"math"
   7	"math/big"
   8
   9	ethCommon "github.com/ethereum/go-ethereum/common"
  10	"github.com/hermeznetwork/hermez-node/common"
  11	"github.com/hermeznetwork/hermez-node/db"
  12	"github.com/hermeznetwork/tracerr"
  13	"github.com/iden3/go-iden3-crypto/babyjub"
  14	"github.com/jmoiron/sqlx"
  15
  16	//nolint:errcheck // driver for postgres DB
  17	_ "github.com/lib/pq"
  18	"github.com/russross/meddler"
  19)
  20
  21const (
  22	// OrderAsc indicates ascending order when using pagination
  23	OrderAsc = "ASC"
  24	// OrderDesc indicates descending order when using pagination
  25	OrderDesc = "DESC"
  26)
  27
  28// TODO(Edu): Document here how HistoryDB is kept consistent
  29
  30// HistoryDB persist the historic of the rollup
  31type HistoryDB struct {
  32	db *sqlx.DB
  33}
  34
  35// NewHistoryDB initialize the DB
  36func NewHistoryDB(db *sqlx.DB) *HistoryDB {
  37	return &HistoryDB{db: db}
  38}
  39
  40// DB returns a pointer to the L2DB.db. This method should be used only for
  41// internal testing purposes.
  42func (hdb *HistoryDB) DB() *sqlx.DB {
  43	return hdb.db
  44}
  45
  46// AddBlock insert a block into the DB
  47func (hdb *HistoryDB) AddBlock(block *common.Block) error { return hdb.addBlock(hdb.db, block) }
  48func (hdb *HistoryDB) addBlock(d meddler.DB, block *common.Block) error {
  49	return tracerr.Wrap(meddler.Insert(d, "block", block))
  50}
  51
  52// AddBlocks inserts blocks into the DB
  53func (hdb *HistoryDB) AddBlocks(blocks []common.Block) error {
  54	return tracerr.Wrap(hdb.addBlocks(hdb.db, blocks))
  55}
  56
  57func (hdb *HistoryDB) addBlocks(d meddler.DB, blocks []common.Block) error {
  58	return tracerr.Wrap(db.BulkInsert(
  59		d,
  60		`INSERT INTO block (
  61			eth_block_num,
  62			timestamp,
  63			hash
  64		) VALUES %s;`,
  65		blocks[:],
  66	))
  67}
  68
  69// GetBlock retrieve a block from the DB, given a block number
  70func (hdb *HistoryDB) GetBlock(blockNum int64) (*common.Block, error) {
  71	block := &common.Block{}
  72	err := meddler.QueryRow(
  73		hdb.db, block,
  74		"SELECT * FROM block WHERE eth_block_num = $1;", blockNum,
  75	)
  76	return block, tracerr.Wrap(err)
  77}
  78
  79// GetAllBlocks retrieve all blocks from the DB
  80func (hdb *HistoryDB) GetAllBlocks() ([]common.Block, error) {
  81	var blocks []*common.Block
  82	err := meddler.QueryAll(
  83		hdb.db, &blocks,
  84		"SELECT * FROM block ORDER BY eth_block_num;",
  85	)
  86	return db.SlicePtrsToSlice(blocks).([]common.Block), tracerr.Wrap(err)
  87}
  88
  89// GetBlocks retrieve blocks from the DB, given a range of block numbers defined by from and to
  90func (hdb *HistoryDB) GetBlocks(from, to int64) ([]common.Block, error) {
  91	var blocks []*common.Block
  92	err := meddler.QueryAll(
  93		hdb.db, &blocks,
  94		"SELECT * FROM block WHERE $1 <= eth_block_num AND eth_block_num < $2 ORDER BY eth_block_num;",
  95		from, to,
  96	)
  97	return db.SlicePtrsToSlice(blocks).([]common.Block), tracerr.Wrap(err)
  98}
  99
 100// GetLastBlock retrieve the block with the highest block number from the DB
 101func (hdb *HistoryDB) GetLastBlock() (*common.Block, error) {
 102	block := &common.Block{}
 103	err := meddler.QueryRow(
 104		hdb.db, block, "SELECT * FROM block ORDER BY eth_block_num DESC LIMIT 1;",
 105	)
 106	return block, tracerr.Wrap(err)
 107}
 108
 109// AddBatch insert a Batch into the DB
 110func (hdb *HistoryDB) AddBatch(batch *common.Batch) error { return hdb.addBatch(hdb.db, batch) }
 111func (hdb *HistoryDB) addBatch(d meddler.DB, batch *common.Batch) error {
 112	// Calculate total collected fees in USD
 113	// Get IDs of collected tokens for fees
 114	tokenIDs := []common.TokenID{}
 115	for id := range batch.CollectedFees {
 116		tokenIDs = append(tokenIDs, id)
 117	}
 118	// Get USD value of the tokens
 119	type tokenPrice struct {
 120		ID       common.TokenID `meddler:"token_id"`
 121		USD      *float64       `meddler:"usd"`
 122		Decimals int            `meddler:"decimals"`
 123	}
 124	var tokenPrices []*tokenPrice
 125	if len(tokenIDs) > 0 {
 126		query, args, err := sqlx.In(
 127			"SELECT token_id, usd, decimals FROM token WHERE token_id IN (?);",
 128			tokenIDs,
 129		)
 130		if err != nil {
 131			return tracerr.Wrap(err)
 132		}
 133		query = hdb.db.Rebind(query)
 134		if err := meddler.QueryAll(
 135			hdb.db, &tokenPrices, query, args...,
 136		); err != nil {
 137			return tracerr.Wrap(err)
 138		}
 139	}
 140	// Calculate total collected
 141	var total float64
 142	for _, tokenPrice := range tokenPrices {
 143		if tokenPrice.USD == nil {
 144			continue
 145		}
 146		f := new(big.Float).SetInt(batch.CollectedFees[tokenPrice.ID])
 147		amount, _ := f.Float64()
 148		total += *tokenPrice.USD * (amount / math.Pow(10, float64(tokenPrice.Decimals))) //nolint decimals have to be ^10
 149	}
 150	batch.TotalFeesUSD = &total
 151	// Insert to DB
 152	return tracerr.Wrap(meddler.Insert(d, "batch", batch))
 153}
 154
 155// AddBatches insert Bids into the DB
 156func (hdb *HistoryDB) AddBatches(batches []common.Batch) error {
 157	return tracerr.Wrap(hdb.addBatches(hdb.db, batches))
 158}
 159func (hdb *HistoryDB) addBatches(d meddler.DB, batches []common.Batch) error {
 160	for i := 0; i < len(batches); i++ {
 161		if err := hdb.addBatch(d, &batches[i]); err != nil {
 162			return tracerr.Wrap(err)
 163		}
 164	}
 165	return nil
 166}
 167
 168// GetBatchAPI return the batch with the given batchNum
 169func (hdb *HistoryDB) GetBatchAPI(batchNum common.BatchNum) (*BatchAPI, error) {
 170	batch := &BatchAPI{}
 171	return batch, tracerr.Wrap(meddler.QueryRow(
 172		hdb.db, batch,
 173		`SELECT batch.*, block.timestamp, block.hash,
 174	    COALESCE ((SELECT COUNT(*) FROM tx WHERE batch_num = batch.batch_num), 0) AS forged_txs
 175	    FROM batch INNER JOIN block ON batch.eth_block_num = block.eth_block_num
 176	 	WHERE batch_num = $1;`, batchNum,
 177	))
 178}
 179
 180// GetBatchesAPI return the batches applying the given filters
 181func (hdb *HistoryDB) GetBatchesAPI(
 182	minBatchNum, maxBatchNum, slotNum *uint,
 183	forgerAddr *ethCommon.Address,
 184	fromItem, limit *uint, order string,
 185) ([]BatchAPI, uint64, error) {
 186	var query string
 187	var args []interface{}
 188	queryStr := `SELECT batch.*, block.timestamp, block.hash,
 189	COALESCE ((SELECT COUNT(*) FROM tx WHERE batch_num = batch.batch_num), 0) AS forged_txs,
 190	count(*) OVER() AS total_items
 191	FROM batch INNER JOIN block ON batch.eth_block_num = block.eth_block_num `
 192	// Apply filters
 193	nextIsAnd := false
 194	// minBatchNum filter
 195	if minBatchNum != nil {
 196		if nextIsAnd {
 197			queryStr += "AND "
 198		} else {
 199			queryStr += "WHERE "
 200		}
 201		queryStr += "batch.batch_num > ? "
 202		args = append(args, minBatchNum)
 203		nextIsAnd = true
 204	}
 205	// maxBatchNum filter
 206	if maxBatchNum != nil {
 207		if nextIsAnd {
 208			queryStr += "AND "
 209		} else {
 210			queryStr += "WHERE "
 211		}
 212		queryStr += "batch.batch_num < ? "
 213		args = append(args, maxBatchNum)
 214		nextIsAnd = true
 215	}
 216	// slotNum filter
 217	if slotNum != nil {
 218		if nextIsAnd {
 219			queryStr += "AND "
 220		} else {
 221			queryStr += "WHERE "
 222		}
 223		queryStr += "batch.slot_num = ? "
 224		args = append(args, slotNum)
 225		nextIsAnd = true
 226	}
 227	// forgerAddr filter
 228	if forgerAddr != nil {
 229		if nextIsAnd {
 230			queryStr += "AND "
 231		} else {
 232			queryStr += "WHERE "
 233		}
 234		queryStr += "batch.forger_addr = ? "
 235		args = append(args, forgerAddr)
 236		nextIsAnd = true
 237	}
 238	// pagination
 239	if fromItem != nil {
 240		if nextIsAnd {
 241			queryStr += "AND "
 242		} else {
 243			queryStr += "WHERE "
 244		}
 245		if order == OrderAsc {
 246			queryStr += "batch.item_id >= ? "
 247		} else {
 248			queryStr += "batch.item_id <= ? "
 249		}
 250		args = append(args, fromItem)
 251	}
 252	queryStr += "ORDER BY batch.item_id "
 253	if order == OrderAsc {
 254		queryStr += " ASC "
 255	} else {
 256		queryStr += " DESC "
 257	}
 258	queryStr += fmt.Sprintf("LIMIT %d;", *limit)
 259	query = hdb.db.Rebind(queryStr)
 260	// log.Debug(query)
 261	batchPtrs := []*BatchAPI{}
 262	if err := meddler.QueryAll(hdb.db, &batchPtrs, query, args...); err != nil {
 263		return nil, 0, tracerr.Wrap(err)
 264	}
 265	batches := db.SlicePtrsToSlice(batchPtrs).([]BatchAPI)
 266	if len(batches) == 0 {
 267		return batches, 0, nil
 268	}
 269	return batches, batches[0].TotalItems - uint64(len(batches)), nil
 270}
 271
 272// GetAllBatches retrieve all batches from the DB
 273func (hdb *HistoryDB) GetAllBatches() ([]common.Batch, error) {
 274	var batches []*common.Batch
 275	err := meddler.QueryAll(
 276		hdb.db, &batches,
 277		`SELECT batch.batch_num, batch.eth_block_num, batch.forger_addr, batch.fees_collected,
 278		 batch.fee_idxs_coordinator, batch.state_root, batch.num_accounts, batch.last_idx, batch.exit_root,
 279		 batch.forge_l1_txs_num, batch.slot_num, batch.total_fees_usd FROM batch
 280		 ORDER BY item_id;`,
 281	)
 282	return db.SlicePtrsToSlice(batches).([]common.Batch), tracerr.Wrap(err)
 283}
 284
 285// GetBatches retrieve batches from the DB, given a range of batch numbers defined by from and to
 286func (hdb *HistoryDB) GetBatches(from, to common.BatchNum) ([]common.Batch, error) {
 287	var batches []*common.Batch
 288	err := meddler.QueryAll(
 289		hdb.db, &batches,
 290		"SELECT * FROM batch WHERE $1 <= batch_num AND batch_num < $2 ORDER BY batch_num;",
 291		from, to,
 292	)
 293	return db.SlicePtrsToSlice(batches).([]common.Batch), tracerr.Wrap(err)
 294}
 295
 296// GetFirstBatchBlockNumBySlot returns the ethereum block number of the first
 297// batch within a slot
 298func (hdb *HistoryDB) GetFirstBatchBlockNumBySlot(slotNum int64) (int64, error) {
 299	row := hdb.db.QueryRow(
 300		`SELECT eth_block_num FROM batch
 301		WHERE slot_num = $1 ORDER BY batch_num ASC LIMIT 1;`, slotNum,
 302	)
 303	var blockNum int64
 304	return blockNum, tracerr.Wrap(row.Scan(&blockNum))
 305}
 306
 307// GetLastBatchNum returns the BatchNum of the latest forged batch
 308func (hdb *HistoryDB) GetLastBatchNum() (common.BatchNum, error) {
 309	row := hdb.db.QueryRow("SELECT batch_num FROM batch ORDER BY batch_num DESC LIMIT 1;")
 310	var batchNum common.BatchNum
 311	return batchNum, tracerr.Wrap(row.Scan(&batchNum))
 312}
 313
 314// GetLastL1BatchBlockNum returns the blockNum of the latest forged l1Batch
 315func (hdb *HistoryDB) GetLastL1BatchBlockNum() (int64, error) {
 316	row := hdb.db.QueryRow(`SELECT eth_block_num FROM batch
 317		WHERE forge_l1_txs_num IS NOT NULL
 318		ORDER BY batch_num DESC LIMIT 1;`)
 319	var blockNum int64
 320	return blockNum, tracerr.Wrap(row.Scan(&blockNum))
 321}
 322
 323// GetLastL1TxsNum returns the greatest ForgeL1TxsNum in the DB from forged
 324// batches.  If there's no batch in the DB (nil, nil) is returned.
 325func (hdb *HistoryDB) GetLastL1TxsNum() (*int64, error) {
 326	row := hdb.db.QueryRow("SELECT MAX(forge_l1_txs_num) FROM batch;")
 327	lastL1TxsNum := new(int64)
 328	return lastL1TxsNum, tracerr.Wrap(row.Scan(&lastL1TxsNum))
 329}
 330
 331// Reorg deletes all the information that was added into the DB after the
 332// lastValidBlock.  If lastValidBlock is negative, all block information is
 333// deleted.
 334func (hdb *HistoryDB) Reorg(lastValidBlock int64) error {
 335	var err error
 336	if lastValidBlock < 0 {
 337		_, err = hdb.db.Exec("DELETE FROM block;")
 338	} else {
 339		_, err = hdb.db.Exec("DELETE FROM block WHERE eth_block_num > $1;", lastValidBlock)
 340	}
 341	return tracerr.Wrap(err)
 342}
 343
 344// AddBids insert Bids into the DB
 345func (hdb *HistoryDB) AddBids(bids []common.Bid) error { return hdb.addBids(hdb.db, bids) }
 346func (hdb *HistoryDB) addBids(d meddler.DB, bids []common.Bid) error {
 347	if len(bids) == 0 {
 348		return nil
 349	}
 350	// TODO: check the coordinator info
 351	return tracerr.Wrap(db.BulkInsert(
 352		d,
 353		"INSERT INTO bid (slot_num, bid_value, eth_block_num, bidder_addr) VALUES %s;",
 354		bids[:],
 355	))
 356}
 357
 358// GetAllBids retrieve all bids from the DB
 359func (hdb *HistoryDB) GetAllBids() ([]common.Bid, error) {
 360	var bids []*common.Bid
 361	err := meddler.QueryAll(
 362		hdb.db, &bids,
 363		`SELECT bid.slot_num, bid.bid_value, bid.eth_block_num, bid.bidder_addr FROM bid
 364		ORDER BY item_id;`,
 365	)
 366	return db.SlicePtrsToSlice(bids).([]common.Bid), tracerr.Wrap(err)
 367}
 368
 369// GetBestBidAPI returns the best bid in specific slot by slotNum
 370func (hdb *HistoryDB) GetBestBidAPI(slotNum *int64) (BidAPI, error) {
 371	bid := &BidAPI{}
 372	err := meddler.QueryRow(
 373		hdb.db, bid, `SELECT bid.*, block.timestamp, coordinator.forger_addr, coordinator.url 
 374		FROM bid INNER JOIN block ON bid.eth_block_num = block.eth_block_num 
 375		INNER JOIN coordinator ON bid.bidder_addr = coordinator.bidder_addr 
 376		WHERE slot_num = $1 ORDER BY item_id DESC LIMIT 1;`, slotNum,
 377	)
 378	return *bid, tracerr.Wrap(err)
 379}
 380
 381// GetBestBidCoordinator returns the forger address of the highest bidder in a slot by slotNum
 382func (hdb *HistoryDB) GetBestBidCoordinator(slotNum int64) (*common.BidCoordinator, error) {
 383	bidCoord := &common.BidCoordinator{}
 384	err := meddler.QueryRow(
 385		hdb.db, bidCoord,
 386		`SELECT (
 387			SELECT default_slot_set_bid
 388			FROM auction_vars
 389			WHERE default_slot_set_bid_slot_num <= $1
 390			ORDER BY eth_block_num DESC LIMIT 1
 391			),
 392		bid.slot_num, bid.bid_value, bid.bidder_addr,
 393		coordinator.forger_addr, coordinator.url
 394		FROM bid
 395		INNER JOIN coordinator ON bid.bidder_addr = coordinator.bidder_addr
 396		WHERE bid.slot_num = $1 ORDER BY bid.item_id DESC LIMIT 1;`,
 397		slotNum)
 398
 399	return bidCoord, tracerr.Wrap(err)
 400}
 401
 402// GetBestBidsAPI returns the best bid in specific slot by slotNum
 403func (hdb *HistoryDB) GetBestBidsAPI(
 404	minSlotNum, maxSlotNum *int64,
 405	bidderAddr *ethCommon.Address,
 406	limit *uint, order string,
 407) ([]BidAPI, uint64, error) {
 408	var query string
 409	var args []interface{}
 410	queryStr := `SELECT b.*, block.timestamp, coordinator.forger_addr, coordinator.url, 
 411	COUNT(*) OVER() AS total_items FROM (
 412	   SELECT slot_num, MAX(item_id) as maxitem 
 413	   FROM bid GROUP BY slot_num
 414	   )
 415	AS x INNER JOIN bid AS b ON b.item_id = x.maxitem
 416	INNER JOIN block ON b.eth_block_num = block.eth_block_num
 417	INNER JOIN coordinator ON b.bidder_addr = coordinator.bidder_addr 
 418	WHERE (b.slot_num >= ? AND b.slot_num <= ?)`
 419	args = append(args, minSlotNum)
 420	args = append(args, maxSlotNum)
 421	// Apply filters
 422	if bidderAddr != nil {
 423		queryStr += " AND b.bidder_addr = ? "
 424		args = append(args, bidderAddr)
 425	}
 426	queryStr += " ORDER BY b.slot_num "
 427	if order == OrderAsc {
 428		queryStr += "ASC "
 429	} else {
 430		queryStr += "DESC "
 431	}
 432	if limit != nil {
 433		queryStr += fmt.Sprintf("LIMIT %d;", *limit)
 434	}
 435	query = hdb.db.Rebind(queryStr)
 436	bidPtrs := []*BidAPI{}
 437	if err := meddler.QueryAll(hdb.db, &bidPtrs, query, args...); err != nil {
 438		return nil, 0, tracerr.Wrap(err)
 439	}
 440	// log.Debug(query)
 441	bids := db.SlicePtrsToSlice(bidPtrs).([]BidAPI)
 442	if len(bids) == 0 {
 443		return bids, 0, nil
 444	}
 445	return bids, bids[0].TotalItems - uint64(len(bids)), nil
 446}
 447
 448// GetBidsAPI return the bids applying the given filters
 449func (hdb *HistoryDB) GetBidsAPI(
 450	slotNum *int64, forgerAddr *ethCommon.Address,
 451	fromItem, limit *uint, order string,
 452) ([]BidAPI, uint64, error) {
 453	var query string
 454	var args []interface{}
 455	queryStr := `SELECT bid.*, block.timestamp, coordinator.forger_addr, coordinator.url, 
 456	COUNT(*) OVER() AS total_items
 457	FROM bid INNER JOIN block ON bid.eth_block_num = block.eth_block_num 
 458	INNER JOIN coordinator ON bid.bidder_addr = coordinator.bidder_addr `
 459	// Apply filters
 460	nextIsAnd := false
 461	// slotNum filter
 462	if slotNum != nil {
 463		if nextIsAnd {
 464			queryStr += "AND "
 465		} else {
 466			queryStr += "WHERE "
 467		}
 468		queryStr += "bid.slot_num = ? "
 469		args = append(args, slotNum)
 470		nextIsAnd = true
 471	}
 472	// slotNum filter
 473	if forgerAddr != nil {
 474		if nextIsAnd {
 475			queryStr += "AND "
 476		} else {
 477			queryStr += "WHERE "
 478		}
 479		queryStr += "bid.bidder_addr = ? "
 480		args = append(args, forgerAddr)
 481		nextIsAnd = true
 482	}
 483	if fromItem != nil {
 484		if nextIsAnd {
 485			queryStr += "AND "
 486		} else {
 487			queryStr += "WHERE "
 488		}
 489		if order == OrderAsc {
 490			queryStr += "bid.item_id >= ? "
 491		} else {
 492			queryStr += "bid.item_id <= ? "
 493		}
 494		args = append(args, fromItem)
 495	}
 496	// pagination
 497	queryStr += "ORDER BY bid.item_id "
 498	if order == OrderAsc {
 499		queryStr += "ASC "
 500	} else {
 501		queryStr += "DESC "
 502	}
 503	queryStr += fmt.Sprintf("LIMIT %d;", *limit)
 504	query, argsQ, err := sqlx.In(queryStr, args...)
 505	if err != nil {
 506		return nil, 0, tracerr.Wrap(err)
 507	}
 508	query = hdb.db.Rebind(query)
 509	bids := []*BidAPI{}
 510	if err := meddler.QueryAll(hdb.db, &bids, query, argsQ...); err != nil {
 511		return nil, 0, tracerr.Wrap(err)
 512	}
 513	if len(bids) == 0 {
 514		return []BidAPI{}, 0, nil
 515	}
 516	return db.SlicePtrsToSlice(bids).([]BidAPI), bids[0].TotalItems - uint64(len(bids)), nil
 517}
 518
 519// AddCoordinators insert Coordinators into the DB
 520func (hdb *HistoryDB) AddCoordinators(coordinators []common.Coordinator) error {
 521	return tracerr.Wrap(hdb.addCoordinators(hdb.db, coordinators))
 522}
 523func (hdb *HistoryDB) addCoordinators(d meddler.DB, coordinators []common.Coordinator) error {
 524	if len(coordinators) == 0 {
 525		return nil
 526	}
 527	return tracerr.Wrap(db.BulkInsert(
 528		d,
 529		"INSERT INTO coordinator (bidder_addr, forger_addr, eth_block_num, url) VALUES %s;",
 530		coordinators[:],
 531	))
 532}
 533
 534// AddExitTree insert Exit tree into the DB
 535func (hdb *HistoryDB) AddExitTree(exitTree []common.ExitInfo) error {
 536	return tracerr.Wrap(hdb.addExitTree(hdb.db, exitTree))
 537}
 538func (hdb *HistoryDB) addExitTree(d meddler.DB, exitTree []common.ExitInfo) error {
 539	if len(exitTree) == 0 {
 540		return nil
 541	}
 542	return tracerr.Wrap(db.BulkInsert(
 543		d,
 544		"INSERT INTO exit_tree (batch_num, account_idx, merkle_proof, balance, "+
 545			"instant_withdrawn, delayed_withdraw_request, delayed_withdrawn) VALUES %s;",
 546		exitTree[:],
 547	))
 548}
 549
 550func (hdb *HistoryDB) updateExitTree(d sqlx.Ext, blockNum int64,
 551	rollupWithdrawals []common.WithdrawInfo, wDelayerWithdrawals []common.WDelayerTransfer) error {
 552	if len(rollupWithdrawals) == 0 && len(wDelayerWithdrawals) == 0 {
 553		return nil
 554	}
 555	type withdrawal struct {
 556		BatchNum               int64              `db:"batch_num"`
 557		AccountIdx             int64              `db:"account_idx"`
 558		InstantWithdrawn       *int64             `db:"instant_withdrawn"`
 559		DelayedWithdrawRequest *int64             `db:"delayed_withdraw_request"`
 560		DelayedWithdrawn       *int64             `db:"delayed_withdrawn"`
 561		Owner                  *ethCommon.Address `db:"owner"`
 562		Token                  *ethCommon.Address `db:"token"`
 563	}
 564	withdrawals := make([]withdrawal, len(rollupWithdrawals)+len(wDelayerWithdrawals))
 565	for i := range rollupWithdrawals {
 566		info := &rollupWithdrawals[i]
 567		withdrawals[i] = withdrawal{
 568			BatchNum:   int64(info.NumExitRoot),
 569			AccountIdx: int64(info.Idx),
 570		}
 571		if info.InstantWithdraw {
 572			withdrawals[i].InstantWithdrawn = &blockNum
 573		} else {
 574			withdrawals[i].DelayedWithdrawRequest = &blockNum
 575			withdrawals[i].Owner = &info.Owner
 576			withdrawals[i].Token = &info.Token
 577		}
 578	}
 579	for i := range wDelayerWithdrawals {
 580		info := &wDelayerWithdrawals[i]
 581		withdrawals[len(rollupWithdrawals)+i] = withdrawal{
 582			DelayedWithdrawn: &blockNum,
 583			Owner:            &info.Owner,
 584			Token:            &info.Token,
 585		}
 586	}
 587	// In VALUES we set an initial row of NULLs to set the types of each
 588	// variable passed as argument
 589	const query string = `
 590		UPDATE exit_tree e SET
 591			instant_withdrawn = d.instant_withdrawn,
 592			delayed_withdraw_request = CASE
 593				WHEN e.delayed_withdraw_request IS NOT NULL THEN e.delayed_withdraw_request
 594				ELSE d.delayed_withdraw_request
 595			END,
 596			delayed_withdrawn = d.delayed_withdrawn,
 597			owner = d.owner,
 598			token = d.token
 599		FROM (VALUES
 600			(NULL::::BIGINT, NULL::::BIGINT, NULL::::BIGINT, NULL::::BIGINT, NULL::::BIGINT, NULL::::BYTEA, NULL::::BYTEA),
 601			(:batch_num,
 602			 :account_idx,
 603			 :instant_withdrawn,
 604			 :delayed_withdraw_request,
 605			 :delayed_withdrawn,
 606			 :owner,
 607			 :token)
 608		) as d (batch_num, account_idx, instant_withdrawn, delayed_withdraw_request, delayed_withdrawn, owner, token)
 609		WHERE
 610			(d.batch_num IS NOT NULL AND e.batch_num = d.batch_num AND e.account_idx = d.account_idx) OR
 611			(d.delayed_withdrawn IS NOT NULL AND e.delayed_withdrawn IS NULL AND e.owner = d.owner AND e.token = d.token);
 612		`
 613	if len(withdrawals) > 0 {
 614		if _, err := sqlx.NamedExec(d, query, withdrawals); err != nil {
 615			return tracerr.Wrap(err)
 616		}
 617	}
 618
 619	return nil
 620}
 621
 622// AddToken insert a token into the DB
 623func (hdb *HistoryDB) AddToken(token *common.Token) error {
 624	return tracerr.Wrap(meddler.Insert(hdb.db, "token", token))
 625}
 626
 627// AddTokens insert tokens into the DB
 628func (hdb *HistoryDB) AddTokens(tokens []common.Token) error { return hdb.addTokens(hdb.db, tokens) }
 629func (hdb *HistoryDB) addTokens(d meddler.DB, tokens []common.Token) error {
 630	if len(tokens) == 0 {
 631		return nil
 632	}
 633	return tracerr.Wrap(db.BulkInsert(
 634		d,
 635		`INSERT INTO token (
 636			token_id,
 637			eth_block_num,
 638			eth_addr,
 639			name,
 640			symbol,
 641			decimals
 642		) VALUES %s;`,
 643		tokens[:],
 644	))
 645}
 646
 647// UpdateTokenValue updates the USD value of a token
 648func (hdb *HistoryDB) UpdateTokenValue(tokenSymbol string, value float64) error {
 649	_, err := hdb.db.Exec(
 650		"UPDATE token SET usd = $1 WHERE symbol = $2;",
 651		value, tokenSymbol,
 652	)
 653	return tracerr.Wrap(err)
 654}
 655
 656// GetToken returns a token from the DB given a TokenID
 657func (hdb *HistoryDB) GetToken(tokenID common.TokenID) (*TokenWithUSD, error) {
 658	token := &TokenWithUSD{}
 659	err := meddler.QueryRow(
 660		hdb.db, token, `SELECT * FROM token WHERE token_id = $1;`, tokenID,
 661	)
 662	return token, tracerr.Wrap(err)
 663}
 664
 665// GetAllTokens returns all tokens from the DB
 666func (hdb *HistoryDB) GetAllTokens() ([]TokenWithUSD, error) {
 667	var tokens []*TokenWithUSD
 668	err := meddler.QueryAll(
 669		hdb.db, &tokens,
 670		"SELECT * FROM token ORDER BY token_id;",
 671	)
 672	return db.SlicePtrsToSlice(tokens).([]TokenWithUSD), tracerr.Wrap(err)
 673}
 674
 675// GetTokens returns a list of tokens from the DB
 676func (hdb *HistoryDB) GetTokens(
 677	ids []common.TokenID, symbols []string, name string, fromItem,
 678	limit *uint, order string,
 679) ([]TokenWithUSD, uint64, error) {
 680	var query string
 681	var args []interface{}
 682	queryStr := `SELECT * , COUNT(*) OVER() AS total_items FROM token `
 683	// Apply filters
 684	nextIsAnd := false
 685	if len(ids) > 0 {
 686		queryStr += "WHERE token_id IN (?) "
 687		nextIsAnd = true
 688		args = append(args, ids)
 689	}
 690	if len(symbols) > 0 {
 691		if nextIsAnd {
 692			queryStr += "AND "
 693		} else {
 694			queryStr += "WHERE "
 695		}
 696		queryStr += "symbol IN (?) "
 697		args = append(args, symbols)
 698		nextIsAnd = true
 699	}
 700	if name != "" {
 701		if nextIsAnd {
 702			queryStr += "AND "
 703		} else {
 704			queryStr += "WHERE "
 705		}
 706		queryStr += "name ~ ? "
 707		args = append(args, name)
 708		nextIsAnd = true
 709	}
 710	if fromItem != nil {
 711		if nextIsAnd {
 712			queryStr += "AND "
 713		} else {
 714			queryStr += "WHERE "
 715		}
 716		if order == OrderAsc {
 717			queryStr += "item_id >= ? "
 718		} else {
 719			queryStr += "item_id <= ? "
 720		}
 721		args = append(args, fromItem)
 722	}
 723	// pagination
 724	queryStr += "ORDER BY item_id "
 725	if order == OrderAsc {
 726		queryStr += "ASC "
 727	} else {
 728		queryStr += "DESC "
 729	}
 730	queryStr += fmt.Sprintf("LIMIT %d;", *limit)
 731	query, argsQ, err := sqlx.In(queryStr, args...)
 732	if err != nil {
 733		return nil, 0, tracerr.Wrap(err)
 734	}
 735	query = hdb.db.Rebind(query)
 736	tokens := []*TokenWithUSD{}
 737	if err := meddler.QueryAll(hdb.db, &tokens, query, argsQ...); err != nil {
 738		return nil, 0, tracerr.Wrap(err)
 739	}
 740	if len(tokens) == 0 {
 741		return []TokenWithUSD{}, 0, nil
 742	}
 743	return db.SlicePtrsToSlice(tokens).([]TokenWithUSD), uint64(len(tokens)) - tokens[0].TotalItems, nil
 744}
 745
 746// GetTokenSymbols returns all the token symbols from the DB
 747func (hdb *HistoryDB) GetTokenSymbols() ([]string, error) {
 748	var tokenSymbols []string
 749	rows, err := hdb.db.Query("SELECT symbol FROM token;")
 750	if err != nil {
 751		return nil, tracerr.Wrap(err)
 752	}
 753	defer db.RowsClose(rows)
 754	sym := new(string)
 755	for rows.Next() {
 756		err = rows.Scan(sym)
 757		if err != nil {
 758			return nil, tracerr.Wrap(err)
 759		}
 760		tokenSymbols = append(tokenSymbols, *sym)
 761	}
 762	return tokenSymbols, nil
 763}
 764
 765// AddAccounts insert accounts into the DB
 766func (hdb *HistoryDB) AddAccounts(accounts []common.Account) error {
 767	return tracerr.Wrap(hdb.addAccounts(hdb.db, accounts))
 768}
 769func (hdb *HistoryDB) addAccounts(d meddler.DB, accounts []common.Account) error {
 770	if len(accounts) == 0 {
 771		return nil
 772	}
 773	return tracerr.Wrap(db.BulkInsert(
 774		d,
 775		`INSERT INTO account (
 776			idx,
 777			token_id,
 778			batch_num,
 779			bjj,
 780			eth_addr
 781		) VALUES %s;`,
 782		accounts[:],
 783	))
 784}
 785
 786// GetAllAccounts returns a list of accounts from the DB
 787func (hdb *HistoryDB) GetAllAccounts() ([]common.Account, error) {
 788	var accs []*common.Account
 789	err := meddler.QueryAll(
 790		hdb.db, &accs,
 791		"SELECT * FROM account ORDER BY idx;",
 792	)
 793	return db.SlicePtrsToSlice(accs).([]common.Account), tracerr.Wrap(err)
 794}
 795
 796// AddL1Txs inserts L1 txs to the DB. USD and DepositAmountUSD will be set automatically before storing the tx.
 797// If the tx is originated by a coordinator, BatchNum must be provided. If it's originated by a user,
 798// BatchNum should be null, and the value will be setted by a trigger when a batch forges the tx.
 799// EffectiveAmount and EffectiveDepositAmount are seted with default values by the DB.
 800func (hdb *HistoryDB) AddL1Txs(l1txs []common.L1Tx) error {
 801	return tracerr.Wrap(hdb.addL1Txs(hdb.db, l1txs))
 802}
 803
 804// addL1Txs inserts L1 txs to the DB. USD and DepositAmountUSD will be set automatically before storing the tx.
 805// If the tx is originated by a coordinator, BatchNum must be provided. If it's originated by a user,
 806// BatchNum should be null, and the value will be setted by a trigger when a batch forges the tx.
 807// EffectiveAmount and EffectiveDepositAmount are seted with default values by the DB.
 808func (hdb *HistoryDB) addL1Txs(d meddler.DB, l1txs []common.L1Tx) error {
 809	if len(l1txs) == 0 {
 810		return nil
 811	}
 812	txs := []txWrite{}
 813	for i := 0; i < len(l1txs); i++ {
 814		af := new(big.Float).SetInt(l1txs[i].Amount)
 815		amountFloat, _ := af.Float64()
 816		laf := new(big.Float).SetInt(l1txs[i].DepositAmount)
 817		depositAmountFloat, _ := laf.Float64()
 818		txs = append(txs, txWrite{
 819			// Generic
 820			IsL1:        true,
 821			TxID:        l1txs[i].TxID,
 822			Type:        l1txs[i].Type,
 823			Position:    l1txs[i].Position,
 824			FromIdx:     &l1txs[i].FromIdx,
 825			ToIdx:       l1txs[i].ToIdx,
 826			Amount:      l1txs[i].Amount,
 827			AmountFloat: amountFloat,
 828			TokenID:     l1txs[i].TokenID,
 829			BatchNum:    l1txs[i].BatchNum,
 830			EthBlockNum: l1txs[i].EthBlockNum,
 831			// L1
 832			ToForgeL1TxsNum:    l1txs[i].ToForgeL1TxsNum,
 833			UserOrigin:         &l1txs[i].UserOrigin,
 834			FromEthAddr:        &l1txs[i].FromEthAddr,
 835			FromBJJ:            &l1txs[i].FromBJJ,
 836			DepositAmount:      l1txs[i].DepositAmount,
 837			DepositAmountFloat: &depositAmountFloat,
 838		})
 839	}
 840	return tracerr.Wrap(hdb.addTxs(d, txs))
 841}
 842
 843// AddL2Txs inserts L2 txs to the DB. TokenID, USD and FeeUSD will be set automatically before storing the tx.
 844func (hdb *HistoryDB) AddL2Txs(l2txs []common.L2Tx) error {
 845	return tracerr.Wrap(hdb.addL2Txs(hdb.db, l2txs))
 846}
 847
 848// addL2Txs inserts L2 txs to the DB. TokenID, USD and FeeUSD will be set automatically before storing the tx.
 849func (hdb *HistoryDB) addL2Txs(d meddler.DB, l2txs []common.L2Tx) error {
 850	txs := []txWrite{}
 851	for i := 0; i < len(l2txs); i++ {
 852		f := new(big.Float).SetInt(l2txs[i].Amount)
 853		amountFloat, _ := f.Float64()
 854		txs = append(txs, txWrite{
 855			// Generic
 856			IsL1:        false,
 857			TxID:        l2txs[i].TxID,
 858			Type:        l2txs[i].Type,
 859			Position:    l2txs[i].Position,
 860			FromIdx:     &l2txs[i].FromIdx,
 861			ToIdx:       l2txs[i].ToIdx,
 862			Amount:      l2txs[i].Amount,
 863			AmountFloat: amountFloat,
 864			BatchNum:    &l2txs[i].BatchNum,
 865			EthBlockNum: l2txs[i].EthBlockNum,
 866			// L2
 867			Fee:   &l2txs[i].Fee,
 868			Nonce: &l2txs[i].Nonce,
 869		})
 870	}
 871	return tracerr.Wrap(hdb.addTxs(d, txs))
 872}
 873
 874func (hdb *HistoryDB) addTxs(d meddler.DB, txs []txWrite) error {
 875	if len(txs) == 0 {
 876		return nil
 877	}
 878	return tracerr.Wrap(db.BulkInsert(
 879		d,
 880		`INSERT INTO tx (
 881			is_l1,
 882			id,
 883			type,
 884			position,
 885			from_idx,
 886			to_idx,
 887			amount,
 888			amount_f,
 889			token_id,
 890			batch_num,
 891			eth_block_num,
 892			to_forge_l1_txs_num,
 893			user_origin,
 894			from_eth_addr,
 895			from_bjj,
 896			deposit_amount,
 897			deposit_amount_f,
 898			fee,
 899			nonce
 900		) VALUES %s;`,
 901		txs[:],
 902	))
 903}
 904
 905// // GetTxs returns a list of txs from the DB
 906// func (hdb *HistoryDB) GetTxs() ([]common.Tx, error) {
 907// 	var txs []*common.Tx
 908// 	err := meddler.QueryAll(
 909// 		hdb.db, &txs,
 910// 		`SELECT * FROM tx
 911// 		ORDER BY (batch_num, position) ASC`,
 912// 	)
 913// 	return db.SlicePtrsToSlice(txs).([]common.Tx), err
 914// }
 915
 916// GetHistoryTx returns a tx from the DB given a TxID
 917func (hdb *HistoryDB) GetHistoryTx(txID common.TxID) (*TxAPI, error) {
 918	// Warning: amount_success and deposit_amount_success have true as default for
 919	// performance reasons. The expected default value is false (when txs are unforged)
 920	// this case is handled at the function func (tx TxAPI) MarshalJSON() ([]byte, error)
 921	tx := &TxAPI{}
 922	err := meddler.QueryRow(
 923		hdb.db, tx, `SELECT tx.item_id, tx.is_l1, tx.id, tx.type, tx.position, 
 924		hez_idx(tx.from_idx, token.symbol) AS from_idx, tx.from_eth_addr, tx.from_bjj,
 925		hez_idx(tx.to_idx, token.symbol) AS to_idx, tx.to_eth_addr, tx.to_bjj,
 926		tx.amount, tx.amount_success, tx.token_id, tx.amount_usd, 
 927		tx.batch_num, tx.eth_block_num, tx.to_forge_l1_txs_num, tx.user_origin, 
 928		tx.deposit_amount, tx.deposit_amount_usd, tx.deposit_amount_success, tx.fee, tx.fee_usd, tx.nonce,
 929		token.token_id, token.item_id AS token_item_id, token.eth_block_num AS token_block,
 930		token.eth_addr, token.name, token.symbol, token.decimals, token.usd,
 931		token.usd_update, block.timestamp
 932		FROM tx INNER JOIN token ON tx.token_id = token.token_id 
 933		INNER JOIN block ON tx.eth_block_num = block.eth_block_num 
 934		WHERE tx.id = $1;`, txID,
 935	)
 936	return tx, tracerr.Wrap(err)
 937}
 938
 939// GetHistoryTxs returns a list of txs from the DB using the HistoryTx struct
 940// and pagination info
 941func (hdb *HistoryDB) GetHistoryTxs(
 942	ethAddr *ethCommon.Address, bjj *babyjub.PublicKeyComp,
 943	tokenID *common.TokenID, idx *common.Idx, batchNum *uint, txType *common.TxType,
 944	fromItem, limit *uint, order string,
 945) ([]TxAPI, uint64, error) {
 946	// Warning: amount_success and deposit_amount_success have true as default for
 947	// performance reasons. The expected default value is false (when txs are unforged)
 948	// this case is handled at the function func (tx TxAPI) MarshalJSON() ([]byte, error)
 949	if ethAddr != nil && bjj != nil {
 950		return nil, 0, tracerr.Wrap(errors.New("ethAddr and bjj are incompatible"))
 951	}
 952	var query string
 953	var args []interface{}
 954	queryStr := `SELECT tx.item_id, tx.is_l1, tx.id, tx.type, tx.position, 
 955	hez_idx(tx.from_idx, token.symbol) AS from_idx, tx.from_eth_addr, tx.from_bjj,
 956	hez_idx(tx.to_idx, token.symbol) AS to_idx, tx.to_eth_addr, tx.to_bjj,
 957	tx.amount, tx.amount_success, tx.token_id, tx.amount_usd, 
 958	tx.batch_num, tx.eth_block_num, tx.to_forge_l1_txs_num, tx.user_origin, 
 959	tx.deposit_amount, tx.deposit_amount_usd, tx.deposit_amount_success, tx.fee, tx.fee_usd, tx.nonce,
 960	token.token_id, token.item_id AS token_item_id, token.eth_block_num AS token_block,
 961	token.eth_addr, token.name, token.symbol, token.decimals, token.usd,
 962	token.usd_update, block.timestamp, count(*) OVER() AS total_items 
 963	FROM tx INNER JOIN token ON tx.token_id = token.token_id 
 964	INNER JOIN block ON tx.eth_block_num = block.eth_block_num `
 965	// Apply filters
 966	nextIsAnd := false
 967	// ethAddr filter
 968	if ethAddr != nil {
 969		queryStr += "WHERE (tx.from_eth_addr = ? OR tx.to_eth_addr = ?) "
 970		nextIsAnd = true
 971		args = append(args, ethAddr, ethAddr)
 972	} else if bjj != nil { // bjj filter
 973		queryStr += "WHERE (tx.from_bjj = ? OR tx.to_bjj = ?) "
 974		nextIsAnd = true
 975		args = append(args, bjj, bjj)
 976	}
 977	// tokenID filter
 978	if tokenID != nil {
 979		if nextIsAnd {
 980			queryStr += "AND "
 981		} else {
 982			queryStr += "WHERE "
 983		}
 984		queryStr += "tx.token_id = ? "
 985		args = append(args, tokenID)
 986		nextIsAnd = true
 987	}
 988	// idx filter
 989	if idx != nil {
 990		if nextIsAnd {
 991			queryStr += "AND "
 992		} else {
 993			queryStr += "WHERE "
 994		}
 995		queryStr += "(tx.from_idx = ? OR tx.to_idx = ?) "
 996		args = append(args, idx, idx)
 997		nextIsAnd = true
 998	}
 999	// batchNum filter
1000	if batchNum != nil {
1001		if nextIsAnd {
1002			queryStr += "AND "
1003		} else {
1004			queryStr += "WHERE "
1005		}
1006		queryStr += "tx.batch_num = ? "
1007		args = append(args, batchNum)
1008		nextIsAnd = true
1009	}
1010	// txType filter
1011	if txType != nil {
1012		if nextIsAnd {
1013			queryStr += "AND "
1014		} else {
1015			queryStr += "WHERE "
1016		}
1017		queryStr += "tx.type = ? "
1018		args = append(args, txType)
1019		nextIsAnd = true
1020	}
1021	if fromItem != nil {
1022		if nextIsAnd {
1023			queryStr += "AND "
1024		} else {
1025			queryStr += "WHERE "
1026		}
1027		if order == OrderAsc {
1028			queryStr += "tx.item_id >= ? "
1029		} else {
1030			queryStr += "tx.item_id <= ? "
1031		}
1032		args = append(args, fromItem)
1033		nextIsAnd = true
1034	}
1035	if nextIsAnd {
1036		queryStr += "AND "
1037	} else {
1038		queryStr += "WHERE "
1039	}
1040	queryStr += "tx.batch_num IS NOT NULL "
1041
1042	// pagination
1043	queryStr += "ORDER BY tx.item_id "
1044	if order == OrderAsc {
1045		queryStr += " ASC "
1046	} else {
1047		queryStr += " DESC "
1048	}
1049	queryStr += fmt.Sprintf("LIMIT %d;", *limit)
1050	query = hdb.db.Rebind(queryStr)
1051	// log.Debug(query)
1052	txsPtrs := []*TxAPI{}
1053	if err := meddler.QueryAll(hdb.db, &txsPtrs, query, args...); err != nil {
1054		return nil, 0, tracerr.Wrap(err)
1055	}
1056	txs := db.SlicePtrsToSlice(txsPtrs).([]TxAPI)
1057	if len(txs) == 0 {
1058		return txs, 0, nil
1059	}
1060	return txs, txs[0].TotalItems - uint64(len(txs)), nil
1061}
1062
1063// GetAllExits returns all exit from the DB
1064func (hdb *HistoryDB) GetAllExits() ([]common.ExitInfo, error) {
1065	var exits []*common.ExitInfo
1066	err := meddler.QueryAll(
1067		hdb.db, &exits,
1068		`SELECT exit_tree.batch_num, exit_tree.account_idx, exit_tree.merkle_proof,
1069		exit_tree.balance, exit_tree.instant_withdrawn, exit_tree.delayed_withdraw_request,
1070		exit_tree.delayed_withdrawn FROM exit_tree ORDER BY item_id;`,
1071	)
1072	return db.SlicePtrsToSlice(exits).([]common.ExitInfo), tracerr.Wrap(err)
1073}
1074
1075// GetExitAPI returns a exit from the DB
1076func (hdb *HistoryDB) GetExitAPI(batchNum *uint, idx *common.Idx) (*ExitAPI, error) {
1077	exit := &ExitAPI{}
1078	err := meddler.QueryRow(
1079		hdb.db, exit, `SELECT exit_tree.item_id, exit_tree.batch_num,
1080		hez_idx(exit_tree.account_idx, token.symbol) AS account_idx,
1081		account.bjj, account.eth_addr,
1082		exit_tree.merkle_proof, exit_tree.balance, exit_tree.instant_withdrawn,
1083		exit_tree.delayed_withdraw_request, exit_tree.delayed_withdrawn,
1084		token.token_id, token.item_id AS token_item_id, 
1085		token.eth_block_num AS token_block, token.eth_addr AS token_eth_addr, token.name, token.symbol, 
1086		token.decimals, token.usd, token.usd_update
1087		FROM exit_tree INNER JOIN account ON exit_tree.account_idx = account.idx 
1088		INNER JOIN token ON account.token_id = token.token_id 
1089		WHERE exit_tree.batch_num = $1 AND exit_tree.account_idx = $2;`, batchNum, idx,
1090	)
1091	return exit, tracerr.Wrap(err)
1092}
1093
1094// GetExitsAPI returns a list of exits from the DB and pagination info
1095func (hdb *HistoryDB) GetExitsAPI(
1096	ethAddr *ethCommon.Address, bjj *babyjub.PublicKeyComp, tokenID *common.TokenID,
1097	idx *common.Idx, batchNum *uint, onlyPendingWithdraws *bool,
1098	fromItem, limit *uint, order string,
1099) ([]ExitAPI, uint64, error) {
1100	if ethAddr != nil && bjj != nil {
1101		return nil, 0, tracerr.Wrap(errors.New("ethAddr and bjj are incompatible"))
1102	}
1103	var query string
1104	var args []interface{}
1105	queryStr := `SELECT exit_tree.item_id, exit_tree.batch_num,
1106	hez_idx(exit_tree.account_idx, token.symbol) AS account_idx,
1107	account.bjj, account.eth_addr,
1108	exit_tree.merkle_proof, exit_tree.balance, exit_tree.instant_withdrawn,
1109	exit_tree.delayed_withdraw_request, exit_tree.delayed_withdrawn,
1110	token.token_id, token.item_id AS token_item_id,
1111	token.eth_block_num AS token_block, token.eth_addr AS token_eth_addr, token.name, token.symbol,
1112	token.decimals, token.usd, token.usd_update, COUNT(*) OVER() AS total_items
1113	FROM exit_tree INNER JOIN account ON exit_tree.account_idx = account.idx 
1114	INNER JOIN token ON account.token_id = token.token_id `
1115	// Apply filters
1116	nextIsAnd := false
1117	// ethAddr filter
1118	if ethAddr != nil {
1119		queryStr += "WHERE account.eth_addr = ? "
1120		nextIsAnd = true
1121		args = append(args, ethAddr)
1122	} else if bjj != nil { // bjj filter
1123		queryStr += "WHERE account.bjj = ? "
1124		nextIsAnd = true
1125		args = append(args, bjj)
1126	}
1127	// tokenID filter
1128	if tokenID != nil {
1129		if nextIsAnd {
1130			queryStr += "AND "
1131		} else {
1132			queryStr += "WHERE "
1133		}
1134		queryStr += "account.token_id = ? "
1135		args = append(args, tokenID)
1136		nextIsAnd = true
1137	}
1138	// idx filter
1139	if idx != nil {
1140		if nextIsAnd {
1141			queryStr += "AND "
1142		} else {
1143			queryStr += "WHERE "
1144		}
1145		queryStr += "exit_tree.account_idx = ? "
1146		args = append(args, idx)
1147		nextIsAnd = true
1148	}
1149	// batchNum filter
1150	if batchNum != nil {
1151		if nextIsAnd {
1152			queryStr += "AND "
1153		} else {
1154			queryStr += "WHERE "
1155		}
1156		queryStr += "exit_tree.batch_num = ? "
1157		args = append(args, batchNum)
1158		nextIsAnd = true
1159	}
1160	// onlyPendingWithdraws
1161	if onlyPendingWithdraws != nil {
1162		if *onlyPendingWithdraws {
1163			if nextIsAnd {
1164				queryStr += "AND "
1165			} else {
1166				queryStr += "WHERE "
1167			}
1168			queryStr += "(exit_tree.instant_withdrawn IS NULL AND exit_tree.delayed_withdrawn IS NULL) "
1169			nextIsAnd = true
1170		}
1171	}
1172	if fromItem != nil {
1173		if nextIsAnd {
1174			queryStr += "AND "
1175		} else {
1176			queryStr += "WHERE "
1177		}
1178		if order == OrderAsc {
1179			queryStr += "exit_tree.item_id >= ? "
1180		} else {
1181			queryStr += "exit_tree.item_id <= ? "
1182		}
1183		args = append(args, fromItem)
1184		// nextIsAnd = true
1185	}
1186	// pagination
1187	queryStr += "ORDER BY exit_tree.item_id "
1188	if order == OrderAsc {
1189		queryStr += " ASC "
1190	} else {
1191		queryStr += " DESC "
1192	}
1193	queryStr += fmt.Sprintf("LIMIT %d;", *limit)
1194	query = hdb.db.Rebind(queryStr)
1195	// log.Debug(query)
1196	exits := []*ExitAPI{}
1197	if err := meddler.QueryAll(hdb.db, &exits, query, args...); err != nil {
1198		return nil, 0, tracerr.Wrap(err)
1199	}
1200	if len(exits) == 0 {
1201		return []ExitAPI{}, 0, nil
1202	}
1203	return db.SlicePtrsToSlice(exits).([]ExitAPI), exits[0].TotalItems - uint64(len(exits)), nil
1204}
1205
1206// GetAllL1UserTxs returns all L1UserTxs from the DB
1207func (hdb *HistoryDB) GetAllL1UserTxs() ([]common.L1Tx, error) {
1208	var txs []*common.L1Tx
1209	err := meddler.QueryAll(
1210		hdb.db, &txs, // Note that '\x' gets parsed as a big.Int with value = 0
1211		`SELECT tx.id, tx.to_forge_l1_txs_num, tx.position, tx.user_origin,
1212		tx.from_idx, tx.from_eth_addr, tx.from_bjj, tx.to_idx, tx.token_id,
1213		tx.amount, (CASE WHEN tx.batch_num IS NULL THEN NULL WHEN tx.amount_success THEN tx.amount ELSE '\x' END) AS effective_amount,
1214		tx.deposit_amount, (CASE WHEN tx.batch_num IS NULL THEN NULL WHEN tx.deposit_amount_success THEN tx.deposit_amount ELSE '\x' END) AS effective_deposit_amount,
1215		tx.eth_block_num, tx.type, tx.batch_num
1216		FROM tx WHERE is_l1 = TRUE AND user_origin = TRUE ORDER BY item_id;`,
1217	)
1218	return db.SlicePtrsToSlice(txs).([]common.L1Tx), tracerr.Wrap(err)
1219}
1220
1221// GetAllL1CoordinatorTxs returns all L1CoordinatorTxs from the DB
1222func (hdb *HistoryDB) GetAllL1CoordinatorTxs() ([]common.L1Tx, error) {
1223	var txs []*common.L1Tx
1224	// Since the query specifies that only coordinator txs are returned, it's safe to assume
1225	// that returned txs will always have effective amounts
1226	err := meddler.QueryAll(
1227		hdb.db, &txs,
1228		`SELECT tx.id, tx.to_forge_l1_txs_num, tx.position, tx.user_origin,
1229		tx.from_idx, tx.from_eth_addr, tx.from_bjj, tx.to_idx, tx.token_id,
1230		tx.amount, tx.amount AS effective_amount,
1231		tx.deposit_amount, tx.deposit_amount AS effective_deposit_amount,
1232		tx.eth_block_num, tx.type, tx.batch_num
1233		FROM tx WHERE is_l1 = TRUE AND user_origin = FALSE ORDER BY item_id;`,
1234	)
1235	return db.SlicePtrsToSlice(txs).([]common.L1Tx), tracerr.Wrap(err)
1236}
1237
1238// GetAllL2Txs returns all L2Txs from the DB
1239func (hdb *HistoryDB) GetAllL2Txs() ([]common.L2Tx, error) {
1240	var txs []*common.L2Tx
1241	err := meddler.QueryAll(
1242		hdb.db, &txs,
1243		`SELECT tx.id, tx.batch_num, tx.position,
1244		tx.from_idx, tx.to_idx, tx.amount, tx.fee, tx.nonce,
1245		tx.type, tx.eth_block_num
1246		FROM tx WHERE is_l1 = FALSE ORDER BY item_id;`,
1247	)
1248	return db.SlicePtrsToSlice(txs).([]common.L2Tx), tracerr.Wrap(err)
1249}
1250
1251// GetUnforgedL1UserTxs gets L1 User Txs to be forged in the L1Batch with toForgeL1TxsNum.
1252func (hdb *HistoryDB) GetUnforgedL1UserTxs(toForgeL1TxsNum int64) ([]common.L1Tx, error) {
1253	var txs []*common.L1Tx
1254	err := meddler.QueryAll(
1255		hdb.db, &txs, // only L1 user txs can have batch_num set to null
1256		`SELECT tx.id, tx.to_forge_l1_txs_num, tx.position, tx.user_origin,
1257		tx.from_idx, tx.from_eth_addr, tx.from_bjj, tx.to_idx, tx.token_id,
1258		tx.amount, NULL AS effective_amount,
1259		tx.deposit_amount, NULL AS effective_deposit_amount,
1260		tx.eth_block_num, tx.type, tx.batch_num
1261		FROM tx WHERE batch_num IS NULL AND to_forge_l1_txs_num = $1
1262		ORDER BY position;`,
1263		toForgeL1TxsNum,
1264	)
1265	return db.SlicePtrsToSlice(txs).([]common.L1Tx), tracerr.Wrap(err)
1266}
1267
1268// TODO: Think about chaning all the queries that return a last value, to queries that return the next valid value.
1269
1270// GetLastTxsPosition for a given to_forge_l1_txs_num
1271func (hdb *HistoryDB) GetLastTxsPosition(toForgeL1TxsNum int64) (int, error) {
1272	row := hdb.db.QueryRow(
1273		"SELECT position FROM tx WHERE to_forge_l1_txs_num = $1 ORDER BY position DESC;",
1274		toForgeL1TxsNum,
1275	)
1276	var lastL1TxsPosition int
1277	return lastL1TxsPosition, tracerr.Wrap(row.Scan(&lastL1TxsPosition))
1278}
1279
1280// GetSCVars returns the rollup, auction and wdelayer smart contracts variables at their last update.
1281func (hdb *HistoryDB) GetSCVars() (*common.RollupVariables, *common.AuctionVariables,
1282	*common.WDelayerVariables, error) {
1283	var rollup common.RollupVariables
1284	var auction common.AuctionVariables
1285	var wDelayer common.WDelayerVariables
1286	if err := meddler.QueryRow(hdb.db, &rollup,
1287		"SELECT * FROM rollup_vars ORDER BY eth_block_num DESC LIMIT 1;"); err != nil {
1288		return nil, nil, nil, tracerr.Wrap(err)
1289	}
1290	if err := meddler.QueryRow(hdb.db, &auction,
1291		"SELECT * FROM auction_vars ORDER BY eth_block_num DESC LIMIT 1;"); err != nil {
1292		return nil, nil, nil, tracerr.Wrap(err)
1293	}
1294	if err := meddler.QueryRow(hdb.db, &wDelayer,
1295		"SELECT * FROM wdelayer_vars ORDER BY eth_block_num DESC LIMIT 1;"); err != nil {
1296		return nil, nil, nil, tracerr.Wrap(err)
1297	}
1298	return &rollup, &auction, &wDelayer, nil
1299}
1300
1301func (hdb *HistoryDB) setRollupVars(d meddler.DB, rollup *common.RollupVariables) error {
1302	return tracerr.Wrap(meddler.Insert(d, "rollup_vars", rollup))
1303}
1304
1305func (hdb *HistoryDB) setAuctionVars(d meddler.DB, auction *common.AuctionVariables) error {
1306	return tracerr.Wrap(meddler.Insert(d, "auction_vars", auction))
1307}
1308
1309func (hdb *HistoryDB) setWDelayerVars(d meddler.DB, wDelayer *common.WDelayerVariables) error {
1310	return tracerr.Wrap(meddler.Insert(d, "wdelayer_vars", wDelayer))
1311}
1312
1313func (hdb *HistoryDB) addBucketUpdates(d meddler.DB, bucketUpdates []common.BucketUpdate) error {
1314	if len(bucketUpdates) == 0 {
1315		return nil
1316	}
1317	return tracerr.Wrap(db.BulkInsert(
1318		d,
1319		`INSERT INTO bucket_update (
1320		 	eth_block_num,
1321		 	num_bucket,
1322		 	block_stamp,
1323		 	withdrawals
1324		) VALUES %s;`,
1325		bucketUpdates[:],
1326	))
1327}
1328
1329// GetAllBucketUpdates retrieves all the bucket updates
1330func (hdb *HistoryDB) GetAllBucketUpdates() ([]common.BucketUpdate, error) {
1331	var bucketUpdates []*common.BucketUpdate
1332	err := meddler.QueryAll(
1333		hdb.db, &bucketUpdates,
1334		"SELECT * FROM bucket_update ORDER BY item_id;",
1335	)
1336	return db.SlicePtrsToSlice(bucketUpdates).([]common.BucketUpdate), tracerr.Wrap(err)
1337}
1338
1339func (hdb *HistoryDB) addTokenExchanges(d meddler.DB, tokenExchanges []common.TokenExchange) error {
1340	if len(tokenExchanges) == 0 {
1341		return nil
1342	}
1343	return tracerr.Wrap(db.BulkInsert(
1344		d,
1345		`INSERT INTO token_exchange (
1346			eth_block_num,
1347    			eth_addr,
1348    			value_usd
1349		) VALUES %s;`,
1350		tokenExchanges[:],
1351	))
1352}
1353
1354// GetAllTokenExchanges retrieves all the token exchanges
1355func (hdb *HistoryDB) GetAllTokenExchanges() ([]common.TokenExchange, error) {
1356	var tokenExchanges []*common.TokenExchange
1357	err := meddler.QueryAll(
1358		hdb.db, &tokenExchanges,
1359		"SELECT * FROM token_exchange ORDER BY item_id;",
1360	)
1361	return db.SlicePtrsToSlice(tokenExchanges).([]common.TokenExchange), tracerr.Wrap(err)
1362}
1363
1364func (hdb *HistoryDB) addEscapeHatchWithdrawals(d meddler.DB,
1365	escapeHatchWithdrawals []common.WDelayerEscapeHatchWithdrawal) error {
1366	if len(escapeHatchWithdrawals) == 0 {
1367		return nil
1368	}
1369	return tracerr.Wrap(db.BulkInsert(
1370		d,
1371		`INSERT INTO escape_hatch_withdrawal (
1372			eth_block_num,
1373			who_addr,
1374			to_addr,
1375			token_addr,
1376			amount
1377		) VALUES %s;`,
1378		escapeHatchWithdrawals[:],
1379	))
1380}
1381
1382// GetAllEscapeHatchWithdrawals retrieves all the escape hatch withdrawals
1383func (hdb *HistoryDB) GetAllEscapeHatchWithdrawals() ([]common.WDelayerEscapeHatchWithdrawal, error) {
1384	var escapeHatchWithdrawals []*common.WDelayerEscapeHatchWithdrawal
1385	err := meddler.QueryAll(
1386		hdb.db, &escapeHatchWithdrawals,
1387		"SELECT * FROM escape_hatch_withdrawal ORDER BY item_id;",
1388	)
1389	return db.SlicePtrsToSlice(escapeHatchWithdrawals).([]common.WDelayerEscapeHatchWithdrawal),
1390		tracerr.Wrap(err)
1391}
1392
1393// SetInitialSCVars sets the initial state of rollup, auction, wdelayer smart
1394// contract variables.  This initial state is stored linked to block 0, which
1395// always exist in the DB and is used to store initialization data that always
1396// exist in the smart contracts.
1397func (hdb *HistoryDB) SetInitialSCVars(rollup *common.RollupVariables,
1398	auction *common.AuctionVariables, wDelayer *common.WDelayerVariables) error {
1399	txn, err := hdb.db.Beginx()
1400	if err != nil {
1401		return tracerr.Wrap(err)
1402	}
1403	defer func() {
1404		if err != nil {
1405			db.Rollback(txn)
1406		}
1407	}()
1408	// Force EthBlockNum to be 0 because it's the block used to link data
1409	// that belongs to the creation of the smart contracts
1410	rollup.EthBlockNum = 0
1411	auction.EthBlockNum = 0
1412	wDelayer.EthBlockNum = 0
1413	auction.DefaultSlotSetBidSlotNum = 0
1414	if err := hdb.setRollupVars(txn, rollup); err != nil {
1415		return tracerr.Wrap(err)
1416	}
1417	if err := hdb.setAuctionVars(txn, auction); err != nil {
1418		return tracerr.Wrap(err)
1419	}
1420	if err := hdb.setWDelayerVars(txn, wDelayer); err != nil {
1421		return tracerr.Wrap(err)
1422	}
1423
1424	return tracerr.Wrap(txn.Commit())
1425}
1426
1427// setL1UserTxEffectiveAmounts sets the EffectiveAmount and EffectiveDepositAmount
1428// of the given l1UserTxs (with an UPDATE)
1429func (hdb *HistoryDB) setL1UserTxEffectiveAmounts(d sqlx.Ext, txs []common.L1Tx) error {
1430	if len(txs) == 0 {
1431		return nil
1432	}
1433	// Effective amounts are stored as success flags in the DB, with true value by default
1434	// to reduce the amount of updates. Therefore, only amounts that became uneffective should be
1435	// updated to become false
1436	type txUpdate struct {
1437		ID                   common.TxID `db:"id"`
1438		AmountSuccess        bool        `db:"amount_success"`
1439		DepositAmountSuccess bool        `db:"deposit_amount_success"`
1440	}
1441	txUpdates := []txUpdate{}
1442	equal := func(a *big.Int, b *big.Int) bool {
1443		return a.Cmp(b) == 0
1444	}
1445	for i := range txs {
1446		amountSuccess := equal(txs[i].Amount, txs[i].EffectiveAmount)
1447		depositAmountSuccess := equal(txs[i].DepositAmount, txs[i].EffectiveDepositAmount)
1448		if !amountSuccess || !depositAmountSuccess {
1449			txUpdates = append(txUpdates, txUpdate{
1450				ID:                   txs[i].TxID,
1451				AmountSuccess:        amountSuccess,
1452				DepositAmountSuccess: depositAmountSuccess,
1453			})
1454		}
1455	}
1456	const query string = `
1457		UPDATE tx SET
1458			amount_success = tx_update.amount_success,
1459			deposit_amount_success = tx_update.deposit_amount_success
1460		FROM (VALUES
1461			(NULL::::BYTEA, NULL::::BOOL, NULL::::BOOL),
1462			(:id, :amount_success, :deposit_amount_success)
1463		) as tx_update (id, amount_success, deposit_amount_success)
1464		WHERE tx.id = tx_update.id;
1465	`
1466	if len(txUpdates) > 0 {
1467		if _, err := sqlx.NamedExec(d, query, txUpdates); err != nil {
1468			return tracerr.Wrap(err)
1469		}
1470	}
1471	return nil
1472}
1473
1474// AddBlockSCData stores all the information of a block retrieved by the
1475// Synchronizer.  Blocks should be inserted in order, leaving no gaps because
1476// the pagination system of the API/DB depends on this.  Within blocks, all
1477// items should also be in the correct order (Accounts, Tokens, Txs, etc.)
1478func (hdb *HistoryDB) AddBlockSCData(blockData *common.BlockData) (err error) {
1479	txn, err := hdb.db.Beginx()
1480	if err != nil {
1481		return tracerr.Wrap(err)
1482	}
1483	defer func() {
1484		if err != nil {
1485			db.Rollback(txn)
1486		}
1487	}()
1488
1489	// Add block
1490	if err := hdb.addBlock(txn, &blockData.Block); err != nil {
1491		return tracerr.Wrap(err)
1492	}
1493
1494	// Add Coordinators
1495	if err := hdb.addCoordinators(txn, blockData.Auction.Coordinators); err != nil {
1496		return tracerr.Wrap(err)
1497	}
1498
1499	// Add Bids
1500	if err := hdb.addBids(txn, blockData.Auction.Bids); err != nil {
1501		return tracerr.Wrap(err)
1502	}
1503
1504	// Add Tokens
1505	if err := hdb.addTokens(txn, blockData.Rollup.AddedTokens); err != nil {
1506		return tracerr.Wrap(err)
1507	}
1508
1509	// Prepare user L1 txs to be added.
1510	// They must be added before the batch that will forge them (which can be in the same block)
1511	// and after the account that will be sent to (also can be in the same block).
1512	// Note: insert order is not relevant since item_id will be updated by a DB trigger when
1513	// the batch that forges those txs is inserted
1514	userL1s := make(map[common.BatchNum][]common.L1Tx)
1515	for i := range blockData.Rollup.L1UserTxs {
1516		batchThatForgesIsInTheBlock := false
1517		for _, batch := range blockData.Rollup.Batches {
1518			if batch.Batch.ForgeL1TxsNum != nil &&
1519				*batch.Batch.ForgeL1TxsNum == *blockData.Rollup.L1UserTxs[i].ToForgeL1TxsNum {
1520				// Tx is forged in this block. It's guaranteed that:
1521				// * the first batch of the block won't forge user L1 txs that have been added in this block
1522				// * batch nums are sequential therefore it's safe to add the tx at batch.BatchNum -1
1523				batchThatForgesIsInTheBlock = true
1524				addAtBatchNum := batch.Batch.BatchNum - 1
1525				userL1s[addAtBatchNum] = append(userL1s[addAtBatchNum], blockData.Rollup.L1UserTxs[i])
1526				break
1527			}
1528		}
1529		if !batchThatForgesIsInTheBlock {
1530			// User artificial batchNum 0 to add txs that are not forge in this block
1531			// after all the accounts of the block have been added
1532			userL1s[0] = append(userL1s[0], blockData.Rollup.L1UserTxs[i])
1533		}
1534	}
1535
1536	// Add Batches
1537	for i := range blockData.Rollup.Batches {
1538		batch := &blockData.Rollup.Batches[i]
1539
1540		// Add Batch: this will trigger an update on the DB
1541		// that will set the batch num of forged L1 txs in this batch
1542		if err = hdb.addBatch(txn, &batch.Batch); err != nil {
1543			return tracerr.Wrap(err)
1544		}
1545
1546		// Set the EffectiveAmount and EffectiveDepositAmount of all the
1547		// L1UserTxs that have been forged in this batch
1548		if err = hdb.setL1UserTxEffectiveAmounts(txn, batch.L1UserTxs); err != nil {
1549			return tracerr.Wrap(err)
1550		}
1551
1552		// Add accounts
1553		if err := hdb.addAccounts(txn, batch.CreatedAccounts); err != nil {
1554			return tracerr.Wrap(err)
1555		}
1556
1557		// Add forged l1 coordinator Txs
1558		if err := hdb.addL1Txs(txn, batch.L1CoordinatorTxs); err != nil {
1559			return tracerr.Wrap(err)
1560		}
1561
1562		// Add l2 Txs
1563		if err := hdb.addL2Txs(txn, batch.L2Txs); err != nil {
1564			return tracerr.Wrap(err)
1565		}
1566
1567		// Add user L1 txs that will be forged in next batch
1568		if userlL1s, ok := userL1s[batch.Batch.BatchNum]; ok {
1569			if err := hdb.addL1Txs(txn, userlL1s); err != nil {
1570				return tracerr.Wrap(err)
1571			}
1572		}
1573
1574		// Add exit tree
1575		if err := hdb.addExitTree(txn, batch.ExitTree); err != nil {
1576			return tracerr.Wrap(err)
1577		}
1578	}
1579	// Add user L1 txs that won't be forged in this block
1580	if userL1sNotForgedInThisBlock, ok := userL1s[0]; ok {
1581		if err := hdb.addL1Txs(txn, userL1sNotForgedInThisBlock); err != nil {
1582			return tracerr.Wrap(err)
1583		}
1584	}
1585
1586	// Set SC Vars if there was an update
1587	if blockData.Rollup.Vars != nil {
1588		if err := hdb.setRollupVars(txn, blockData.Rollup.Vars); err != nil {
1589			return tracerr.Wrap(err)
1590		}
1591	}
1592	if blockData.Auction.Vars != nil {
1593		if err := hdb.setAuctionVars(txn, blockData.Auction.Vars); err != nil {
1594			return tracerr.Wrap(err)
1595		}
1596	}
1597	if blockData.WDelayer.Vars != nil {
1598		if err := hdb.setWDelayerVars(txn, blockData.WDelayer.Vars); err != nil {
1599			return tracerr.Wrap(err)
1600		}
1601	}
1602
1603	// Update withdrawals in exit tree table
1604	if err := hdb.updateExitTree(txn, blockData.Block.Num,
1605		blockData.Rollup.Withdrawals, blockData.WDelayer.Withdrawals); err != nil {
1606		return tracerr.Wrap(err)
1607	}
1608
1609	// Add Escape Hatch Withdrawals
1610	if err := hdb.addEscapeHatchWithdrawals(txn,
1611		blockData.WDelayer.EscapeHatchWithdrawals); err != nil {
1612		return tracerr.Wrap(err)
1613	}
1614
1615	// Add Buckets withdrawals updates
1616	if err := hdb.addBucketUpdates(txn, blockData.Rollup.UpdateBucketWithdraw); err != nil {
1617		return tracerr.Wrap(err)
1618	}
1619
1620	// Add Token exchange updates
1621	if err := hdb.addTokenExchanges(txn, blockData.Rollup.TokenExchanges); err != nil {
1622		return tracerr.Wrap(err)
1623	}
1624
1625	return tracerr.Wrap(txn.Commit())
1626}
1627
1628// GetCoordinatorAPI returns a coordinator by its bidderAddr
1629func (hdb *HistoryDB) GetCoordinatorAPI(bidderAddr ethCommon.Address) (*CoordinatorAPI, error) {
1630	coordinator := &CoordinatorAPI{}
1631	err := meddler.QueryRow(hdb.db, coordinator, "SELECT * FROM coordinator WHERE bidder_addr = $1;", bidderAddr)
1632	return coordinator, tracerr.Wrap(err)
1633}
1634
1635// GetCoordinatorsAPI returns a list of coordinators from the DB and pagination info
1636func (hdb *HistoryDB) GetCoordinatorsAPI(
1637	bidderAddr, forgerAddr *ethCommon.Address,
1638	fromItem, limit *uint, order string,
1639) ([]CoordinatorAPI, uint64, error) {
1640	var query string
1641	var args []interface{}
1642	queryStr := `SELECT coordinator.*, 
1643	COUNT(*) OVER() AS total_items
1644	FROM coordinator `
1645	// Apply filters
1646	nextIsAnd := false
1647	if bidderAddr != nil {
1648		queryStr += "WHERE bidder_addr = ? "
1649		nextIsAnd = true
1650		args = append(args, bidderAddr)
1651	}
1652	if forgerAddr != nil {
1653		if nextIsAnd {
1654			queryStr += "AND "
1655		} else {
1656			queryStr += "WHERE "
1657		}
1658		queryStr += "forger_addr = ? "
1659		nextIsAnd = true
1660		args = append(args, forgerAddr)
1661	}
1662	if fromItem != nil {
1663		if nextIsAnd {
1664			queryStr += "AND "
1665		} else {
1666			queryStr += "WHERE "
1667		}
1668		if order == OrderAsc {
1669			queryStr += "coordinator.item_id >= ? "
1670		} else {
1671			queryStr += "coordinator.item_id <= ? "
1672		}
1673		args = append(args, fromItem)
1674	}
1675	// pagination
1676	queryStr += "ORDER BY coordinator.item_id "
1677	if order == OrderAsc {
1678		queryStr += " ASC "
1679	} else {
1680		queryStr += " DESC "
1681	}
1682	queryStr += fmt.Sprintf("LIMIT %d;", *limit)
1683	query = hdb.db.Rebind(queryStr)
1684
1685	coordinators := []*CoordinatorAPI{}
1686	if err := meddler.QueryAll(hdb.db, &coordinators, query, args...); err != nil {
1687		return nil, 0, tracerr.Wrap(err)
1688	}
1689	if len(coordinators) == 0 {
1690		return []CoordinatorAPI{}, 0, nil
1691	}
1692	return db.SlicePtrsToSlice(coordinators).([]CoordinatorAPI),
1693		coordinators[0].TotalItems - uint64(len(coordinators)), nil
1694}
1695
1696// AddAuctionVars insert auction vars into the DB
1697func (hdb *HistoryDB) AddAuctionVars(auctionVars *common.AuctionVariables) error {
1698	return tracerr.Wrap(meddler.Insert(hdb.db, "auction_vars", auctionVars))
1699}
1700
1701// GetAuctionVars returns auction variables
1702func (hdb *HistoryDB) GetAuctionVars() (*common.AuctionVariables, error) {
1703	auctionVars := &common.AuctionVariables{}
1704	err := meddler.QueryRow(
1705		hdb.db, auctionVars, `SELECT * FROM auction_vars;`,
1706	)
1707	return auctionVars, tracerr.Wrap(err)
1708}
1709
1710// GetAuctionVarsUntilSetSlotNum returns all the updates of the auction vars
1711// from the last entry in which DefaultSlotSetBidSlotNum <= slotNum
1712func (hdb *HistoryDB) GetAuctionVarsUntilSetSlotNum(slotNum int64, maxItems int) ([]MinBidInfo, error) {
1713	auctionVars := []*MinBidInfo{}
1714	query := `
1715		SELECT DISTINCT default_slot_set_bid, default_slot_set_bid_slot_num FROM auction_vars
1716		WHERE default_slot_set_bid_slot_num < $1
1717		ORDER BY default_slot_set_bid_slot_num DESC
1718		LIMIT $2;
1719	`
1720	err := meddler.QueryAll(hdb.db, &auctionVars, query, slotNum, maxItems)
1721	if err != nil {
1722		return nil, tracerr.Wrap(err)
1723	}
1724	return db.SlicePtrsToSlice(auctionVars).([]MinBidInfo), nil
1725}
1726
1727// GetAccountAPI returns an account by its index
1728func (hdb *HistoryDB) GetAccountAPI(idx common.Idx) (*AccountAPI, error) {
1729	account := &AccountAPI{}
1730	err := meddler.QueryRow(hdb.db, account, `SELECT account.item_id, hez_idx(account.idx, 
1731	token.symbol) as idx, account.batch_num, account.bjj, account.eth_addr,
1732	token.token_id, token.item_id AS token_item_id, token.eth_block_num AS token_block,
1733	token.eth_addr as token_eth_addr, token.name, token.symbol, token.decimals, token.usd, token.usd_update 
1734	FROM account INNER JOIN token ON account.token_id = token.token_id WHERE idx = $1;`, idx)
1735
1736	if err != nil {
1737		return nil, tracerr.Wrap(err)
1738	}
1739
1740	return account, nil
1741}
1742
1743// GetAccountsAPI returns a list of accounts from the DB and pagination info
1744func (hdb *HistoryDB) GetAccountsAPI(
1745	tokenIDs []common.TokenID, ethAddr *ethCommon.Address,
1746	bjj *babyjub.PublicKeyComp, fromItem, limit *uint, order string,
1747) ([]AccountAPI, uint64, error) {
1748	if ethAddr != nil && bjj != nil {
1749		return nil, 0, tracerr.Wrap(errors.New("ethAddr and bjj are incompatible"))
1750	}
1751	var query string
1752	var args []interface{}
1753	queryStr := `SELECT account.item_id, hez_idx(account.idx, token.symbol) as idx, account.batch_num, 
1754	account.bjj, account.eth_addr, token.token_id, token.item_id AS token_item_id, token.eth_block_num AS token_block,
1755	token.eth_addr as token_eth_addr, token.name, token.symbol, token.decimals, token.usd, token.usd_update, 
1756	COUNT(*) OVER() AS total_items
1757	FROM account INNER JOIN token ON account.token_id = token.token_id `
1758	// Apply filters
1759	nextIsAnd := false
1760	// ethAddr filter
1761	if ethAddr != nil {
1762		queryStr += "WHERE account.eth_addr = ? "
1763		nextIsAnd = true
1764		args = append(args, ethAddr)
1765	} else if bjj != nil { // bjj filter
1766		queryStr += "WHERE account.bjj = ? "
1767		nextIsAnd = true
1768		args = append(args, bjj)
1769	}
1770	// tokenID filter
1771	if len(tokenIDs) > 0 {
1772		if nextIsAnd {
1773			queryStr += "AND "
1774		} else {
1775			queryStr += "WHERE "
1776		}
1777		queryStr += "account.token_id IN (?) "
1778		args = append(args, tokenIDs)
1779		nextIsAnd = true
1780	}
1781	if fromItem != nil {
1782		if nextIsAnd {
1783			queryStr += "AND "
1784		} else {
1785			queryStr += "WHERE "
1786		}
1787		if order == OrderAsc {
1788			queryStr += "account.item_id >= ? "
1789		} else {
1790			queryStr += "account.item_id <= ? "
1791		}
1792		args = append(args, fromItem)
1793	}
1794	// pagination
1795	queryStr += "ORDER BY account.item_id "
1796	if order == OrderAsc {
1797		queryStr += " ASC "
1798	} else {
1799		queryStr += " DESC "
1800	}
1801	queryStr += fmt.Sprintf("LIMIT %d;", *limit)
1802	query, argsQ, err := sqlx.In(queryStr, args...)
1803	if err != nil {
1804		return nil, 0, tracerr.Wrap(err)
1805	}
1806	query = hdb.db.Rebind(query)
1807
1808	accounts := []*AccountAPI{}
1809	if err := meddler.QueryAll(hdb.db, &accounts, query, argsQ...); err != nil {
1810		return nil, 0, tracerr.Wrap(err)
1811	}
1812	if len(accounts) == 0 {
1813		return []AccountAPI{}, 0, nil
1814	}
1815
1816	return db.SlicePtrsToSlice(accounts).([]AccountAPI),
1817		accounts[0].TotalItems - uint64(len(accounts)), nil
1818}
1819
1820// GetMetrics returns metrics
1821func (hdb *HistoryDB) GetMetrics(lastBatchNum common.BatchNum) (*Metrics, error) {
1822	metricsTotals := &MetricsTotals{}
1823	metrics := &Metrics{}
1824	err := meddler.QueryRow(
1825		hdb.db, metricsTotals, `SELECT COUNT(tx.*) as total_txs,
1826		COALESCE (MIN(tx.batch_num), 0) as batch_num, COALESCE (MIN(block.timestamp), 
1827		NOW()) AS min_timestamp, COALESCE (MAX(block.timestamp), NOW()) AS max_timestamp
1828		FROM tx INNER JOIN block ON tx.eth_block_num = block.eth_block_num
1829		WHERE block.timestamp >= NOW() - INTERVAL '24 HOURS';`)
1830	if err != nil {
1831		return nil, tracerr.Wrap(err)
1832	}
1833
1834	seconds := metricsTotals.MaxTimestamp.Sub(metricsTotals.MinTimestamp).Seconds()
1835	// Avoid dividing by 0
1836	if seconds == 0 {
1837		seconds++
1838	}
1839
1840	metrics.TransactionsPerSecond = float64(metricsTotals.TotalTransactions) / seconds
1841
1842	if (lastBatchNum - metricsTotals.FirstBatchNum) > 0 {
1843		metrics.TransactionsPerBatch = float64(metricsTotals.TotalTransactions) /
1844			float64(lastBatchNum-metricsTotals.FirstBatchNum+1)
1845	} else {
1846		metrics.TransactionsPerBatch = float64(0)
1847	}
1848
1849	err = meddler.QueryRow(
1850		hdb.db, metricsTotals, `SELECT COUNT(*) AS total_batches, 
1851		COALESCE (SUM(total_fees_usd), 0) AS total_fees FROM batch 
1852		WHERE batch_num > $1;`, metricsTotals.FirstBatchNum)
1853	if err != nil {
1854		return nil, tracerr.Wrap(err)
1855	}
1856	if metricsTotals.TotalBatches > 0 {
1857		metrics.BatchFrequency = seconds / float64(metricsTotals.TotalBatches)
1858	} else {
1859		metrics.BatchFrequency = 0
1860	}
1861	if metricsTotals.TotalTransactions > 0 {
1862		metrics.AvgTransactionFee = metricsTotals.TotalFeesUSD / float64(metricsTotals.TotalTransactions)
1863	} else {
1864		metrics.AvgTransactionFee = 0
1865	}
1866	err = meddler.QueryRow(
1867		hdb.db, metrics,
1868		`SELECT COUNT(*) AS total_bjjs, COUNT(DISTINCT(bjj)) AS total_accounts FROM account;`)
1869	if err != nil {
1870		return nil, tracerr.Wrap(err)
1871	}
1872
1873	return metrics, nil
1874}
1875
1876// GetAvgTxFee returns average transaction fee of the last 1h
1877func (hdb *HistoryDB) GetAvgTxFee() (float64, error) {
1878	metricsTotals := &MetricsTotals{}
1879	err := meddler.QueryRow(
1880		hdb.db, metricsTotals, `SELECT COUNT(tx.*) as total_txs, 
1881		COALESCE (MIN(tx.batch_num), 0) as batch_num 
1882		FROM tx INNER JOIN block ON tx.eth_block_num = block.eth_block_num
1883		WHERE block.timestamp >= NOW() - INTERVAL '1 HOURS';`)
1884	if err != nil {
1885		return 0, tracerr.Wrap(err)
1886	}
1887	err = meddler.QueryRow(
1888		hdb.db, metricsTotals, `SELECT COUNT(*) AS total_batches, 
1889		COALESCE (SUM(total_fees_usd), 0) AS total_fees FROM batch 
1890		WHERE batch_num > $1;`, metricsTotals.FirstBatchNum)
1891	if err != nil {
1892		return 0, tracerr.Wrap(err)
1893	}
1894
1895	var avgTransactionFee float64
1896	if metricsTotals.TotalTransactions > 0 {
1897		avgTransactionFee = metricsTotals.TotalFeesUSD / float64(metricsTotals.TotalTransactions)
1898	} else {
1899		avgTransactionFee = 0
1900	}
1901
1902	return avgTransactionFee, nil
1903}