arnaucube
arnaucube/hermez-nodePublic Self-hosted repository
hermez-node/api/dbtoapistructs.go
api/dbtoapistructs.goRaw
  1package api
  2
  3import (
  4	"encoding/base64"
  5	"errors"
  6	"fmt"
  7	"math/big"
  8	"strconv"
  9	"time"
 10
 11	ethCommon "github.com/ethereum/go-ethereum/common"
 12	"github.com/hermeznetwork/hermez-node/common"
 13	"github.com/hermeznetwork/hermez-node/db"
 14	"github.com/hermeznetwork/hermez-node/db/historydb"
 15	"github.com/hermeznetwork/hermez-node/db/l2db"
 16	"github.com/hermeznetwork/hermez-node/eth"
 17	"github.com/iden3/go-iden3-crypto/babyjub"
 18)
 19
 20const exitIdx = "hez:EXIT:1"
 21
 22type errorMsg struct {
 23	Message string
 24}
 25
 26func bjjToString(bjj *babyjub.PublicKey) string {
 27	pkComp := [32]byte(bjj.Compress())
 28	sum := pkComp[0]
 29	for i := 1; i < len(pkComp); i++ {
 30		sum += pkComp[i]
 31	}
 32	bjjSum := append(pkComp[:], sum)
 33	return "hez:" + base64.RawURLEncoding.EncodeToString(bjjSum)
 34}
 35
 36func ethAddrToHez(addr ethCommon.Address) string {
 37	return "hez:" + addr.String()
 38}
 39
 40func idxToHez(idx common.Idx, tokenSymbol string) string {
 41	if idx == 1 {
 42		return exitIdx
 43	}
 44	return "hez:" + tokenSymbol + ":" + strconv.Itoa(int(idx))
 45}
 46
 47// History Tx
 48
 49type historyTxsAPI struct {
 50	Txs        []historyTxAPI `json:"transactions"`
 51	Pagination *db.Pagination `json:"pagination"`
 52}
 53
 54func (htx *historyTxsAPI) GetPagination() *db.Pagination {
 55	if htx.Txs[0].ItemID < htx.Txs[len(htx.Txs)-1].ItemID {
 56		htx.Pagination.FirstReturnedItem = htx.Txs[0].ItemID
 57		htx.Pagination.LastReturnedItem = htx.Txs[len(htx.Txs)-1].ItemID
 58	} else {
 59		htx.Pagination.LastReturnedItem = htx.Txs[0].ItemID
 60		htx.Pagination.FirstReturnedItem = htx.Txs[len(htx.Txs)-1].ItemID
 61	}
 62	return htx.Pagination
 63}
 64func (htx *historyTxsAPI) Len() int { return len(htx.Txs) }
 65
 66type l1Info struct {
 67	ToForgeL1TxsNum       *int64   `json:"toForgeL1TransactionsNum"`
 68	UserOrigin            bool     `json:"userOrigin"`
 69	FromEthAddr           string   `json:"fromHezEthereumAddress"`
 70	FromBJJ               string   `json:"fromBJJ"`
 71	LoadAmount            string   `json:"loadAmount"`
 72	HistoricLoadAmountUSD *float64 `json:"historicLoadAmountUSD"`
 73	EthBlockNum           int64    `json:"ethereumBlockNum"`
 74}
 75
 76type l2Info struct {
 77	Fee            common.FeeSelector `json:"fee"`
 78	HistoricFeeUSD *float64           `json:"historicFeeUSD"`
 79	Nonce          common.Nonce       `json:"nonce"`
 80}
 81
 82type historyTxAPI struct {
 83	IsL1        string           `json:"L1orL2"`
 84	TxID        common.TxID      `json:"id"`
 85	ItemID      int              `json:"itemId"`
 86	Type        common.TxType    `json:"type"`
 87	Position    int              `json:"position"`
 88	FromIdx     *string          `json:"fromAccountIndex"`
 89	ToIdx       string           `json:"toAccountIndex"`
 90	Amount      string           `json:"amount"`
 91	BatchNum    *common.BatchNum `json:"batchNum"`
 92	HistoricUSD *float64         `json:"historicUSD"`
 93	Timestamp   time.Time        `json:"timestamp"`
 94	L1Info      *l1Info          `json:"L1Info"`
 95	L2Info      *l2Info          `json:"L2Info"`
 96	Token       tokenAPI         `json:"token"`
 97}
 98
 99func historyTxsToAPI(dbTxs []historydb.HistoryTx) []historyTxAPI {
100	apiTxs := []historyTxAPI{}
101	for i := 0; i < len(dbTxs); i++ {
102		apiTx := historyTxAPI{
103			TxID:        dbTxs[i].TxID,
104			ItemID:      dbTxs[i].ItemID,
105			Type:        dbTxs[i].Type,
106			Position:    dbTxs[i].Position,
107			ToIdx:       idxToHez(dbTxs[i].ToIdx, dbTxs[i].TokenSymbol),
108			Amount:      dbTxs[i].Amount.String(),
109			HistoricUSD: dbTxs[i].HistoricUSD,
110			BatchNum:    dbTxs[i].BatchNum,
111			Timestamp:   dbTxs[i].Timestamp,
112			Token: tokenAPI{
113				TokenID:     dbTxs[i].TokenID,
114				EthBlockNum: dbTxs[i].TokenEthBlockNum,
115				EthAddr:     dbTxs[i].TokenEthAddr,
116				Name:        dbTxs[i].TokenName,
117				Symbol:      dbTxs[i].TokenSymbol,
118				Decimals:    dbTxs[i].TokenDecimals,
119				USD:         dbTxs[i].TokenUSD,
120				USDUpdate:   dbTxs[i].TokenUSDUpdate,
121			},
122			L1Info: nil,
123			L2Info: nil,
124		}
125		if dbTxs[i].FromIdx != nil {
126			fromIdx := new(string)
127			*fromIdx = idxToHez(*dbTxs[i].FromIdx, dbTxs[i].TokenSymbol)
128			apiTx.FromIdx = fromIdx
129		}
130		if dbTxs[i].IsL1 {
131			apiTx.IsL1 = "L1"
132			apiTx.L1Info = &l1Info{
133				ToForgeL1TxsNum:       dbTxs[i].ToForgeL1TxsNum,
134				UserOrigin:            *dbTxs[i].UserOrigin,
135				FromEthAddr:           ethAddrToHez(*dbTxs[i].FromEthAddr),
136				FromBJJ:               bjjToString(dbTxs[i].FromBJJ),
137				LoadAmount:            dbTxs[i].LoadAmount.String(),
138				HistoricLoadAmountUSD: dbTxs[i].HistoricLoadAmountUSD,
139				EthBlockNum:           dbTxs[i].EthBlockNum,
140			}
141		} else {
142			apiTx.IsL1 = "L2"
143			apiTx.L2Info = &l2Info{
144				Fee:            *dbTxs[i].Fee,
145				HistoricFeeUSD: dbTxs[i].HistoricFeeUSD,
146				Nonce:          *dbTxs[i].Nonce,
147			}
148		}
149		apiTxs = append(apiTxs, apiTx)
150	}
151	return apiTxs
152}
153
154// Exit
155
156type exitsAPI struct {
157	Exits      []exitAPI      `json:"exits"`
158	Pagination *db.Pagination `json:"pagination"`
159}
160
161func (e *exitsAPI) GetPagination() *db.Pagination {
162	if e.Exits[0].ItemID < e.Exits[len(e.Exits)-1].ItemID {
163		e.Pagination.FirstReturnedItem = e.Exits[0].ItemID
164		e.Pagination.LastReturnedItem = e.Exits[len(e.Exits)-1].ItemID
165	} else {
166		e.Pagination.LastReturnedItem = e.Exits[0].ItemID
167		e.Pagination.FirstReturnedItem = e.Exits[len(e.Exits)-1].ItemID
168	}
169	return e.Pagination
170}
171func (e *exitsAPI) Len() int { return len(e.Exits) }
172
173type merkleProofAPI struct {
174	Root     string
175	Siblings []string
176	OldKey   string
177	OldValue string
178	IsOld0   bool
179	Key      string
180	Value    string
181	Fnc      int
182}
183
184type exitAPI struct {
185	ItemID                 int             `json:"itemId"`
186	BatchNum               common.BatchNum `json:"batchNum"`
187	AccountIdx             string          `json:"accountIndex"`
188	MerkleProof            merkleProofAPI  `json:"merkleProof"`
189	Balance                string          `json:"balance"`
190	InstantWithdrawn       *int64          `json:"instantWithdrawn"`
191	DelayedWithdrawRequest *int64          `json:"delayedWithdrawRequest"`
192	DelayedWithdrawn       *int64          `json:"delayedWithdrawn"`
193	Token                  tokenAPI        `json:"token"`
194}
195
196func historyExitsToAPI(dbExits []historydb.HistoryExit) []exitAPI {
197	apiExits := []exitAPI{}
198	for i := 0; i < len(dbExits); i++ {
199		exit := exitAPI{
200			ItemID:     dbExits[i].ItemID,
201			BatchNum:   dbExits[i].BatchNum,
202			AccountIdx: idxToHez(dbExits[i].AccountIdx, dbExits[i].TokenSymbol),
203			MerkleProof: merkleProofAPI{
204				Root:     dbExits[i].MerkleProof.Root.String(),
205				OldKey:   dbExits[i].MerkleProof.OldKey.String(),
206				OldValue: dbExits[i].MerkleProof.OldValue.String(),
207				IsOld0:   dbExits[i].MerkleProof.IsOld0,
208				Key:      dbExits[i].MerkleProof.Key.String(),
209				Value:    dbExits[i].MerkleProof.Value.String(),
210				Fnc:      dbExits[i].MerkleProof.Fnc,
211			},
212			Balance:                dbExits[i].Balance.String(),
213			InstantWithdrawn:       dbExits[i].InstantWithdrawn,
214			DelayedWithdrawRequest: dbExits[i].DelayedWithdrawRequest,
215			DelayedWithdrawn:       dbExits[i].DelayedWithdrawn,
216			Token: tokenAPI{
217				TokenID:     dbExits[i].TokenID,
218				EthBlockNum: dbExits[i].TokenEthBlockNum,
219				EthAddr:     dbExits[i].TokenEthAddr,
220				Name:        dbExits[i].TokenName,
221				Symbol:      dbExits[i].TokenSymbol,
222				Decimals:    dbExits[i].TokenDecimals,
223				USD:         dbExits[i].TokenUSD,
224				USDUpdate:   dbExits[i].TokenUSDUpdate,
225			},
226		}
227		siblings := []string{}
228		for j := 0; j < len(dbExits[i].MerkleProof.Siblings); j++ {
229			siblings = append(siblings, dbExits[i].MerkleProof.Siblings[j].String())
230		}
231		exit.MerkleProof.Siblings = siblings
232		apiExits = append(apiExits, exit)
233	}
234	return apiExits
235}
236
237// Tokens
238type tokensAPI struct {
239	Tokens     []tokenAPI     `json:"tokens"`
240	Pagination *db.Pagination `json:"pagination"`
241}
242
243func (t *tokensAPI) GetPagination() *db.Pagination {
244	if t.Tokens[0].ItemID < t.Tokens[len(t.Tokens)-1].ItemID {
245		t.Pagination.FirstReturnedItem = t.Tokens[0].ItemID
246		t.Pagination.LastReturnedItem = t.Tokens[len(t.Tokens)-1].ItemID
247	} else {
248		t.Pagination.LastReturnedItem = t.Tokens[0].ItemID
249		t.Pagination.FirstReturnedItem = t.Tokens[len(t.Tokens)-1].ItemID
250	}
251	return t.Pagination
252}
253func (t *tokensAPI) Len() int { return len(t.Tokens) }
254
255type tokenAPI struct {
256	ItemID      int               `json:"itemId"`
257	TokenID     common.TokenID    `json:"id"`
258	EthBlockNum int64             `json:"ethereumBlockNum"` // Ethereum block number in which this token was registered
259	EthAddr     ethCommon.Address `json:"ethereumAddress"`
260	Name        string            `json:"name"`
261	Symbol      string            `json:"symbol"`
262	Decimals    uint64            `json:"decimals"`
263	USD         *float64          `json:"USD"`
264	USDUpdate   *time.Time        `json:"fiatUpdate"`
265}
266
267func tokensToAPI(dbTokens []historydb.TokenRead) []tokenAPI {
268	apiTokens := []tokenAPI{}
269	for i := 0; i < len(dbTokens); i++ {
270		apiTokens = append(apiTokens, tokenAPI{
271			ItemID:      dbTokens[i].ItemID,
272			TokenID:     dbTokens[i].TokenID,
273			EthBlockNum: dbTokens[i].EthBlockNum,
274			EthAddr:     dbTokens[i].EthAddr,
275			Name:        dbTokens[i].Name,
276			Symbol:      dbTokens[i].Symbol,
277			Decimals:    dbTokens[i].Decimals,
278			USD:         dbTokens[i].USD,
279			USDUpdate:   dbTokens[i].USDUpdate,
280		})
281	}
282	return apiTokens
283}
284
285// Config
286
287type rollupConstants struct {
288	PublicConstants         eth.RollupPublicConstants `json:"publicConstants"`
289	MaxFeeIdxCoordinator    int                       `json:"maxFeeIdxCoordinator"`
290	ReservedIdx             int                       `json:"reservedIdx"`
291	ExitIdx                 int                       `json:"exitIdx"`
292	LimitLoadAmount         *big.Int                  `json:"limitLoadAmount"`
293	LimitL2TransferAmount   *big.Int                  `json:"limitL2TransferAmount"`
294	LimitTokens             int                       `json:"limitTokens"`
295	L1CoordinatorTotalBytes int                       `json:"l1CoordinatorTotalBytes"`
296	L1UserTotalBytes        int                       `json:"l1UserTotalBytes"`
297	MaxL1UserTx             int                       `json:"maxL1UserTx"`
298	MaxL1Tx                 int                       `json:"maxL1Tx"`
299	InputSHAConstantBytes   int                       `json:"inputSHAConstantBytes"`
300	NumBuckets              int                       `json:"numBuckets"`
301	MaxWithdrawalDelay      int                       `json:"maxWithdrawalDelay"`
302	ExchangeMultiplier      int                       `json:"exchangeMultiplier"`
303}
304
305type configAPI struct {
306	RollupConstants   rollupConstants       `json:"hermez"`
307	AuctionConstants  eth.AuctionConstants  `json:"auction"`
308	WDelayerConstants eth.WDelayerConstants `json:"withdrawalDelayer"`
309}
310
311// PoolL2Tx
312
313type receivedPoolTx struct {
314	TxID        common.TxID           `json:"id" binding:"required"`
315	Type        common.TxType         `json:"type" binding:"required"`
316	TokenID     common.TokenID        `json:"tokenId"`
317	FromIdx     string                `json:"fromAccountIndex" binding:"required"`
318	ToIdx       *string               `json:"toAccountIndex"`
319	ToEthAddr   *string               `json:"toHezEthereumAddress"`
320	ToBJJ       *string               `json:"toBjj"`
321	Amount      string                `json:"amount" binding:"required"`
322	Fee         common.FeeSelector    `json:"fee"`
323	Nonce       common.Nonce          `json:"nonce"`
324	Signature   babyjub.SignatureComp `json:"signature" binding:"required"`
325	RqFromIdx   *string               `json:"requestFromAccountIndex"`
326	RqToIdx     *string               `json:"requestToAccountIndex"`
327	RqToEthAddr *string               `json:"requestToHezEthereumAddress"`
328	RqToBJJ     *string               `json:"requestToBjj"`
329	RqTokenID   *common.TokenID       `json:"requestTokenId"`
330	RqAmount    *string               `json:"requestAmount"`
331	RqFee       *common.FeeSelector   `json:"requestFee"`
332	RqNonce     *common.Nonce         `json:"requestNonce"`
333}
334
335func (tx *receivedPoolTx) toDBWritePoolL2Tx() (*l2db.PoolL2TxWrite, error) {
336	amount := new(big.Int)
337	amount.SetString(tx.Amount, 10)
338	txw := &l2db.PoolL2TxWrite{
339		TxID:      tx.TxID,
340		TokenID:   tx.TokenID,
341		Amount:    amount,
342		Fee:       tx.Fee,
343		Nonce:     tx.Nonce,
344		State:     common.PoolL2TxStatePending,
345		Signature: tx.Signature,
346		RqTokenID: tx.RqTokenID,
347		RqFee:     tx.RqFee,
348		RqNonce:   tx.RqNonce,
349		Type:      tx.Type,
350	}
351	// Check FromIdx (required)
352	fidx, err := stringToIdx(tx.FromIdx, "fromAccountIndex")
353	if err != nil {
354		return nil, err
355	}
356	if fidx == nil {
357		return nil, errors.New("invalid fromAccountIndex")
358	}
359	// Set FromIdx
360	txw.FromIdx = common.Idx(*fidx)
361	// Set AmountFloat
362	f := new(big.Float).SetInt(amount)
363	amountF, _ := f.Float64()
364	txw.AmountFloat = amountF
365	if amountF < 0 {
366		return nil, errors.New("amount must be positive")
367	}
368	// Check "to" fields, only one of: ToIdx, ToEthAddr, ToBJJ
369	if tx.ToIdx != nil { // Case: Tx with ToIdx setted
370		// Set ToIdx
371		tidxUint, err := stringToIdx(*tx.ToIdx, "toAccountIndex")
372		if err != nil || tidxUint == nil {
373			return nil, errors.New("invalid toAccountIndex")
374		}
375		tidx := common.Idx(*tidxUint)
376		txw.ToIdx = &tidx
377	} else if tx.ToBJJ != nil { // Case: Tx with ToBJJ setted
378		// tx.ToEthAddr must be equal to ethAddrWhenBJJLower or ethAddrWhenBJJUpper
379		if tx.ToEthAddr != nil {
380			toEthAddr, err := hezStringToEthAddr(*tx.ToEthAddr, "toHezEthereumAddress")
381			if err != nil || *toEthAddr != common.FFAddr {
382				return nil, fmt.Errorf("if toBjj is setted, toHezEthereumAddress must be hez:%s", common.FFAddr.Hex())
383			}
384		} else {
385			return nil, fmt.Errorf("if toBjj is setted, toHezEthereumAddress must be hez:%s and toAccountIndex must be null", common.FFAddr.Hex())
386		}
387		// Set ToEthAddr and ToBJJ
388		toBJJ, err := hezStringToBJJ(*tx.ToBJJ, "toBjj")
389		if err != nil || toBJJ == nil {
390			return nil, errors.New("invalid toBjj")
391		}
392		txw.ToBJJ = toBJJ
393		txw.ToEthAddr = &common.FFAddr
394	} else if tx.ToEthAddr != nil { // Case: Tx with ToEthAddr setted
395		// Set ToEthAddr
396		toEthAddr, err := hezStringToEthAddr(*tx.ToEthAddr, "toHezEthereumAddress")
397		if err != nil || toEthAddr == nil {
398			return nil, errors.New("invalid toHezEthereumAddress")
399		}
400		txw.ToEthAddr = toEthAddr
401	} else {
402		return nil, errors.New("one of toAccountIndex, toHezEthereumAddress or toBjj must be setted")
403	}
404	// Check "rq" fields
405	if tx.RqFromIdx != nil {
406		// check and set RqFromIdx
407		rqfidxUint, err := stringToIdx(tx.FromIdx, "requestFromAccountIndex")
408		if err != nil || rqfidxUint == nil {
409			return nil, errors.New("invalid requestFromAccountIndex")
410		}
411		// Set RqFromIdx
412		rqfidx := common.Idx(*rqfidxUint)
413		txw.RqFromIdx = &rqfidx
414		// Case: RqTx with RqToIdx setted
415		if tx.RqToIdx != nil {
416			// Set ToIdx
417			tidxUint, err := stringToIdx(*tx.RqToIdx, "requestToAccountIndex")
418			if err != nil || tidxUint == nil {
419				return nil, errors.New("invalid requestToAccountIndex")
420			}
421			tidx := common.Idx(*tidxUint)
422			txw.ToIdx = &tidx
423		} else if tx.RqToBJJ != nil { // Case: Tx with ToBJJ setted
424			// tx.ToEthAddr must be equal to ethAddrWhenBJJLower or ethAddrWhenBJJUpper
425			if tx.RqToEthAddr != nil {
426				rqEthAddr, err := hezStringToEthAddr(*tx.RqToEthAddr, "")
427				if err != nil || *rqEthAddr != common.FFAddr {
428					return nil, fmt.Errorf("if requestToBjj is setted, requestToHezEthereumAddress must be hez:%s", common.FFAddr.Hex())
429				}
430			} else {
431				return nil, fmt.Errorf("if requestToBjj is setted, toHezEthereumAddress must be hez:%s and requestToAccountIndex must be null", common.FFAddr.Hex())
432			}
433			// Set ToEthAddr and ToBJJ
434			rqToBJJ, err := hezStringToBJJ(*tx.RqToBJJ, "requestToBjj")
435			if err != nil || rqToBJJ == nil {
436				return nil, errors.New("invalid requestToBjj")
437			}
438			txw.RqToBJJ = rqToBJJ
439			txw.RqToEthAddr = &common.FFAddr
440		} else if tx.RqToEthAddr != nil { // Case: Tx with ToEthAddr setted
441			// Set ToEthAddr
442			rqToEthAddr, err := hezStringToEthAddr(*tx.ToEthAddr, "requestToHezEthereumAddress")
443			if err != nil || rqToEthAddr == nil {
444				return nil, errors.New("invalid requestToHezEthereumAddress")
445			}
446			txw.RqToEthAddr = rqToEthAddr
447		} else {
448			return nil, errors.New("one of requestToAccountIndex, requestToHezEthereumAddress or requestToBjj must be setted")
449		}
450		if tx.RqAmount == nil {
451			return nil, errors.New("requestAmount must be provided if other request fields are setted")
452		}
453		rqAmount := new(big.Int)
454		rqAmount.SetString(*tx.RqAmount, 10)
455		txw.RqAmount = rqAmount
456	} else if tx.RqToIdx != nil && tx.RqToEthAddr != nil && tx.RqToBJJ != nil &&
457		tx.RqTokenID != nil && tx.RqAmount != nil && tx.RqNonce != nil && tx.RqFee != nil {
458		// if tx.RqToIdx is not setted, tx.Rq* must be null as well
459		return nil, errors.New("if requestFromAccountIndex is setted, the rest of request fields must be null as well")
460	}
461
462	return txw, validatePoolL2TxWrite(txw)
463}
464
465func validatePoolL2TxWrite(txw *l2db.PoolL2TxWrite) error {
466	poolTx := common.PoolL2Tx{
467		TxID:      txw.TxID,
468		FromIdx:   txw.FromIdx,
469		ToBJJ:     txw.ToBJJ,
470		TokenID:   txw.TokenID,
471		Amount:    txw.Amount,
472		Fee:       txw.Fee,
473		Nonce:     txw.Nonce,
474		State:     txw.State,
475		Signature: txw.Signature,
476		RqToBJJ:   txw.RqToBJJ,
477		RqAmount:  txw.RqAmount,
478		Type:      txw.Type,
479	}
480	// ToIdx
481	if txw.ToIdx != nil {
482		poolTx.ToIdx = *txw.ToIdx
483	}
484	// ToEthAddr
485	if txw.ToEthAddr == nil {
486		poolTx.ToEthAddr = common.EmptyAddr
487	} else {
488		poolTx.ToEthAddr = *txw.ToEthAddr
489	}
490	// RqFromIdx
491	if txw.RqFromIdx != nil {
492		poolTx.RqFromIdx = *txw.RqFromIdx
493	}
494	// RqToIdx
495	if txw.RqToIdx != nil {
496		poolTx.RqToIdx = *txw.RqToIdx
497	}
498	// RqToEthAddr
499	if txw.RqToEthAddr == nil {
500		poolTx.RqToEthAddr = common.EmptyAddr
501	} else {
502		poolTx.RqToEthAddr = *txw.RqToEthAddr
503	}
504	// RqTokenID
505	if txw.RqTokenID != nil {
506		poolTx.RqTokenID = *txw.RqTokenID
507	}
508	// RqFee
509	if txw.RqFee != nil {
510		poolTx.RqFee = *txw.RqFee
511	}
512	// RqNonce
513	if txw.RqNonce != nil {
514		poolTx.RqNonce = *txw.RqNonce
515	}
516	// Check type and id
517	_, err := common.NewPoolL2Tx(&poolTx)
518	if err != nil {
519		return err
520	}
521	// Check signature
522	// Get public key
523	account, err := s.GetAccount(poolTx.FromIdx)
524	if err != nil {
525		return err
526	}
527	if !poolTx.VerifySignature(account.PublicKey) {
528		return errors.New("wrong signature")
529	}
530	return nil
531}
532
533type sendPoolTx struct {
534	TxID        common.TxID           `json:"id"`
535	Type        common.TxType         `json:"type"`
536	FromIdx     string                `json:"fromAccountIndex"`
537	ToIdx       *string               `json:"toAccountIndex"`
538	ToEthAddr   *string               `json:"toHezEthereumAddress"`
539	ToBJJ       *string               `json:"toBjj"`
540	Amount      string                `json:"amount"`
541	Fee         common.FeeSelector    `json:"fee"`
542	Nonce       common.Nonce          `json:"nonce"`
543	State       common.PoolL2TxState  `json:"state"`
544	Signature   babyjub.SignatureComp `json:"signature"`
545	Timestamp   time.Time             `json:"timestamp"`
546	BatchNum    *common.BatchNum      `json:"batchNum"`
547	RqFromIdx   *string               `json:"requestFromAccountIndex"`
548	RqToIdx     *string               `json:"requestToAccountIndex"`
549	RqToEthAddr *string               `json:"requestToHezEthereumAddress"`
550	RqToBJJ     *string               `json:"requestToBJJ"`
551	RqTokenID   *common.TokenID       `json:"requestTokenId"`
552	RqAmount    *string               `json:"requestAmount"`
553	RqFee       *common.FeeSelector   `json:"requestFee"`
554	RqNonce     *common.Nonce         `json:"requestNonce"`
555	Token       tokenAPI              `json:"token"`
556}
557
558func poolL2TxReadToSend(dbTx *l2db.PoolL2TxRead) *sendPoolTx {
559	tx := &sendPoolTx{
560		TxID:      dbTx.TxID,
561		Type:      dbTx.Type,
562		FromIdx:   idxToHez(dbTx.FromIdx, dbTx.TokenSymbol),
563		Amount:    dbTx.Amount.String(),
564		Fee:       dbTx.Fee,
565		Nonce:     dbTx.Nonce,
566		State:     dbTx.State,
567		Signature: dbTx.Signature,
568		Timestamp: dbTx.Timestamp,
569		BatchNum:  dbTx.BatchNum,
570		RqTokenID: dbTx.RqTokenID,
571		RqFee:     dbTx.RqFee,
572		RqNonce:   dbTx.RqNonce,
573		Token: tokenAPI{
574			TokenID:     dbTx.TokenID,
575			EthBlockNum: dbTx.TokenEthBlockNum,
576			EthAddr:     dbTx.TokenEthAddr,
577			Name:        dbTx.TokenName,
578			Symbol:      dbTx.TokenSymbol,
579			Decimals:    dbTx.TokenDecimals,
580			USD:         dbTx.TokenUSD,
581			USDUpdate:   dbTx.TokenUSDUpdate,
582		},
583	}
584	// ToIdx
585	if dbTx.ToIdx != nil {
586		toIdx := idxToHez(*dbTx.ToIdx, dbTx.TokenSymbol)
587		tx.ToIdx = &toIdx
588	}
589	// ToEthAddr
590	if dbTx.ToEthAddr != nil {
591		toEth := ethAddrToHez(*dbTx.ToEthAddr)
592		tx.ToEthAddr = &toEth
593	}
594	// ToBJJ
595	if dbTx.ToBJJ != nil {
596		toBJJ := bjjToString(dbTx.ToBJJ)
597		tx.ToBJJ = &toBJJ
598	}
599	// RqFromIdx
600	if dbTx.RqFromIdx != nil {
601		rqFromIdx := idxToHez(*dbTx.RqFromIdx, dbTx.TokenSymbol)
602		tx.RqFromIdx = &rqFromIdx
603	}
604	// RqToIdx
605	if dbTx.RqToIdx != nil {
606		rqToIdx := idxToHez(*dbTx.RqToIdx, dbTx.TokenSymbol)
607		tx.RqToIdx = &rqToIdx
608	}
609	// RqToEthAddr
610	if dbTx.RqToEthAddr != nil {
611		rqToEth := ethAddrToHez(*dbTx.RqToEthAddr)
612		tx.RqToEthAddr = &rqToEth
613	}
614	// RqToBJJ
615	if dbTx.RqToBJJ != nil {
616		rqToBJJ := bjjToString(dbTx.RqToBJJ)
617		tx.RqToBJJ = &rqToBJJ
618	}
619	// RqAmount
620	if dbTx.RqAmount != nil {
621		rqAmount := dbTx.RqAmount.String()
622		tx.RqAmount = &rqAmount
623	}
624	return tx
625}
626
627// Coordinators
628
629type coordinatorAPI struct {
630	ItemID      int               `json:"itemId"`
631	Bidder      ethCommon.Address `json:"bidderAddr"`
632	Forger      ethCommon.Address `json:"forgerAddr"`
633	EthBlockNum int64             `json:"ethereumBlock"`
634	URL         string            `json:"URL"`
635}
636
637type coordinatorsAPI struct {
638	Coordinators []coordinatorAPI `json:"coordinators"`
639	Pagination   *db.Pagination   `json:"pagination"`
640}
641
642func (t *coordinatorsAPI) GetPagination() *db.Pagination {
643	if t.Coordinators[0].ItemID < t.Coordinators[len(t.Coordinators)-1].ItemID {
644		t.Pagination.FirstReturnedItem = t.Coordinators[0].ItemID
645		t.Pagination.LastReturnedItem = t.Coordinators[len(t.Coordinators)-1].ItemID
646	} else {
647		t.Pagination.LastReturnedItem = t.Coordinators[0].ItemID
648		t.Pagination.FirstReturnedItem = t.Coordinators[len(t.Coordinators)-1].ItemID
649	}
650	return t.Pagination
651}
652
653func (t *coordinatorsAPI) Len() int { return len(t.Coordinators) }
654
655func coordinatorsToAPI(dbCoordinators []historydb.HistoryCoordinator) []coordinatorAPI {
656	apiCoordinators := []coordinatorAPI{}
657	for i := 0; i < len(dbCoordinators); i++ {
658		apiCoordinators = append(apiCoordinators, coordinatorAPI{
659			ItemID:      dbCoordinators[i].ItemID,
660			Bidder:      dbCoordinators[i].Bidder,
661			Forger:      dbCoordinators[i].Forger,
662			EthBlockNum: dbCoordinators[i].EthBlockNum,
663			URL:         dbCoordinators[i].URL,
664		})
665	}
666	return apiCoordinators
667}