diff --git a/api/api_test.go b/api/api_test.go index 6235fc8..8ef2b0f 100644 --- a/api/api_test.go +++ b/api/api_test.go @@ -39,7 +39,7 @@ const apiURL = "http://localhost" + apiPort + "/" type testCommon struct { blocks []common.Block - tokens []tokenAPI + tokens []historydb.TokenWithUSD batches []testBatch coordinators []coordinatorAPI usrAddr string @@ -256,9 +256,9 @@ func TestMain(m *testing.M) { panic(err) } // Set token value - tokensUSD := []tokenAPI{} + tokensUSD := []historydb.TokenWithUSD{} for i, tkn := range tokens { - token := tokenAPI{ + token := historydb.TokenWithUSD{ TokenID: tkn.TokenID, EthBlockNum: tkn.EthBlockNum, EthAddr: tkn.EthAddr, @@ -347,7 +347,7 @@ func TestMain(m *testing.M) { } panic("timesamp not found") } - getToken := func(id common.TokenID) tokenAPI { + getToken := func(id common.TokenID) historydb.TokenWithUSD { for i := 0; i < len(tokensUSD); i++ { if tokensUSD[i].TokenID == id { return tokensUSD[i] @@ -355,7 +355,7 @@ func TestMain(m *testing.M) { } panic("token not found") } - getTokenByIdx := func(idx common.Idx) tokenAPI { + getTokenByIdx := func(idx common.Idx) historydb.TokenWithUSD { for _, acc := range accs { if idx == acc.Idx { return getToken(acc.TokenID) @@ -1071,127 +1071,6 @@ func assertExitAPIs(t *testing.T, expected, actual []exitAPI) { assert.Equal(t, expected[i], actual[i]) } } - -func TestGetToken(t *testing.T) { - // Get all txs by their ID - endpoint := apiURL + "tokens/" - fetchedTokens := []tokenAPI{} - for _, token := range tc.tokens { - fetchedToken := tokenAPI{} - assert.NoError(t, doGoodReq("GET", endpoint+strconv.Itoa(int(token.TokenID)), nil, &fetchedToken)) - fetchedTokens = append(fetchedTokens, fetchedToken) - } - assertTokensAPIs(t, tc.tokens, fetchedTokens) -} - -func TestGetTokens(t *testing.T) { - endpoint := apiURL + "tokens" - fetchedTokens := []tokenAPI{} - appendIter := func(intr interface{}) { - for i := 0; i < len(intr.(*tokensAPI).Tokens); i++ { - tmp, err := copystructure.Copy(intr.(*tokensAPI).Tokens[i]) - if err != nil { - panic(err) - } - fetchedTokens = append(fetchedTokens, tmp.(tokenAPI)) - } - } - // Get all (no filters) - limit := 8 - path := fmt.Sprintf("%s?limit=%d&fromItem=", endpoint, limit) - err := doGoodReqPaginated(path, historydb.OrderAsc, &tokensAPI{}, appendIter) - assert.NoError(t, err) - assertTokensAPIs(t, tc.tokens, fetchedTokens) - - // Get by tokenIds - fetchedTokens = []tokenAPI{} - limit = 7 - stringIds := strconv.Itoa(int(tc.tokens[2].TokenID)) + "," + strconv.Itoa(int(tc.tokens[5].TokenID)) + "," + strconv.Itoa(int(tc.tokens[6].TokenID)) - path = fmt.Sprintf( - "%s?ids=%s&limit=%d&fromItem=", - endpoint, stringIds, limit, - ) - err = doGoodReqPaginated(path, historydb.OrderAsc, &tokensAPI{}, appendIter) - assert.NoError(t, err) - var tokensFiltered []tokenAPI - tokensFiltered = append(tokensFiltered, tc.tokens[2]) - tokensFiltered = append(tokensFiltered, tc.tokens[5]) - tokensFiltered = append(tokensFiltered, tc.tokens[6]) - assertTokensAPIs(t, tokensFiltered, fetchedTokens) - - // Get by symbols - fetchedTokens = []tokenAPI{} - limit = 7 - stringSymbols := tc.tokens[1].Symbol + "," + tc.tokens[3].Symbol - path = fmt.Sprintf( - "%s?symbols=%s&limit=%d&fromItem=", - endpoint, stringSymbols, limit, - ) - err = doGoodReqPaginated(path, historydb.OrderAsc, &tokensAPI{}, appendIter) - assert.NoError(t, err) - tokensFiltered = nil - tokensFiltered = append(tokensFiltered, tc.tokens[1]) - tokensFiltered = append(tokensFiltered, tc.tokens[3]) - assertTokensAPIs(t, tokensFiltered, fetchedTokens) - - // Get by name - fetchedTokens = []tokenAPI{} - limit = 5 - stringName := tc.tokens[8].Name[4:5] - path = fmt.Sprintf( - "%s?name=%s&limit=%d&fromItem=", - endpoint, stringName, limit, - ) - err = doGoodReqPaginated(path, historydb.OrderAsc, &tokensAPI{}, appendIter) - assert.NoError(t, err) - tokensFiltered = nil - tokensFiltered = append(tokensFiltered, tc.tokens[8]) - assertTokensAPIs(t, tokensFiltered, fetchedTokens) - - // Multiple filters - fetchedTokens = []tokenAPI{} - limit = 5 - stringSymbols = tc.tokens[2].Symbol + "," + tc.tokens[6].Symbol - stringIds = strconv.Itoa(int(tc.tokens[2].TokenID)) + "," + strconv.Itoa(int(tc.tokens[5].TokenID)) + "," + strconv.Itoa(int(tc.tokens[6].TokenID)) - path = fmt.Sprintf( - "%s?symbols=%s&ids=%s&limit=%d&fromItem=", - endpoint, stringSymbols, stringIds, limit, - ) - err = doGoodReqPaginated(path, historydb.OrderAsc, &tokensAPI{}, appendIter) - assert.NoError(t, err) - - tokensFiltered = nil - tokensFiltered = append(tokensFiltered, tc.tokens[2]) - tokensFiltered = append(tokensFiltered, tc.tokens[6]) - assertTokensAPIs(t, tokensFiltered, fetchedTokens) - - // All, in reverse order - fetchedTokens = []tokenAPI{} - limit = 5 - path = fmt.Sprintf("%s?limit=%d&fromItem=", endpoint, limit) - err = doGoodReqPaginated(path, historydb.OrderDesc, &tokensAPI{}, appendIter) - assert.NoError(t, err) - flipedTokens := []tokenAPI{} - for i := 0; i < len(tc.tokens); i++ { - flipedTokens = append(flipedTokens, tc.tokens[len(tc.tokens)-1-i]) - } - assertTokensAPIs(t, flipedTokens, fetchedTokens) -} - -func assertTokensAPIs(t *testing.T, expected, actual []tokenAPI) { - require.Equal(t, len(expected), len(actual)) - for i := 0; i < len(actual); i++ { //nolint len(actual) won't change within the loop - actual[i].ItemID = 0 - if expected[i].USDUpdate == nil { - assert.Equal(t, expected[i].USDUpdate, actual[i].USDUpdate) - } else { - assert.Equal(t, expected[i].USDUpdate.Unix(), actual[i].USDUpdate.Unix()) - expected[i].USDUpdate = actual[i].USDUpdate - } - assert.Equal(t, expected[i], actual[i]) - } -} - func TestGetConfig(t *testing.T) { endpoint := apiURL + "config" var configTest configAPI diff --git a/api/dbtoapistructs.go b/api/dbtoapistructs.go index b6b4b3b..8d78fc2 100644 --- a/api/dbtoapistructs.go +++ b/api/dbtoapistructs.go @@ -82,20 +82,20 @@ type l2Info struct { } type historyTxAPI struct { - IsL1 string `json:"L1orL2"` - TxID common.TxID `json:"id"` - ItemID int `json:"itemId"` - Type common.TxType `json:"type"` - Position int `json:"position"` - FromIdx *string `json:"fromAccountIndex"` - ToIdx string `json:"toAccountIndex"` - Amount string `json:"amount"` - BatchNum *common.BatchNum `json:"batchNum"` - HistoricUSD *float64 `json:"historicUSD"` - Timestamp time.Time `json:"timestamp"` - L1Info *l1Info `json:"L1Info"` - L2Info *l2Info `json:"L2Info"` - Token tokenAPI `json:"token"` + IsL1 string `json:"L1orL2"` + TxID common.TxID `json:"id"` + ItemID int `json:"itemId"` + Type common.TxType `json:"type"` + Position int `json:"position"` + FromIdx *string `json:"fromAccountIndex"` + ToIdx string `json:"toAccountIndex"` + Amount string `json:"amount"` + BatchNum *common.BatchNum `json:"batchNum"` + HistoricUSD *float64 `json:"historicUSD"` + Timestamp time.Time `json:"timestamp"` + L1Info *l1Info `json:"L1Info"` + L2Info *l2Info `json:"L2Info"` + Token historydb.TokenWithUSD `json:"token"` } func historyTxsToAPI(dbTxs []historydb.HistoryTx) []historyTxAPI { @@ -111,7 +111,7 @@ func historyTxsToAPI(dbTxs []historydb.HistoryTx) []historyTxAPI { HistoricUSD: dbTxs[i].HistoricUSD, BatchNum: dbTxs[i].BatchNum, Timestamp: dbTxs[i].Timestamp, - Token: tokenAPI{ + Token: historydb.TokenWithUSD{ TokenID: dbTxs[i].TokenID, EthBlockNum: dbTxs[i].TokenEthBlockNum, EthAddr: dbTxs[i].TokenEthAddr, @@ -184,15 +184,15 @@ type merkleProofAPI struct { } type exitAPI struct { - ItemID int `json:"itemId"` - BatchNum common.BatchNum `json:"batchNum"` - AccountIdx string `json:"accountIndex"` - MerkleProof merkleProofAPI `json:"merkleProof"` - Balance string `json:"balance"` - InstantWithdrawn *int64 `json:"instantWithdrawn"` - DelayedWithdrawRequest *int64 `json:"delayedWithdrawRequest"` - DelayedWithdrawn *int64 `json:"delayedWithdrawn"` - Token tokenAPI `json:"token"` + ItemID int `json:"itemId"` + BatchNum common.BatchNum `json:"batchNum"` + AccountIdx string `json:"accountIndex"` + MerkleProof merkleProofAPI `json:"merkleProof"` + Balance string `json:"balance"` + InstantWithdrawn *int64 `json:"instantWithdrawn"` + DelayedWithdrawRequest *int64 `json:"delayedWithdrawRequest"` + DelayedWithdrawn *int64 `json:"delayedWithdrawn"` + Token historydb.TokenWithUSD `json:"token"` } func historyExitsToAPI(dbExits []historydb.HistoryExit) []exitAPI { @@ -215,7 +215,7 @@ func historyExitsToAPI(dbExits []historydb.HistoryExit) []exitAPI { InstantWithdrawn: dbExits[i].InstantWithdrawn, DelayedWithdrawRequest: dbExits[i].DelayedWithdrawRequest, DelayedWithdrawn: dbExits[i].DelayedWithdrawn, - Token: tokenAPI{ + Token: historydb.TokenWithUSD{ TokenID: dbExits[i].TokenID, EthBlockNum: dbExits[i].TokenEthBlockNum, EthAddr: dbExits[i].TokenEthAddr, @@ -236,54 +236,6 @@ func historyExitsToAPI(dbExits []historydb.HistoryExit) []exitAPI { return apiExits } -// Tokens -type tokensAPI struct { - Tokens []tokenAPI `json:"tokens"` - Pagination *db.Pagination `json:"pagination"` -} - -func (t *tokensAPI) GetPagination() *db.Pagination { - if t.Tokens[0].ItemID < t.Tokens[len(t.Tokens)-1].ItemID { - t.Pagination.FirstReturnedItem = t.Tokens[0].ItemID - t.Pagination.LastReturnedItem = t.Tokens[len(t.Tokens)-1].ItemID - } else { - t.Pagination.LastReturnedItem = t.Tokens[0].ItemID - t.Pagination.FirstReturnedItem = t.Tokens[len(t.Tokens)-1].ItemID - } - return t.Pagination -} -func (t *tokensAPI) Len() int { return len(t.Tokens) } - -type tokenAPI struct { - ItemID int `json:"itemId"` - TokenID common.TokenID `json:"id"` - EthBlockNum int64 `json:"ethereumBlockNum"` // Ethereum block number in which this token was registered - EthAddr ethCommon.Address `json:"ethereumAddress"` - Name string `json:"name"` - Symbol string `json:"symbol"` - Decimals uint64 `json:"decimals"` - USD *float64 `json:"USD"` - USDUpdate *time.Time `json:"fiatUpdate"` -} - -func tokensToAPI(dbTokens []historydb.TokenRead) []tokenAPI { - apiTokens := []tokenAPI{} - for i := 0; i < len(dbTokens); i++ { - apiTokens = append(apiTokens, tokenAPI{ - ItemID: dbTokens[i].ItemID, - TokenID: dbTokens[i].TokenID, - EthBlockNum: dbTokens[i].EthBlockNum, - EthAddr: dbTokens[i].EthAddr, - Name: dbTokens[i].Name, - Symbol: dbTokens[i].Symbol, - Decimals: dbTokens[i].Decimals, - USD: dbTokens[i].USD, - USDUpdate: dbTokens[i].USDUpdate, - }) - } - return apiTokens -} - // Config type rollupConstants struct { @@ -533,28 +485,28 @@ func validatePoolL2TxWrite(txw *l2db.PoolL2TxWrite) error { } type sendPoolTx struct { - TxID common.TxID `json:"id"` - Type common.TxType `json:"type"` - FromIdx string `json:"fromAccountIndex"` - ToIdx *string `json:"toAccountIndex"` - ToEthAddr *string `json:"toHezEthereumAddress"` - ToBJJ *string `json:"toBjj"` - Amount string `json:"amount"` - Fee common.FeeSelector `json:"fee"` - Nonce common.Nonce `json:"nonce"` - State common.PoolL2TxState `json:"state"` - Signature babyjub.SignatureComp `json:"signature"` - Timestamp time.Time `json:"timestamp"` - BatchNum *common.BatchNum `json:"batchNum"` - RqFromIdx *string `json:"requestFromAccountIndex"` - RqToIdx *string `json:"requestToAccountIndex"` - RqToEthAddr *string `json:"requestToHezEthereumAddress"` - RqToBJJ *string `json:"requestToBJJ"` - RqTokenID *common.TokenID `json:"requestTokenId"` - RqAmount *string `json:"requestAmount"` - RqFee *common.FeeSelector `json:"requestFee"` - RqNonce *common.Nonce `json:"requestNonce"` - Token tokenAPI `json:"token"` + TxID common.TxID `json:"id"` + Type common.TxType `json:"type"` + FromIdx string `json:"fromAccountIndex"` + ToIdx *string `json:"toAccountIndex"` + ToEthAddr *string `json:"toHezEthereumAddress"` + ToBJJ *string `json:"toBjj"` + Amount string `json:"amount"` + Fee common.FeeSelector `json:"fee"` + Nonce common.Nonce `json:"nonce"` + State common.PoolL2TxState `json:"state"` + Signature babyjub.SignatureComp `json:"signature"` + Timestamp time.Time `json:"timestamp"` + BatchNum *common.BatchNum `json:"batchNum"` + RqFromIdx *string `json:"requestFromAccountIndex"` + RqToIdx *string `json:"requestToAccountIndex"` + RqToEthAddr *string `json:"requestToHezEthereumAddress"` + RqToBJJ *string `json:"requestToBJJ"` + RqTokenID *common.TokenID `json:"requestTokenId"` + RqAmount *string `json:"requestAmount"` + RqFee *common.FeeSelector `json:"requestFee"` + RqNonce *common.Nonce `json:"requestNonce"` + Token historydb.TokenWithUSD `json:"token"` } func poolL2TxReadToSend(dbTx *l2db.PoolL2TxRead) *sendPoolTx { @@ -572,7 +524,7 @@ func poolL2TxReadToSend(dbTx *l2db.PoolL2TxRead) *sendPoolTx { RqTokenID: dbTx.RqTokenID, RqFee: dbTx.RqFee, RqNonce: dbTx.RqNonce, - Token: tokenAPI{ + Token: historydb.TokenWithUSD{ TokenID: dbTx.TokenID, EthBlockNum: dbTx.TokenEthBlockNum, EthAddr: dbTx.TokenEthAddr, diff --git a/api/handlers.go b/api/handlers.go index cc28e42..36f5073 100644 --- a/api/handlers.go +++ b/api/handlers.go @@ -6,7 +6,6 @@ import ( "net/http" "github.com/gin-gonic/gin" - "github.com/hermeznetwork/hermez-node/common" "github.com/hermeznetwork/hermez-node/db/historydb" ) @@ -216,54 +215,6 @@ func getConfig(c *gin.Context) { c.JSON(http.StatusOK, cg) } -func getTokens(c *gin.Context) { - // Account filters - tokenIDs, symbols, name, err := parseTokenFilters(c) - if err != nil { - retBadReq(err, c) - return - } - // Pagination - fromItem, order, limit, err := parsePagination(c) - if err != nil { - retBadReq(err, c) - return - } - // Fetch exits from historyDB - tokens, pagination, err := h.GetTokens( - tokenIDs, symbols, name, fromItem, limit, order, - ) - if err != nil { - retSQLErr(err, c) - return - } - - // Build succesfull response - apiTokens := tokensToAPI(tokens) - c.JSON(http.StatusOK, &tokensAPI{ - Tokens: apiTokens, - Pagination: pagination, - }) -} - -func getToken(c *gin.Context) { - // Get TokenID - tokenIDUint, err := parseParamUint("id", nil, 0, maxUint32, c) - if err != nil { - retBadReq(err, c) - return - } - tokenID := common.TokenID(*tokenIDUint) - // Fetch token from historyDB - token, err := h.GetToken(tokenID) - if err != nil { - retSQLErr(err, c) - return - } - apiToken := tokensToAPI([]historydb.TokenRead{*token}) - c.JSON(http.StatusOK, apiToken[0]) -} - func getRecommendedFee(c *gin.Context) { } diff --git a/api/token.go b/api/token.go new file mode 100644 index 0000000..d258a51 --- /dev/null +++ b/api/token.go @@ -0,0 +1,66 @@ +package api + +import ( + "errors" + "net/http" + + "github.com/gin-gonic/gin" + "github.com/hermeznetwork/hermez-node/common" + "github.com/hermeznetwork/hermez-node/db" + "github.com/hermeznetwork/hermez-node/db/historydb" +) + +func getToken(c *gin.Context) { + // Get TokenID + tokenIDUint, err := parseParamUint("id", nil, 0, maxUint32, c) + if err != nil { + retBadReq(err, c) + return + } + if tokenIDUint == nil { // tokenID is required + retBadReq(errors.New("Invalid tokenID"), c) + return + } + tokenID := common.TokenID(*tokenIDUint) + // Fetch token from historyDB + token, err := h.GetToken(tokenID) + if err != nil { + retSQLErr(err, c) + return + } + c.JSON(http.StatusOK, token) +} + +func getTokens(c *gin.Context) { + // Account filters + tokenIDs, symbols, name, err := parseTokenFilters(c) + if err != nil { + retBadReq(err, c) + return + } + + // Pagination + fromItem, order, limit, err := parsePagination(c) + if err != nil { + retBadReq(err, c) + return + } + // Fetch exits from historyDB + tokens, pagination, err := h.GetTokens( + tokenIDs, symbols, name, fromItem, limit, order, + ) + if err != nil { + retSQLErr(err, c) + return + } + + // Build succesfull response + type tokensResponse struct { + Tokens []historydb.TokenWithUSD `json:"tokens"` + Pagination *db.Pagination `json:"pagination"` + } + c.JSON(http.StatusOK, &tokensResponse{ + Tokens: tokens, + Pagination: pagination, + }) +} diff --git a/api/token_test.go b/api/token_test.go new file mode 100644 index 0000000..aa87f4a --- /dev/null +++ b/api/token_test.go @@ -0,0 +1,153 @@ +package api + +import ( + "fmt" + "strconv" + "testing" + + "github.com/hermeznetwork/hermez-node/db" + "github.com/hermeznetwork/hermez-node/db/historydb" + "github.com/mitchellh/copystructure" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +type testTokensResponse struct { + Tokens []historydb.TokenWithUSD `json:"tokens"` + Pagination *db.Pagination `json:"pagination"` +} + +func (t *testTokensResponse) GetPagination() *db.Pagination { + if t.Tokens[0].ItemID < t.Tokens[len(t.Tokens)-1].ItemID { + t.Pagination.FirstReturnedItem = t.Tokens[0].ItemID + t.Pagination.LastReturnedItem = t.Tokens[len(t.Tokens)-1].ItemID + } else { + t.Pagination.LastReturnedItem = t.Tokens[0].ItemID + t.Pagination.FirstReturnedItem = t.Tokens[len(t.Tokens)-1].ItemID + } + return t.Pagination +} + +func (t *testTokensResponse) Len() int { + return len(t.Tokens) +} + +func TestGetToken(t *testing.T) { + // Get all txs by their ID + endpoint := apiURL + "tokens/" + fetchedTokens := []historydb.TokenWithUSD{} + for _, token := range tc.tokens { + fetchedToken := historydb.TokenWithUSD{} + assert.NoError(t, doGoodReq("GET", endpoint+strconv.Itoa(int(token.TokenID)), nil, &fetchedToken)) + fetchedTokens = append(fetchedTokens, fetchedToken) + } + assertTokensAPIs(t, tc.tokens, fetchedTokens) +} + +func TestGetTokens(t *testing.T) { + endpoint := apiURL + "tokens" + fetchedTokens := []historydb.TokenWithUSD{} + appendIter := func(intr interface{}) { + for i := 0; i < len(intr.(*testTokensResponse).Tokens); i++ { + tmp, err := copystructure.Copy(intr.(*testTokensResponse).Tokens[i]) + if err != nil { + panic(err) + } + fetchedTokens = append(fetchedTokens, tmp.(historydb.TokenWithUSD)) + } + } + // Get all (no filters) + limit := 8 + path := fmt.Sprintf("%s?limit=%d&fromItem=", endpoint, limit) + err := doGoodReqPaginated(path, historydb.OrderAsc, &testTokensResponse{}, appendIter) + assert.NoError(t, err) + assertTokensAPIs(t, tc.tokens, fetchedTokens) + + // Get by tokenIds + fetchedTokens = []historydb.TokenWithUSD{} + limit = 7 + stringIds := strconv.Itoa(int(tc.tokens[2].TokenID)) + "," + strconv.Itoa(int(tc.tokens[5].TokenID)) + "," + strconv.Itoa(int(tc.tokens[6].TokenID)) + path = fmt.Sprintf( + "%s?ids=%s&limit=%d&fromItem=", + endpoint, stringIds, limit, + ) + err = doGoodReqPaginated(path, historydb.OrderAsc, &testTokensResponse{}, appendIter) + assert.NoError(t, err) + var tokensFiltered []historydb.TokenWithUSD + tokensFiltered = append(tokensFiltered, tc.tokens[2]) + tokensFiltered = append(tokensFiltered, tc.tokens[5]) + tokensFiltered = append(tokensFiltered, tc.tokens[6]) + assertTokensAPIs(t, tokensFiltered, fetchedTokens) + + // Get by symbols + fetchedTokens = []historydb.TokenWithUSD{} + limit = 7 + stringSymbols := tc.tokens[1].Symbol + "," + tc.tokens[3].Symbol + path = fmt.Sprintf( + "%s?symbols=%s&limit=%d&fromItem=", + endpoint, stringSymbols, limit, + ) + err = doGoodReqPaginated(path, historydb.OrderAsc, &testTokensResponse{}, appendIter) + assert.NoError(t, err) + tokensFiltered = nil + tokensFiltered = append(tokensFiltered, tc.tokens[1]) + tokensFiltered = append(tokensFiltered, tc.tokens[3]) + assertTokensAPIs(t, tokensFiltered, fetchedTokens) + + // Get by name + fetchedTokens = []historydb.TokenWithUSD{} + limit = 5 + stringName := tc.tokens[8].Name[4:5] + path = fmt.Sprintf( + "%s?name=%s&limit=%d&fromItem=", + endpoint, stringName, limit, + ) + err = doGoodReqPaginated(path, historydb.OrderAsc, &testTokensResponse{}, appendIter) + assert.NoError(t, err) + tokensFiltered = nil + tokensFiltered = append(tokensFiltered, tc.tokens[8]) + assertTokensAPIs(t, tokensFiltered, fetchedTokens) + + // Multiple filters + fetchedTokens = []historydb.TokenWithUSD{} + limit = 5 + stringSymbols = tc.tokens[2].Symbol + "," + tc.tokens[6].Symbol + stringIds = strconv.Itoa(int(tc.tokens[2].TokenID)) + "," + strconv.Itoa(int(tc.tokens[5].TokenID)) + "," + strconv.Itoa(int(tc.tokens[6].TokenID)) + path = fmt.Sprintf( + "%s?symbols=%s&ids=%s&limit=%d&fromItem=", + endpoint, stringSymbols, stringIds, limit, + ) + err = doGoodReqPaginated(path, historydb.OrderAsc, &testTokensResponse{}, appendIter) + assert.NoError(t, err) + + tokensFiltered = nil + tokensFiltered = append(tokensFiltered, tc.tokens[2]) + tokensFiltered = append(tokensFiltered, tc.tokens[6]) + assertTokensAPIs(t, tokensFiltered, fetchedTokens) + + // All, in reverse order + fetchedTokens = []historydb.TokenWithUSD{} + limit = 5 + path = fmt.Sprintf("%s?limit=%d&fromItem=", endpoint, limit) + err = doGoodReqPaginated(path, historydb.OrderDesc, &testTokensResponse{}, appendIter) + assert.NoError(t, err) + flipedTokens := []historydb.TokenWithUSD{} + for i := 0; i < len(tc.tokens); i++ { + flipedTokens = append(flipedTokens, tc.tokens[len(tc.tokens)-1-i]) + } + assertTokensAPIs(t, flipedTokens, fetchedTokens) +} + +func assertTokensAPIs(t *testing.T, expected, actual []historydb.TokenWithUSD) { + require.Equal(t, len(expected), len(actual)) + for i := 0; i < len(actual); i++ { //nolint len(actual) won't change within the loop + actual[i].ItemID = 0 + if expected[i].USDUpdate == nil { + assert.Equal(t, expected[i].USDUpdate, actual[i].USDUpdate) + } else { + assert.Equal(t, expected[i].USDUpdate.Unix(), actual[i].USDUpdate.Unix()) + expected[i].USDUpdate = actual[i].USDUpdate + } + assert.Equal(t, expected[i], actual[i]) + } +} diff --git a/db/historydb/historydb.go b/db/historydb/historydb.go index b7eae25..920334d 100644 --- a/db/historydb/historydb.go +++ b/db/historydb/historydb.go @@ -404,8 +404,8 @@ func (hdb *HistoryDB) UpdateTokenValue(tokenSymbol string, value float64) error } // GetToken returns a token from the DB given a TokenID -func (hdb *HistoryDB) GetToken(tokenID common.TokenID) (*TokenRead, error) { - token := &TokenRead{} +func (hdb *HistoryDB) GetToken(tokenID common.TokenID) (*TokenWithUSD, error) { + token := &TokenWithUSD{} err := meddler.QueryRow( hdb.db, token, `SELECT * FROM token WHERE token_id = $1;`, tokenID, ) @@ -413,17 +413,17 @@ func (hdb *HistoryDB) GetToken(tokenID common.TokenID) (*TokenRead, error) { } // GetAllTokens returns all tokens from the DB -func (hdb *HistoryDB) GetAllTokens() ([]TokenRead, error) { - var tokens []*TokenRead +func (hdb *HistoryDB) GetAllTokens() ([]TokenWithUSD, error) { + var tokens []*TokenWithUSD err := meddler.QueryAll( hdb.db, &tokens, "SELECT * FROM token ORDER BY token_id;", ) - return db.SlicePtrsToSlice(tokens).([]TokenRead), err + return db.SlicePtrsToSlice(tokens).([]TokenWithUSD), err } // GetTokens returns a list of tokens from the DB -func (hdb *HistoryDB) GetTokens(ids []common.TokenID, symbols []string, name string, fromItem, limit *uint, order string) ([]TokenRead, *db.Pagination, error) { +func (hdb *HistoryDB) GetTokens(ids []common.TokenID, symbols []string, name string, fromItem, limit *uint, order string) ([]TokenWithUSD, *db.Pagination, error) { var query string var args []interface{} queryStr := `SELECT * , COUNT(*) OVER() AS total_items, MIN(token.item_id) OVER() AS first_item, MAX(token.item_id) OVER() AS last_item FROM token ` @@ -480,14 +480,14 @@ func (hdb *HistoryDB) GetTokens(ids []common.TokenID, symbols []string, name str return nil, nil, err } query = hdb.db.Rebind(query) - tokens := []*TokenRead{} + tokens := []*TokenWithUSD{} if err := meddler.QueryAll(hdb.db, &tokens, query, argsQ...); err != nil { return nil, nil, err } if len(tokens) == 0 { return nil, nil, sql.ErrNoRows } - return db.SlicePtrsToSlice(tokens).([]TokenRead), &db.Pagination{ + return db.SlicePtrsToSlice(tokens).([]TokenWithUSD), &db.Pagination{ TotalItems: tokens[0].TotalItems, FirstItem: tokens[0].FirstItem, LastItem: tokens[0].LastItem, diff --git a/db/historydb/views.go b/db/historydb/views.go index 892a52d..fefb325 100644 --- a/db/historydb/views.go +++ b/db/historydb/views.go @@ -80,20 +80,20 @@ type txWrite struct { Nonce *common.Nonce `meddler:"nonce"` } -// TokenRead add USD info to common.Token -type TokenRead struct { - ItemID int `meddler:"item_id"` - TokenID common.TokenID `meddler:"token_id"` - EthBlockNum int64 `meddler:"eth_block_num"` // Ethereum block number in which this token was registered - EthAddr ethCommon.Address `meddler:"eth_addr"` - Name string `meddler:"name"` - Symbol string `meddler:"symbol"` - Decimals uint64 `meddler:"decimals"` - USD *float64 `meddler:"usd"` - USDUpdate *time.Time `meddler:"usd_update,utctime"` - TotalItems int `meddler:"total_items"` - FirstItem int `meddler:"first_item"` - LastItem int `meddler:"last_item"` +// TokenWithUSD add USD info to common.Token +type TokenWithUSD struct { + ItemID int `json:"itemId" meddler:"item_id"` + TokenID common.TokenID `json:"id" meddler:"token_id"` + EthBlockNum int64 `json:"ethereumBlockNum" meddler:"eth_block_num"` // Ethereum block number in which this token was registered + EthAddr ethCommon.Address `json:"ethereumAddress" meddler:"eth_addr"` + Name string `json:"name" meddler:"name"` + Symbol string `json:"symbol" meddler:"symbol"` + Decimals uint64 `json:"decimals" meddler:"decimals"` + USD *float64 `json:"USD" meddler:"usd"` + USDUpdate *time.Time `json:"fiatUpdate" meddler:"usd_update,utctime"` + TotalItems int `json:"-" meddler:"total_items"` + FirstItem int `json:"-" meddler:"first_item"` + LastItem int `json:"-" meddler:"last_item"` } // HistoryExit is a representation of a exit with additional information diff --git a/db/l2db/l2db_test.go b/db/l2db/l2db_test.go index cd43a36..c4124b0 100644 --- a/db/l2db/l2db_test.go +++ b/db/l2db/l2db_test.go @@ -18,7 +18,7 @@ import ( var l2DB *L2DB var tokens []common.Token -var tokensUSD []historydb.TokenRead +var tokensUSD []historydb.TokenWithUSD func TestMain(m *testing.M) { // init DB @@ -41,7 +41,7 @@ func TestMain(m *testing.M) { os.Exit(result) } -func prepareHistoryDB(db *sqlx.DB) ([]common.Token, []historydb.TokenRead) { +func prepareHistoryDB(db *sqlx.DB) ([]common.Token, []historydb.TokenWithUSD) { historyDB := historydb.NewHistoryDB(db) const fromBlock int64 = 1 const toBlock int64 = 5 @@ -60,9 +60,9 @@ func prepareHistoryDB(db *sqlx.DB) ([]common.Token, []historydb.TokenRead) { if err := historyDB.AddTokens(tokens); err != nil { panic(err) } - readTokens := []historydb.TokenRead{} + readTokens := []historydb.TokenWithUSD{} for i, token := range tokens { - readToken := historydb.TokenRead{ + readToken := historydb.TokenWithUSD{ TokenID: token.TokenID, EthBlockNum: token.EthBlockNum, EthAddr: token.EthAddr, @@ -116,7 +116,7 @@ func commonToRead(commonTx *common.PoolL2Tx, tokens []common.Token) *PoolL2TxRea } // token related fields // find token - token := historydb.TokenRead{} + token := historydb.TokenWithUSD{} for _, tkn := range tokensUSD { if tkn.TokenID == readTx.TokenID { token = tkn @@ -176,7 +176,7 @@ func assertTx(t *testing.T, expected, actual *common.PoolL2Tx) { expected.Timestamp = actual.Timestamp // Check absolute fee // find token - token := historydb.TokenRead{} + token := historydb.TokenWithUSD{} for _, tkn := range tokensUSD { if expected.TokenID == tkn.TokenID { token = tkn diff --git a/synchronizer/synchronizer_test.go b/synchronizer/synchronizer_test.go index 7b73a9b..1c71fc9 100644 --- a/synchronizer/synchronizer_test.go +++ b/synchronizer/synchronizer_test.go @@ -251,10 +251,10 @@ func TestSync(t *testing.T) { assert.Equal(t, tokenConst.Symbol, syncToken.Symbol) assert.Equal(t, tokenConst.Decimals, syncToken.Decimals) - var tokenCpy historydb.TokenRead + var tokenCpy historydb.TokenWithUSD //nolint:gosec - require.Nil(t, copier.Copy(&tokenCpy, &token)) // copy common.Token to historydb.TokenRead - require.Nil(t, copier.Copy(&tokenCpy, &tokenConst)) // copy common.Token to historydb.TokenRead + require.Nil(t, copier.Copy(&tokenCpy, &token)) // copy common.Token to historydb.TokenWithUSD + require.Nil(t, copier.Copy(&tokenCpy, &tokenConst)) // copy common.Token to historydb.TokenWithUSD tokenCpy.ItemID = dbToken.ItemID // we don't care about ItemID assert.Equal(t, tokenCpy, dbToken) }