arnaucube
arnaucube/hermez-nodePublic Self-hosted repository
hermez-node/db/historydb/apiqueries.go
db/historydb/apiqueries.goRaw
   1package historydb
   2
   3import (
   4	"database/sql"
   5	"errors"
   6	"fmt"
   7	"math/big"
   8	"time"
   9
  10	ethCommon "github.com/ethereum/go-ethereum/common"
  11	"github.com/hermeznetwork/hermez-node/apitypes"
  12	"github.com/hermeznetwork/hermez-node/common"
  13	"github.com/hermeznetwork/hermez-node/db"
  14	"github.com/hermeznetwork/tracerr"
  15	"github.com/iden3/go-iden3-crypto/babyjub"
  16	"github.com/jmoiron/sqlx"
  17	"github.com/russross/meddler"
  18)
  19
  20// GetLastBlockAPI retrieve the block with the highest block number from the DB
  21func (hdb *HistoryDB) GetLastBlockAPI() (*common.Block, error) {
  22	cancel, err := hdb.apiConnCon.Acquire()
  23	defer cancel()
  24	if err != nil {
  25		return nil, tracerr.Wrap(err)
  26	}
  27	defer hdb.apiConnCon.Release()
  28	return hdb.GetLastBlock()
  29}
  30
  31// GetBatchAPI return the batch with the given batchNum
  32func (hdb *HistoryDB) GetBatchAPI(batchNum common.BatchNum) (*BatchAPI, error) {
  33	cancel, err := hdb.apiConnCon.Acquire()
  34	defer cancel()
  35	if err != nil {
  36		return nil, tracerr.Wrap(err)
  37	}
  38	defer hdb.apiConnCon.Release()
  39	return hdb.getBatchAPI(hdb.dbRead, batchNum)
  40}
  41
  42// GetBatchInternalAPI return the batch with the given batchNum
  43func (hdb *HistoryDB) GetBatchInternalAPI(batchNum common.BatchNum) (*BatchAPI, error) {
  44	return hdb.getBatchAPI(hdb.dbRead, batchNum)
  45}
  46
  47func (hdb *HistoryDB) getBatchAPI(d meddler.DB, batchNum common.BatchNum) (*BatchAPI, error) {
  48	batch := &BatchAPI{}
  49	if err := meddler.QueryRow(
  50		d, batch,
  51		`SELECT batch.item_id, batch.batch_num, batch.eth_block_num,
  52		batch.forger_addr, batch.fees_collected, batch.total_fees_usd, batch.state_root,
  53		batch.num_accounts, batch.exit_root, batch.forge_l1_txs_num, batch.slot_num,
  54		block.timestamp, block.hash,
  55	    COALESCE ((SELECT COUNT(*) FROM tx WHERE batch_num = batch.batch_num), 0) AS forged_txs
  56	    FROM batch INNER JOIN block ON batch.eth_block_num = block.eth_block_num
  57	 	WHERE batch_num = $1;`, batchNum,
  58	); err != nil {
  59		return nil, tracerr.Wrap(err)
  60	}
  61	batch.CollectedFeesAPI = apitypes.NewCollectedFeesAPI(batch.CollectedFeesDB)
  62	return batch, nil
  63}
  64
  65// GetBatchesAPI return the batches applying the given filters
  66func (hdb *HistoryDB) GetBatchesAPI(
  67	minBatchNum, maxBatchNum, slotNum *uint,
  68	forgerAddr *ethCommon.Address,
  69	fromItem, limit *uint, order string,
  70) ([]BatchAPI, uint64, error) {
  71	cancel, err := hdb.apiConnCon.Acquire()
  72	defer cancel()
  73	if err != nil {
  74		return nil, 0, tracerr.Wrap(err)
  75	}
  76	defer hdb.apiConnCon.Release()
  77	var query string
  78	var args []interface{}
  79	queryStr := `SELECT batch.item_id, batch.batch_num, batch.eth_block_num,
  80	batch.forger_addr, batch.fees_collected, batch.total_fees_usd, batch.state_root,
  81	batch.num_accounts, batch.exit_root, batch.forge_l1_txs_num, batch.slot_num,
  82	block.timestamp, block.hash,
  83	COALESCE ((SELECT COUNT(*) FROM tx WHERE batch_num = batch.batch_num), 0) AS forged_txs,
  84	count(*) OVER() AS total_items
  85	FROM batch INNER JOIN block ON batch.eth_block_num = block.eth_block_num `
  86	// Apply filters
  87	nextIsAnd := false
  88	// minBatchNum filter
  89	if minBatchNum != nil {
  90		if nextIsAnd {
  91			queryStr += "AND "
  92		} else {
  93			queryStr += "WHERE "
  94		}
  95		queryStr += "batch.batch_num > ? "
  96		args = append(args, minBatchNum)
  97		nextIsAnd = true
  98	}
  99	// maxBatchNum filter
 100	if maxBatchNum != nil {
 101		if nextIsAnd {
 102			queryStr += "AND "
 103		} else {
 104			queryStr += "WHERE "
 105		}
 106		queryStr += "batch.batch_num < ? "
 107		args = append(args, maxBatchNum)
 108		nextIsAnd = true
 109	}
 110	// slotNum filter
 111	if slotNum != nil {
 112		if nextIsAnd {
 113			queryStr += "AND "
 114		} else {
 115			queryStr += "WHERE "
 116		}
 117		queryStr += "batch.slot_num = ? "
 118		args = append(args, slotNum)
 119		nextIsAnd = true
 120	}
 121	// forgerAddr filter
 122	if forgerAddr != nil {
 123		if nextIsAnd {
 124			queryStr += "AND "
 125		} else {
 126			queryStr += "WHERE "
 127		}
 128		queryStr += "batch.forger_addr = ? "
 129		args = append(args, forgerAddr)
 130		nextIsAnd = true
 131	}
 132	// pagination
 133	if fromItem != nil {
 134		if nextIsAnd {
 135			queryStr += "AND "
 136		} else {
 137			queryStr += "WHERE "
 138		}
 139		if order == OrderAsc {
 140			queryStr += "batch.item_id >= ? "
 141		} else {
 142			queryStr += "batch.item_id <= ? "
 143		}
 144		args = append(args, fromItem)
 145	}
 146	queryStr += "ORDER BY batch.item_id "
 147	if order == OrderAsc {
 148		queryStr += " ASC "
 149	} else {
 150		queryStr += " DESC "
 151	}
 152	queryStr += fmt.Sprintf("LIMIT %d;", *limit)
 153	query = hdb.dbRead.Rebind(queryStr)
 154	// log.Debug(query)
 155	batchPtrs := []*BatchAPI{}
 156	if err := meddler.QueryAll(hdb.dbRead, &batchPtrs, query, args...); err != nil {
 157		return nil, 0, tracerr.Wrap(err)
 158	}
 159	batches := db.SlicePtrsToSlice(batchPtrs).([]BatchAPI)
 160	if len(batches) == 0 {
 161		return batches, 0, nil
 162	}
 163	for i := range batches {
 164		batches[i].CollectedFeesAPI = apitypes.NewCollectedFeesAPI(batches[i].CollectedFeesDB)
 165	}
 166	return batches, batches[0].TotalItems - uint64(len(batches)), nil
 167}
 168
 169// GetBestBidAPI returns the best bid in specific slot by slotNum
 170func (hdb *HistoryDB) GetBestBidAPI(slotNum *int64) (BidAPI, error) {
 171	bid := &BidAPI{}
 172	cancel, err := hdb.apiConnCon.Acquire()
 173	defer cancel()
 174	if err != nil {
 175		return *bid, tracerr.Wrap(err)
 176	}
 177	defer hdb.apiConnCon.Release()
 178	err = meddler.QueryRow(
 179		hdb.dbRead, bid, `SELECT bid.*, block.timestamp, coordinator.forger_addr, coordinator.url 
 180		FROM bid INNER JOIN block ON bid.eth_block_num = block.eth_block_num
 181		INNER JOIN (
 182			SELECT bidder_addr, MAX(item_id) AS item_id FROM coordinator
 183			GROUP BY bidder_addr
 184		) c ON bid.bidder_addr = c.bidder_addr 
 185		INNER JOIN coordinator ON c.item_id = coordinator.item_id 
 186		WHERE slot_num = $1 ORDER BY item_id DESC LIMIT 1;`, slotNum,
 187	)
 188	return *bid, tracerr.Wrap(err)
 189}
 190
 191// GetBestBidsAPI returns the best bid in specific slot by slotNum
 192func (hdb *HistoryDB) GetBestBidsAPI(
 193	minSlotNum, maxSlotNum *int64,
 194	bidderAddr *ethCommon.Address,
 195	limit *uint, order string,
 196) ([]BidAPI, uint64, error) {
 197	cancel, err := hdb.apiConnCon.Acquire()
 198	defer cancel()
 199	if err != nil {
 200		return nil, 0, tracerr.Wrap(err)
 201	}
 202	defer hdb.apiConnCon.Release()
 203	return hdb.getBestBidsAPI(hdb.dbRead, minSlotNum, maxSlotNum, bidderAddr, limit, order)
 204}
 205func (hdb *HistoryDB) getBestBidsAPI(
 206	d meddler.DB,
 207	minSlotNum, maxSlotNum *int64,
 208	bidderAddr *ethCommon.Address,
 209	limit *uint, order string,
 210) ([]BidAPI, uint64, error) {
 211	var query string
 212	var args []interface{}
 213	// JOIN the best bid of each slot with the latest update of each coordinator
 214	queryStr := `SELECT b.*, block.timestamp, coordinator.forger_addr, coordinator.url, 
 215	COUNT(*) OVER() AS total_items FROM (
 216	   SELECT slot_num, MAX(item_id) as maxitem 
 217	   FROM bid GROUP BY slot_num
 218	)
 219	AS x INNER JOIN bid AS b ON b.item_id = x.maxitem
 220	INNER JOIN block ON b.eth_block_num = block.eth_block_num
 221	INNER JOIN (
 222		SELECT bidder_addr, MAX(item_id) AS item_id FROM coordinator
 223		GROUP BY bidder_addr
 224	) c ON b.bidder_addr = c.bidder_addr 
 225	INNER JOIN coordinator ON c.item_id = coordinator.item_id 
 226	WHERE (b.slot_num >= ? AND b.slot_num <= ?)`
 227	args = append(args, minSlotNum)
 228	args = append(args, maxSlotNum)
 229	// Apply filters
 230	if bidderAddr != nil {
 231		queryStr += " AND b.bidder_addr = ? "
 232		args = append(args, bidderAddr)
 233	}
 234	queryStr += " ORDER BY b.slot_num "
 235	if order == OrderAsc {
 236		queryStr += "ASC "
 237	} else {
 238		queryStr += "DESC "
 239	}
 240	if limit != nil {
 241		queryStr += fmt.Sprintf("LIMIT %d;", *limit)
 242	}
 243	query = hdb.dbRead.Rebind(queryStr)
 244	bidPtrs := []*BidAPI{}
 245	if err := meddler.QueryAll(d, &bidPtrs, query, args...); err != nil {
 246		return nil, 0, tracerr.Wrap(err)
 247	}
 248	// log.Debug(query)
 249	bids := db.SlicePtrsToSlice(bidPtrs).([]BidAPI)
 250	if len(bids) == 0 {
 251		return bids, 0, nil
 252	}
 253	return bids, bids[0].TotalItems - uint64(len(bids)), nil
 254}
 255
 256// GetBidsAPI return the bids applying the given filters
 257func (hdb *HistoryDB) GetBidsAPI(
 258	slotNum *int64, bidderAddr *ethCommon.Address,
 259	fromItem, limit *uint, order string,
 260) ([]BidAPI, uint64, error) {
 261	cancel, err := hdb.apiConnCon.Acquire()
 262	defer cancel()
 263	if err != nil {
 264		return nil, 0, tracerr.Wrap(err)
 265	}
 266	defer hdb.apiConnCon.Release()
 267	var query string
 268	var args []interface{}
 269	// JOIN each bid with the latest update of each coordinator
 270	queryStr := `SELECT bid.*, block.timestamp, coord.forger_addr, coord.url, 
 271	COUNT(*) OVER() AS total_items
 272	FROM bid INNER JOIN block ON bid.eth_block_num = block.eth_block_num 
 273	INNER JOIN (
 274		SELECT bidder_addr, MAX(item_id) AS item_id FROM coordinator
 275		GROUP BY bidder_addr
 276	) c ON bid.bidder_addr = c.bidder_addr 
 277	INNER JOIN coordinator coord ON c.item_id = coord.item_id `
 278	// Apply filters
 279	nextIsAnd := false
 280	// slotNum filter
 281	if slotNum != nil {
 282		if nextIsAnd {
 283			queryStr += "AND "
 284		} else {
 285			queryStr += "WHERE "
 286		}
 287		queryStr += "bid.slot_num = ? "
 288		args = append(args, slotNum)
 289		nextIsAnd = true
 290	}
 291	// bidder filter
 292	if bidderAddr != nil {
 293		if nextIsAnd {
 294			queryStr += "AND "
 295		} else {
 296			queryStr += "WHERE "
 297		}
 298		queryStr += "bid.bidder_addr = ? "
 299		args = append(args, bidderAddr)
 300		nextIsAnd = true
 301	}
 302	if fromItem != nil {
 303		if nextIsAnd {
 304			queryStr += "AND "
 305		} else {
 306			queryStr += "WHERE "
 307		}
 308		if order == OrderAsc {
 309			queryStr += "bid.item_id >= ? "
 310		} else {
 311			queryStr += "bid.item_id <= ? "
 312		}
 313		args = append(args, fromItem)
 314	}
 315	// pagination
 316	queryStr += "ORDER BY bid.item_id "
 317	if order == OrderAsc {
 318		queryStr += "ASC "
 319	} else {
 320		queryStr += "DESC "
 321	}
 322	queryStr += fmt.Sprintf("LIMIT %d;", *limit)
 323	query, argsQ, err := sqlx.In(queryStr, args...)
 324	if err != nil {
 325		return nil, 0, tracerr.Wrap(err)
 326	}
 327	query = hdb.dbRead.Rebind(query)
 328	bids := []*BidAPI{}
 329	if err := meddler.QueryAll(hdb.dbRead, &bids, query, argsQ...); err != nil {
 330		return nil, 0, tracerr.Wrap(err)
 331	}
 332	if len(bids) == 0 {
 333		return []BidAPI{}, 0, nil
 334	}
 335	return db.SlicePtrsToSlice(bids).([]BidAPI), bids[0].TotalItems - uint64(len(bids)), nil
 336}
 337
 338// GetTokenAPI returns a token from the DB given a TokenID
 339func (hdb *HistoryDB) GetTokenAPI(tokenID common.TokenID) (*TokenWithUSD, error) {
 340	cancel, err := hdb.apiConnCon.Acquire()
 341	defer cancel()
 342	if err != nil {
 343		return nil, tracerr.Wrap(err)
 344	}
 345	defer hdb.apiConnCon.Release()
 346	return hdb.GetToken(tokenID)
 347}
 348
 349// GetTokensAPI returns a list of tokens from the DB
 350func (hdb *HistoryDB) GetTokensAPI(
 351	ids []common.TokenID, symbols []string, name string, fromItem,
 352	limit *uint, order string,
 353) ([]TokenWithUSD, uint64, error) {
 354	cancel, err := hdb.apiConnCon.Acquire()
 355	defer cancel()
 356	if err != nil {
 357		return nil, 0, tracerr.Wrap(err)
 358	}
 359	defer hdb.apiConnCon.Release()
 360	var query string
 361	var args []interface{}
 362	queryStr := `SELECT * , COUNT(*) OVER() AS total_items FROM token `
 363	// Apply filters
 364	nextIsAnd := false
 365	if len(ids) > 0 {
 366		queryStr += "WHERE token_id IN (?) "
 367		nextIsAnd = true
 368		args = append(args, ids)
 369	}
 370	if len(symbols) > 0 {
 371		if nextIsAnd {
 372			queryStr += "AND "
 373		} else {
 374			queryStr += "WHERE "
 375		}
 376		queryStr += "symbol IN (?) "
 377		args = append(args, symbols)
 378		nextIsAnd = true
 379	}
 380	if name != "" {
 381		if nextIsAnd {
 382			queryStr += "AND "
 383		} else {
 384			queryStr += "WHERE "
 385		}
 386		queryStr += "name ~ ? "
 387		args = append(args, name)
 388		nextIsAnd = true
 389	}
 390	if fromItem != nil {
 391		if nextIsAnd {
 392			queryStr += "AND "
 393		} else {
 394			queryStr += "WHERE "
 395		}
 396		if order == OrderAsc {
 397			queryStr += "item_id >= ? "
 398		} else {
 399			queryStr += "item_id <= ? "
 400		}
 401		args = append(args, fromItem)
 402	}
 403	// pagination
 404	queryStr += "ORDER BY item_id "
 405	if order == OrderAsc {
 406		queryStr += "ASC "
 407	} else {
 408		queryStr += "DESC "
 409	}
 410	queryStr += fmt.Sprintf("LIMIT %d;", *limit)
 411	query, argsQ, err := sqlx.In(queryStr, args...)
 412	if err != nil {
 413		return nil, 0, tracerr.Wrap(err)
 414	}
 415	query = hdb.dbRead.Rebind(query)
 416	tokens := []*TokenWithUSD{}
 417	if err := meddler.QueryAll(hdb.dbRead, &tokens, query, argsQ...); err != nil {
 418		return nil, 0, tracerr.Wrap(err)
 419	}
 420	if len(tokens) == 0 {
 421		return []TokenWithUSD{}, 0, nil
 422	}
 423	return db.SlicePtrsToSlice(tokens).([]TokenWithUSD), uint64(len(tokens)) - tokens[0].TotalItems, nil
 424}
 425
 426// GetTxAPI returns a tx from the DB given a TxID
 427func (hdb *HistoryDB) GetTxAPI(txID common.TxID) (*TxAPI, error) {
 428	// Warning: amount_success and deposit_amount_success have true as default for
 429	// performance reasons. The expected default value is false (when txs are unforged)
 430	// this case is handled at the function func (tx TxAPI) MarshalJSON() ([]byte, error)
 431	cancel, err := hdb.apiConnCon.Acquire()
 432	defer cancel()
 433	if err != nil {
 434		return nil, tracerr.Wrap(err)
 435	}
 436	defer hdb.apiConnCon.Release()
 437	tx := &TxAPI{}
 438	err = meddler.QueryRow(
 439		hdb.dbRead, tx, `SELECT tx.item_id, tx.is_l1, tx.id, tx.type, tx.position, 
 440		hez_idx(tx.effective_from_idx, token.symbol) AS from_idx, tx.from_eth_addr, tx.from_bjj,
 441		hez_idx(tx.to_idx, token.symbol) AS to_idx, tx.to_eth_addr, tx.to_bjj,
 442		tx.amount, tx.amount_success, tx.token_id, tx.amount_usd, 
 443		tx.batch_num, tx.eth_block_num, tx.to_forge_l1_txs_num, tx.user_origin, 
 444		tx.deposit_amount, tx.deposit_amount_usd, tx.deposit_amount_success, tx.fee, tx.fee_usd, tx.nonce,
 445		token.token_id, token.item_id AS token_item_id, token.eth_block_num AS token_block,
 446		token.eth_addr, token.name, token.symbol, token.decimals, token.usd,
 447		token.usd_update, block.timestamp
 448		FROM tx INNER JOIN token ON tx.token_id = token.token_id 
 449		INNER JOIN block ON tx.eth_block_num = block.eth_block_num 
 450		WHERE tx.id = $1;`, txID,
 451	)
 452	return tx, tracerr.Wrap(err)
 453}
 454
 455// GetTxsAPI returns a list of txs from the DB using the HistoryTx struct
 456// and pagination info
 457func (hdb *HistoryDB) GetTxsAPI(
 458	ethAddr *ethCommon.Address, bjj *babyjub.PublicKeyComp,
 459	tokenID *common.TokenID, idx *common.Idx, batchNum *uint, txType *common.TxType,
 460	fromItem, limit *uint, order string,
 461) ([]TxAPI, uint64, error) {
 462	// Warning: amount_success and deposit_amount_success have true as default for
 463	// performance reasons. The expected default value is false (when txs are unforged)
 464	// this case is handled at the function func (tx TxAPI) MarshalJSON() ([]byte, error)
 465	cancel, err := hdb.apiConnCon.Acquire()
 466	defer cancel()
 467	if err != nil {
 468		return nil, 0, tracerr.Wrap(err)
 469	}
 470	defer hdb.apiConnCon.Release()
 471	if ethAddr != nil && bjj != nil {
 472		return nil, 0, tracerr.Wrap(errors.New("ethAddr and bjj are incompatible"))
 473	}
 474	var query string
 475	var args []interface{}
 476	queryStr := `SELECT tx.item_id, tx.is_l1, tx.id, tx.type, tx.position, 
 477	hez_idx(tx.effective_from_idx, token.symbol) AS from_idx, tx.from_eth_addr, tx.from_bjj,
 478	hez_idx(tx.to_idx, token.symbol) AS to_idx, tx.to_eth_addr, tx.to_bjj,
 479	tx.amount, tx.amount_success, tx.token_id, tx.amount_usd, 
 480	tx.batch_num, tx.eth_block_num, tx.to_forge_l1_txs_num, tx.user_origin, 
 481	tx.deposit_amount, tx.deposit_amount_usd, tx.deposit_amount_success, tx.fee, tx.fee_usd, tx.nonce,
 482	token.token_id, token.item_id AS token_item_id, token.eth_block_num AS token_block,
 483	token.eth_addr, token.name, token.symbol, token.decimals, token.usd,
 484	token.usd_update, block.timestamp, count(*) OVER() AS total_items 
 485	FROM tx INNER JOIN token ON tx.token_id = token.token_id 
 486	INNER JOIN block ON tx.eth_block_num = block.eth_block_num `
 487	// Apply filters
 488	nextIsAnd := false
 489	// ethAddr filter
 490	if ethAddr != nil {
 491		queryStr += "WHERE (tx.from_eth_addr = ? OR tx.to_eth_addr = ?) "
 492		nextIsAnd = true
 493		args = append(args, ethAddr, ethAddr)
 494	} else if bjj != nil { // bjj filter
 495		queryStr += "WHERE (tx.from_bjj = ? OR tx.to_bjj = ?) "
 496		nextIsAnd = true
 497		args = append(args, bjj, bjj)
 498	}
 499	// tokenID filter
 500	if tokenID != nil {
 501		if nextIsAnd {
 502			queryStr += "AND "
 503		} else {
 504			queryStr += "WHERE "
 505		}
 506		queryStr += "tx.token_id = ? "
 507		args = append(args, tokenID)
 508		nextIsAnd = true
 509	}
 510	// idx filter
 511	if idx != nil {
 512		if nextIsAnd {
 513			queryStr += "AND "
 514		} else {
 515			queryStr += "WHERE "
 516		}
 517		queryStr += "(tx.effective_from_idx = ? OR tx.to_idx = ?) "
 518		args = append(args, idx, idx)
 519		nextIsAnd = true
 520	}
 521	// batchNum filter
 522	if batchNum != nil {
 523		if nextIsAnd {
 524			queryStr += "AND "
 525		} else {
 526			queryStr += "WHERE "
 527		}
 528		queryStr += "tx.batch_num = ? "
 529		args = append(args, batchNum)
 530		nextIsAnd = true
 531	}
 532	// txType filter
 533	if txType != nil {
 534		if nextIsAnd {
 535			queryStr += "AND "
 536		} else {
 537			queryStr += "WHERE "
 538		}
 539		queryStr += "tx.type = ? "
 540		args = append(args, txType)
 541		nextIsAnd = true
 542	}
 543	if fromItem != nil {
 544		if nextIsAnd {
 545			queryStr += "AND "
 546		} else {
 547			queryStr += "WHERE "
 548		}
 549		if order == OrderAsc {
 550			queryStr += "tx.item_id >= ? "
 551		} else {
 552			queryStr += "tx.item_id <= ? "
 553		}
 554		args = append(args, fromItem)
 555		nextIsAnd = true
 556	}
 557	if nextIsAnd {
 558		queryStr += "AND "
 559	} else {
 560		queryStr += "WHERE "
 561	}
 562	queryStr += "tx.batch_num IS NOT NULL "
 563
 564	// pagination
 565	queryStr += "ORDER BY tx.item_id "
 566	if order == OrderAsc {
 567		queryStr += " ASC "
 568	} else {
 569		queryStr += " DESC "
 570	}
 571	queryStr += fmt.Sprintf("LIMIT %d;", *limit)
 572	query = hdb.dbRead.Rebind(queryStr)
 573	// log.Debug(query)
 574	txsPtrs := []*TxAPI{}
 575	if err := meddler.QueryAll(hdb.dbRead, &txsPtrs, query, args...); err != nil {
 576		return nil, 0, tracerr.Wrap(err)
 577	}
 578	txs := db.SlicePtrsToSlice(txsPtrs).([]TxAPI)
 579	if len(txs) == 0 {
 580		return txs, 0, nil
 581	}
 582	return txs, txs[0].TotalItems - uint64(len(txs)), nil
 583}
 584
 585// GetExitAPI returns a exit from the DB
 586func (hdb *HistoryDB) GetExitAPI(batchNum *uint, idx *common.Idx) (*ExitAPI, error) {
 587	cancel, err := hdb.apiConnCon.Acquire()
 588	defer cancel()
 589	if err != nil {
 590		return nil, tracerr.Wrap(err)
 591	}
 592	defer hdb.apiConnCon.Release()
 593	exit := &ExitAPI{}
 594	err = meddler.QueryRow(
 595		hdb.dbRead, exit, `SELECT exit_tree.item_id, exit_tree.batch_num,
 596		hez_idx(exit_tree.account_idx, token.symbol) AS account_idx,
 597		account.bjj, account.eth_addr,
 598		exit_tree.merkle_proof, exit_tree.balance, exit_tree.instant_withdrawn,
 599		exit_tree.delayed_withdraw_request, exit_tree.delayed_withdrawn,
 600		token.token_id, token.item_id AS token_item_id, 
 601		token.eth_block_num AS token_block, token.eth_addr AS token_eth_addr, token.name, token.symbol, 
 602		token.decimals, token.usd, token.usd_update
 603		FROM exit_tree INNER JOIN account ON exit_tree.account_idx = account.idx 
 604		INNER JOIN token ON account.token_id = token.token_id 
 605		WHERE exit_tree.batch_num = $1 AND exit_tree.account_idx = $2;`, batchNum, idx,
 606	)
 607	return exit, tracerr.Wrap(err)
 608}
 609
 610// GetExitsAPI returns a list of exits from the DB and pagination info
 611func (hdb *HistoryDB) GetExitsAPI(
 612	ethAddr *ethCommon.Address, bjj *babyjub.PublicKeyComp, tokenID *common.TokenID,
 613	idx *common.Idx, batchNum *uint, onlyPendingWithdraws *bool,
 614	fromItem, limit *uint, order string,
 615) ([]ExitAPI, uint64, error) {
 616	if ethAddr != nil && bjj != nil {
 617		return nil, 0, tracerr.Wrap(errors.New("ethAddr and bjj are incompatible"))
 618	}
 619	cancel, err := hdb.apiConnCon.Acquire()
 620	defer cancel()
 621	if err != nil {
 622		return nil, 0, tracerr.Wrap(err)
 623	}
 624	defer hdb.apiConnCon.Release()
 625	var query string
 626	var args []interface{}
 627	queryStr := `SELECT exit_tree.item_id, exit_tree.batch_num,
 628	hez_idx(exit_tree.account_idx, token.symbol) AS account_idx,
 629	account.bjj, account.eth_addr,
 630	exit_tree.merkle_proof, exit_tree.balance, exit_tree.instant_withdrawn,
 631	exit_tree.delayed_withdraw_request, exit_tree.delayed_withdrawn,
 632	token.token_id, token.item_id AS token_item_id,
 633	token.eth_block_num AS token_block, token.eth_addr AS token_eth_addr, token.name, token.symbol,
 634	token.decimals, token.usd, token.usd_update, COUNT(*) OVER() AS total_items
 635	FROM exit_tree INNER JOIN account ON exit_tree.account_idx = account.idx 
 636	INNER JOIN token ON account.token_id = token.token_id `
 637	// Apply filters
 638	nextIsAnd := false
 639	// ethAddr filter
 640	if ethAddr != nil {
 641		queryStr += "WHERE account.eth_addr = ? "
 642		nextIsAnd = true
 643		args = append(args, ethAddr)
 644	} else if bjj != nil { // bjj filter
 645		queryStr += "WHERE account.bjj = ? "
 646		nextIsAnd = true
 647		args = append(args, bjj)
 648	}
 649	// tokenID filter
 650	if tokenID != nil {
 651		if nextIsAnd {
 652			queryStr += "AND "
 653		} else {
 654			queryStr += "WHERE "
 655		}
 656		queryStr += "account.token_id = ? "
 657		args = append(args, tokenID)
 658		nextIsAnd = true
 659	}
 660	// idx filter
 661	if idx != nil {
 662		if nextIsAnd {
 663			queryStr += "AND "
 664		} else {
 665			queryStr += "WHERE "
 666		}
 667		queryStr += "exit_tree.account_idx = ? "
 668		args = append(args, idx)
 669		nextIsAnd = true
 670	}
 671	// batchNum filter
 672	if batchNum != nil {
 673		if nextIsAnd {
 674			queryStr += "AND "
 675		} else {
 676			queryStr += "WHERE "
 677		}
 678		queryStr += "exit_tree.batch_num = ? "
 679		args = append(args, batchNum)
 680		nextIsAnd = true
 681	}
 682	// onlyPendingWithdraws
 683	if onlyPendingWithdraws != nil {
 684		if *onlyPendingWithdraws {
 685			if nextIsAnd {
 686				queryStr += "AND "
 687			} else {
 688				queryStr += "WHERE "
 689			}
 690			queryStr += "(exit_tree.instant_withdrawn IS NULL AND exit_tree.delayed_withdrawn IS NULL) "
 691			nextIsAnd = true
 692		}
 693	}
 694	if fromItem != nil {
 695		if nextIsAnd {
 696			queryStr += "AND "
 697		} else {
 698			queryStr += "WHERE "
 699		}
 700		if order == OrderAsc {
 701			queryStr += "exit_tree.item_id >= ? "
 702		} else {
 703			queryStr += "exit_tree.item_id <= ? "
 704		}
 705		args = append(args, fromItem)
 706		// nextIsAnd = true
 707	}
 708	// pagination
 709	queryStr += "ORDER BY exit_tree.item_id "
 710	if order == OrderAsc {
 711		queryStr += " ASC "
 712	} else {
 713		queryStr += " DESC "
 714	}
 715	queryStr += fmt.Sprintf("LIMIT %d;", *limit)
 716	query = hdb.dbRead.Rebind(queryStr)
 717	// log.Debug(query)
 718	exits := []*ExitAPI{}
 719	if err := meddler.QueryAll(hdb.dbRead, &exits, query, args...); err != nil {
 720		return nil, 0, tracerr.Wrap(err)
 721	}
 722	if len(exits) == 0 {
 723		return []ExitAPI{}, 0, nil
 724	}
 725	return db.SlicePtrsToSlice(exits).([]ExitAPI), exits[0].TotalItems - uint64(len(exits)), nil
 726}
 727
 728// GetCoordinatorsAPI returns a list of coordinators from the DB and pagination info
 729func (hdb *HistoryDB) GetCoordinatorsAPI(
 730	bidderAddr, forgerAddr *ethCommon.Address,
 731	fromItem, limit *uint, order string,
 732) ([]CoordinatorAPI, uint64, error) {
 733	cancel, err := hdb.apiConnCon.Acquire()
 734	defer cancel()
 735	if err != nil {
 736		return nil, 0, tracerr.Wrap(err)
 737	}
 738	defer hdb.apiConnCon.Release()
 739	var query string
 740	var args []interface{}
 741	queryStr := `SELECT coordinator.*, COUNT(*) OVER() AS total_items
 742	FROM coordinator INNER JOIN (
 743		SELECT MAX(item_id) AS item_id FROM coordinator
 744		GROUP BY bidder_addr
 745	) c ON coordinator.item_id = c.item_id `
 746	// Apply filters
 747	nextIsAnd := false
 748	if bidderAddr != nil {
 749		queryStr += "WHERE bidder_addr = ? "
 750		nextIsAnd = true
 751		args = append(args, bidderAddr)
 752	}
 753	if forgerAddr != nil {
 754		if nextIsAnd {
 755			queryStr += "AND "
 756		} else {
 757			queryStr += "WHERE "
 758		}
 759		queryStr += "forger_addr = ? "
 760		nextIsAnd = true
 761		args = append(args, forgerAddr)
 762	}
 763	if fromItem != nil {
 764		if nextIsAnd {
 765			queryStr += "AND "
 766		} else {
 767			queryStr += "WHERE "
 768		}
 769		if order == OrderAsc {
 770			queryStr += "coordinator.item_id >= ? "
 771		} else {
 772			queryStr += "coordinator.item_id <= ? "
 773		}
 774		args = append(args, fromItem)
 775	}
 776	// pagination
 777	queryStr += "ORDER BY coordinator.item_id "
 778	if order == OrderAsc {
 779		queryStr += " ASC "
 780	} else {
 781		queryStr += " DESC "
 782	}
 783	queryStr += fmt.Sprintf("LIMIT %d;", *limit)
 784	query = hdb.dbRead.Rebind(queryStr)
 785
 786	coordinators := []*CoordinatorAPI{}
 787	if err := meddler.QueryAll(hdb.dbRead, &coordinators, query, args...); err != nil {
 788		return nil, 0, tracerr.Wrap(err)
 789	}
 790	if len(coordinators) == 0 {
 791		return []CoordinatorAPI{}, 0, nil
 792	}
 793	return db.SlicePtrsToSlice(coordinators).([]CoordinatorAPI),
 794		coordinators[0].TotalItems - uint64(len(coordinators)), nil
 795}
 796
 797// GetAuctionVarsAPI returns auction variables
 798func (hdb *HistoryDB) GetAuctionVarsAPI() (*common.AuctionVariables, error) {
 799	cancel, err := hdb.apiConnCon.Acquire()
 800	defer cancel()
 801	if err != nil {
 802		return nil, tracerr.Wrap(err)
 803	}
 804	defer hdb.apiConnCon.Release()
 805	auctionVars := &common.AuctionVariables{}
 806	err = meddler.QueryRow(
 807		hdb.dbRead, auctionVars, `SELECT * FROM auction_vars;`,
 808	)
 809	return auctionVars, tracerr.Wrap(err)
 810}
 811
 812// GetAccountAPI returns an account by its index
 813func (hdb *HistoryDB) GetAccountAPI(idx common.Idx) (*AccountAPI, error) {
 814	cancel, err := hdb.apiConnCon.Acquire()
 815	defer cancel()
 816	if err != nil {
 817		return nil, tracerr.Wrap(err)
 818	}
 819	defer hdb.apiConnCon.Release()
 820	account := &AccountAPI{}
 821	err = meddler.QueryRow(hdb.dbRead, account, `SELECT account.item_id, hez_idx(account.idx, 
 822		token.symbol) as idx, account.batch_num, account.bjj, account.eth_addr,
 823		token.token_id, token.item_id AS token_item_id, token.eth_block_num AS token_block,
 824		token.eth_addr as token_eth_addr, token.name, token.symbol, token.decimals, token.usd,
 825		token.usd_update, account_update.nonce, account_update.balance 
 826		FROM account inner JOIN (
 827			SELECT idx, nonce, balance 
 828			FROM account_update
 829			WHERE idx = $1
 830			ORDER BY item_id DESC LIMIT 1
 831		) AS account_update ON account_update.idx = account.idx 
 832		INNER JOIN token ON account.token_id = token.token_id
 833		WHERE account.idx = $1;`, idx)
 834
 835	if err != nil {
 836		return nil, tracerr.Wrap(err)
 837	}
 838
 839	return account, nil
 840}
 841
 842// GetAccountsAPI returns a list of accounts from the DB and pagination info
 843func (hdb *HistoryDB) GetAccountsAPI(
 844	tokenIDs []common.TokenID, ethAddr *ethCommon.Address,
 845	bjj *babyjub.PublicKeyComp, fromItem, limit *uint, order string,
 846) ([]AccountAPI, uint64, error) {
 847	if ethAddr != nil && bjj != nil {
 848		return nil, 0, tracerr.Wrap(errors.New("ethAddr and bjj are incompatible"))
 849	}
 850	cancel, err := hdb.apiConnCon.Acquire()
 851	defer cancel()
 852	if err != nil {
 853		return nil, 0, tracerr.Wrap(err)
 854	}
 855	defer hdb.apiConnCon.Release()
 856	var query string
 857	var args []interface{}
 858	queryStr := `SELECT account.item_id, hez_idx(account.idx, token.symbol) as idx, account.batch_num, 
 859	account.bjj, account.eth_addr, token.token_id, token.item_id AS token_item_id, token.eth_block_num AS token_block,
 860	token.eth_addr as token_eth_addr, token.name, token.symbol, token.decimals, token.usd, token.usd_update, 
 861	account_update.nonce, account_update.balance, COUNT(*) OVER() AS total_items
 862	FROM account inner JOIN (
 863		SELECT DISTINCT idx,
 864		first_value(nonce) over(partition by idx ORDER BY item_id DESC) as nonce,
 865		first_value(balance) over(partition by idx ORDER BY item_id DESC) as balance
 866		FROM account_update
 867	) AS account_update ON account_update.idx = account.idx INNER JOIN token ON account.token_id = token.token_id `
 868	// Apply filters
 869	nextIsAnd := false
 870	// ethAddr filter
 871	if ethAddr != nil {
 872		queryStr += "WHERE account.eth_addr = ? "
 873		nextIsAnd = true
 874		args = append(args, ethAddr)
 875	} else if bjj != nil { // bjj filter
 876		queryStr += "WHERE account.bjj = ? "
 877		nextIsAnd = true
 878		args = append(args, bjj)
 879	}
 880	// tokenID filter
 881	if len(tokenIDs) > 0 {
 882		if nextIsAnd {
 883			queryStr += "AND "
 884		} else {
 885			queryStr += "WHERE "
 886		}
 887		queryStr += "account.token_id IN (?) "
 888		args = append(args, tokenIDs)
 889		nextIsAnd = true
 890	}
 891	if fromItem != nil {
 892		if nextIsAnd {
 893			queryStr += "AND "
 894		} else {
 895			queryStr += "WHERE "
 896		}
 897		if order == OrderAsc {
 898			queryStr += "account.item_id >= ? "
 899		} else {
 900			queryStr += "account.item_id <= ? "
 901		}
 902		args = append(args, fromItem)
 903	}
 904	// pagination
 905	queryStr += "ORDER BY account.item_id "
 906	if order == OrderAsc {
 907		queryStr += " ASC "
 908	} else {
 909		queryStr += " DESC "
 910	}
 911	queryStr += fmt.Sprintf("LIMIT %d;", *limit)
 912	query, argsQ, err := sqlx.In(queryStr, args...)
 913	if err != nil {
 914		return nil, 0, tracerr.Wrap(err)
 915	}
 916	query = hdb.dbRead.Rebind(query)
 917
 918	accounts := []*AccountAPI{}
 919	if err := meddler.QueryAll(hdb.dbRead, &accounts, query, argsQ...); err != nil {
 920		return nil, 0, tracerr.Wrap(err)
 921	}
 922	if len(accounts) == 0 {
 923		return []AccountAPI{}, 0, nil
 924	}
 925
 926	return db.SlicePtrsToSlice(accounts).([]AccountAPI),
 927		accounts[0].TotalItems - uint64(len(accounts)), nil
 928}
 929
 930// GetCommonAccountAPI returns the account associated to an account idx
 931func (hdb *HistoryDB) GetCommonAccountAPI(idx common.Idx) (*common.Account, error) {
 932	cancel, err := hdb.apiConnCon.Acquire()
 933	defer cancel()
 934	if err != nil {
 935		return nil, tracerr.Wrap(err)
 936	}
 937	defer hdb.apiConnCon.Release()
 938	account := &common.Account{}
 939	err = meddler.QueryRow(
 940		hdb.dbRead, account, `SELECT idx, token_id, batch_num, bjj, eth_addr
 941		FROM account WHERE idx = $1;`, idx,
 942	)
 943	return account, tracerr.Wrap(err)
 944}
 945
 946// GetCoordinatorAPI returns a coordinator by its bidderAddr
 947func (hdb *HistoryDB) GetCoordinatorAPI(bidderAddr ethCommon.Address) (*CoordinatorAPI, error) {
 948	cancel, err := hdb.apiConnCon.Acquire()
 949	defer cancel()
 950	if err != nil {
 951		return nil, tracerr.Wrap(err)
 952	}
 953	defer hdb.apiConnCon.Release()
 954	return hdb.getCoordinatorAPI(hdb.dbRead, bidderAddr)
 955}
 956
 957func (hdb *HistoryDB) getCoordinatorAPI(d meddler.DB, bidderAddr ethCommon.Address) (*CoordinatorAPI, error) {
 958	coordinator := &CoordinatorAPI{}
 959	err := meddler.QueryRow(
 960		d, coordinator,
 961		"SELECT * FROM coordinator WHERE bidder_addr = $1 ORDER BY item_id DESC LIMIT 1;",
 962		bidderAddr,
 963	)
 964	return coordinator, tracerr.Wrap(err)
 965}
 966
 967// GetNodeInfoAPI retusnt he NodeInfo
 968func (hdb *HistoryDB) GetNodeInfoAPI() (*NodeInfo, error) {
 969	cancel, err := hdb.apiConnCon.Acquire()
 970	defer cancel()
 971	if err != nil {
 972		return nil, tracerr.Wrap(err)
 973	}
 974	defer hdb.apiConnCon.Release()
 975	return hdb.GetNodeInfo()
 976}
 977
 978// GetBucketUpdatesInternalAPI returns the latest bucket updates
 979func (hdb *HistoryDB) GetBucketUpdatesInternalAPI() ([]BucketUpdateAPI, error) {
 980	var bucketUpdates []*BucketUpdateAPI
 981	err := meddler.QueryAll(
 982		hdb.dbRead, &bucketUpdates,
 983		`SELECT num_bucket, withdrawals FROM bucket_update 
 984			WHERE item_id in(SELECT max(item_id) FROM bucket_update 
 985			group by num_bucket) 
 986			ORDER BY num_bucket ASC;`,
 987	)
 988	return db.SlicePtrsToSlice(bucketUpdates).([]BucketUpdateAPI), tracerr.Wrap(err)
 989}
 990
 991// GetNextForgersInternalAPI returns next forgers
 992func (hdb *HistoryDB) GetNextForgersInternalAPI(auctionVars *common.AuctionVariables,
 993	auctionConsts *common.AuctionConstants,
 994	lastBlock common.Block, currentSlot, lastClosedSlot int64) ([]NextForgerAPI, error) {
 995	secondsPerBlock := int64(15) //nolint:gomnd
 996	// currentSlot and lastClosedSlot included
 997	limit := uint(lastClosedSlot - currentSlot + 1)
 998	bids, _, err := hdb.getBestBidsAPI(hdb.dbRead, &currentSlot, &lastClosedSlot, nil, &limit, "ASC")
 999	if err != nil && tracerr.Unwrap(err) != sql.ErrNoRows {
1000		return nil, tracerr.Wrap(err)
1001	}
1002	nextForgers := []NextForgerAPI{}
1003	// Get min bid info
1004	var minBidInfo []MinBidInfo
1005	if currentSlot >= auctionVars.DefaultSlotSetBidSlotNum {
1006		// All min bids can be calculated with the last update of AuctionVariables
1007
1008		minBidInfo = []MinBidInfo{{
1009			DefaultSlotSetBid:        auctionVars.DefaultSlotSetBid,
1010			DefaultSlotSetBidSlotNum: auctionVars.DefaultSlotSetBidSlotNum,
1011		}}
1012	} else {
1013		// Get all the relevant updates from the DB
1014		minBidInfo, err = hdb.getMinBidInfo(hdb.dbRead, currentSlot, lastClosedSlot)
1015		if err != nil {
1016			return nil, tracerr.Wrap(err)
1017		}
1018	}
1019	// Create nextForger for each slot
1020	for i := currentSlot; i <= lastClosedSlot; i++ {
1021		fromBlock := i*int64(auctionConsts.BlocksPerSlot) +
1022			auctionConsts.GenesisBlockNum
1023		toBlock := (i+1)*int64(auctionConsts.BlocksPerSlot) +
1024			auctionConsts.GenesisBlockNum - 1
1025		nextForger := NextForgerAPI{
1026			Period: Period{
1027				SlotNum:   i,
1028				FromBlock: fromBlock,
1029				ToBlock:   toBlock,
1030				FromTimestamp: lastBlock.Timestamp.Add(time.Second *
1031					time.Duration(secondsPerBlock*(fromBlock-lastBlock.Num))),
1032				ToTimestamp: lastBlock.Timestamp.Add(time.Second *
1033					time.Duration(secondsPerBlock*(toBlock-lastBlock.Num))),
1034			},
1035		}
1036		foundForger := false
1037		// If there is a bid for a slot, get forger (coordinator)
1038		for j := range bids {
1039			slotNum := bids[j].SlotNum
1040			if slotNum == i {
1041				// There's a bid for the slot
1042				// Check if the bid is greater than the minimum required
1043				for i := 0; i < len(minBidInfo); i++ {
1044					// Find the most recent update
1045					if slotNum >= minBidInfo[i].DefaultSlotSetBidSlotNum {
1046						// Get min bid
1047						minBidSelector := slotNum % int64(len(auctionVars.DefaultSlotSetBid))
1048						minBid := minBidInfo[i].DefaultSlotSetBid[minBidSelector]
1049						// Check if the bid has beaten the minimum
1050						bid, ok := new(big.Int).SetString(string(bids[j].BidValue), 10)
1051						if !ok {
1052							return nil, tracerr.New("Wrong bid value, error parsing it as big.Int")
1053						}
1054						if minBid.Cmp(bid) == 1 {
1055							// Min bid is greater than bid, the slot will be forged by boot coordinator
1056							break
1057						}
1058						foundForger = true
1059						break
1060					}
1061				}
1062				if !foundForger { // There is no bid or it's smaller than the minimum
1063					break
1064				}
1065				coordinator, err := hdb.getCoordinatorAPI(hdb.dbRead, bids[j].Bidder)
1066				if err != nil {
1067					return nil, tracerr.Wrap(err)
1068				}
1069				nextForger.Coordinator = *coordinator
1070				break
1071			}
1072		}
1073		// If there is no bid, the coordinator that will forge is boot coordinator
1074		if !foundForger {
1075			nextForger.Coordinator = CoordinatorAPI{
1076				Forger: auctionVars.BootCoordinator,
1077				URL:    auctionVars.BootCoordinatorURL,
1078			}
1079		}
1080		nextForgers = append(nextForgers, nextForger)
1081	}
1082	return nextForgers, nil
1083}
1084
1085// GetMetricsInternalAPI returns the MetricsAPI
1086func (hdb *HistoryDB) GetMetricsInternalAPI(lastBatchNum common.BatchNum) (*MetricsAPI, error) {
1087	var metrics MetricsAPI
1088	// Get the first and last batch of the last 24h and their timestamps
1089	// if u.state.Network.LastBatch == nil {
1090	// 	return &metrics, nil
1091	// }
1092	type period struct {
1093		FromBatchNum  common.BatchNum `meddler:"from_batch_num"`
1094		FromTimestamp time.Time       `meddler:"from_timestamp"`
1095		ToBatchNum    common.BatchNum `meddler:"-"`
1096		ToTimestamp   time.Time       `meddler:"to_timestamp"`
1097	}
1098	p := &period{
1099		ToBatchNum: lastBatchNum,
1100	}
1101	if err := meddler.QueryRow(
1102		hdb.dbRead, p, `SELECT
1103			COALESCE (MIN(batch.batch_num), 0) as from_batch_num,
1104			COALESCE (MIN(block.timestamp), NOW()) AS from_timestamp, 
1105			COALESCE (MAX(block.timestamp), NOW()) AS to_timestamp
1106			FROM batch INNER JOIN block ON batch.eth_block_num = block.eth_block_num
1107			WHERE block.timestamp >= NOW() - INTERVAL '24 HOURS';`,
1108	); err != nil {
1109		return nil, tracerr.Wrap(err)
1110	}
1111	// Get the amount of txs of that period
1112	row := hdb.dbRead.QueryRow(
1113		`SELECT COUNT(*) as total_txs FROM tx WHERE tx.batch_num between $1 AND $2;`,
1114		p.FromBatchNum, p.ToBatchNum,
1115	)
1116	var nTxs int
1117	if err := row.Scan(&nTxs); err != nil {
1118		return nil, tracerr.Wrap(err)
1119	}
1120	// Set txs/s
1121	seconds := p.ToTimestamp.Sub(p.FromTimestamp).Seconds()
1122	if seconds == 0 { // Avoid dividing by 0
1123		seconds++
1124	}
1125	metrics.TransactionsPerSecond = float64(nTxs) / seconds
1126	// Set txs/batch
1127	nBatches := p.ToBatchNum - p.FromBatchNum + 1
1128	if nBatches == 0 { // Avoid dividing by 0
1129		nBatches++
1130	}
1131	if (p.ToBatchNum - p.FromBatchNum) > 0 {
1132		fmt.Printf("DBG ntxs: %v, nBatches: %v\n", nTxs, nBatches)
1133		metrics.TransactionsPerBatch = float64(nTxs) /
1134			float64(nBatches)
1135	} else {
1136		metrics.TransactionsPerBatch = 0
1137	}
1138	// Get total fee of that period
1139	row = hdb.dbRead.QueryRow(
1140		`SELECT COALESCE (SUM(total_fees_usd), 0) FROM batch WHERE batch_num between $1 AND $2;`,
1141		p.FromBatchNum, p.ToBatchNum,
1142	)
1143	var totalFee float64
1144	if err := row.Scan(&totalFee); err != nil {
1145		return nil, tracerr.Wrap(err)
1146	}
1147	// Set batch frequency
1148	metrics.BatchFrequency = seconds / float64(nBatches)
1149	// Set avg transaction fee (only L2 txs have fee)
1150	row = hdb.dbRead.QueryRow(
1151		`SELECT COUNT(*) as total_txs FROM tx WHERE tx.batch_num between $1 AND $2 AND NOT is_l1;`,
1152		p.FromBatchNum, p.ToBatchNum,
1153	)
1154	var nL2Txs int
1155	if err := row.Scan(&nL2Txs); err != nil {
1156		return nil, tracerr.Wrap(err)
1157	}
1158	if nL2Txs > 0 {
1159		metrics.AvgTransactionFee = totalFee / float64(nL2Txs)
1160	} else {
1161		metrics.AvgTransactionFee = 0
1162	}
1163	// Get and set amount of registered accounts
1164	type registeredAccounts struct {
1165		TotalIdx int64 `meddler:"total_idx"`
1166		TotalBJJ int64 `meddler:"total_bjj"`
1167	}
1168	ra := &registeredAccounts{}
1169	if err := meddler.QueryRow(
1170		hdb.dbRead, ra,
1171		`SELECT COUNT(*) AS total_bjj, COUNT(DISTINCT(bjj)) AS total_idx FROM account;`,
1172	); err != nil {
1173		return nil, tracerr.Wrap(err)
1174	}
1175	metrics.TotalAccounts = ra.TotalIdx
1176	metrics.TotalBJJs = ra.TotalBJJ
1177	// Get and set estimated time to forge L1 tx
1178	row = hdb.dbRead.QueryRow(
1179		`SELECT COALESCE (AVG(EXTRACT(EPOCH FROM (forged.timestamp - added.timestamp))), 0) FROM tx
1180			INNER JOIN block AS added ON tx.eth_block_num = added.eth_block_num
1181			INNER JOIN batch AS forged_batch ON tx.batch_num = forged_batch.batch_num
1182			INNER JOIN block AS forged ON forged_batch.eth_block_num = forged.eth_block_num
1183			WHERE tx.batch_num between $1 and $2 AND tx.is_l1 AND tx.user_origin;`,
1184		p.FromBatchNum, p.ToBatchNum,
1185	)
1186	var timeToForgeL1 float64
1187	if err := row.Scan(&timeToForgeL1); err != nil {
1188		return nil, tracerr.Wrap(err)
1189	}
1190	metrics.EstimatedTimeToForgeL1 = timeToForgeL1
1191	return &metrics, nil
1192}
1193
1194// GetStateAPI returns the StateAPI
1195func (hdb *HistoryDB) GetStateAPI() (*StateAPI, error) {
1196	cancel, err := hdb.apiConnCon.Acquire()
1197	defer cancel()
1198	if err != nil {
1199		return nil, tracerr.Wrap(err)
1200	}
1201	defer hdb.apiConnCon.Release()
1202	return hdb.getStateAPI(hdb.dbRead)
1203}