From a54cacfd132e5a6b834499ae67c73a1ce10cb49e Mon Sep 17 00:00:00 2001 From: Artem Date: Wed, 29 Jan 2025 15:29:28 +0300 Subject: [PATCH] Refactoring: L2Beat change enpoint --- .vscode/launch.json | 11 +++ Makefile | 7 +- internal/storage/mock/namespace.go | 1 - internal/storage/mock/rollup.go | 56 +++++++------- internal/storage/mock/stats.go | 1 - internal/storage/mock/tvl.go | 1 - internal/storage/tvl.go | 14 +--- internal/test_suite/helpers.go | 16 +++- internal/tvl/l2beat/interface.go | 3 +- internal/tvl/l2beat/mock/interface.go | 11 +-- internal/tvl/l2beat/timeframe.go | 17 +++++ internal/tvl/l2beat/timeframe_enum.go | 104 ++++++++++++++++++++++++++ internal/tvl/l2beat/tvl.go | 72 +++++++++--------- internal/tvl/l2beat/tvl_test.go | 40 ++++++++++ internal/tvl/lama/mock/interface.go | 4 +- pkg/tvl/module.go | 51 +++++-------- pkg/tvl/module_test.go | 31 +++++--- 17 files changed, 308 insertions(+), 132 deletions(-) create mode 100644 internal/tvl/l2beat/timeframe.go create mode 100644 internal/tvl/l2beat/timeframe_enum.go create mode 100644 internal/tvl/l2beat/tvl_test.go diff --git a/.vscode/launch.json b/.vscode/launch.json index c49e60f8..04132a56 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -26,6 +26,17 @@ "../../configs/dipdup.yml", ], "envFile": "${workspaceFolder}/.env" + }, { + "name": "Launch TVL", + "type": "go", + "request": "launch", + "mode": "debug", + "program": "${workspaceFolder}/cmd/tvl", + "args": [ + "-c", + "../../configs/dipdup.yml", + ], + "envFile": "${workspaceFolder}/.env" } ] } \ No newline at end of file diff --git a/Makefile b/Makefile index 6d125e6e..fb952303 100644 --- a/Makefile +++ b/Makefile @@ -13,6 +13,9 @@ api: quotes: cd cmd/quotes && go run . -c ../../configs/dipdup.yml +tvl: + cd cmd/tvl && go run . -c ../../configs/dipdup.yml + build: cd cmd/indexer && go build -a -o ../../bin/indexer . cd cmd/api && go build -a -o ../../bin/api . @@ -40,7 +43,7 @@ adr: @cp adr/adr-template.md adr/adr-$(NUM)-$(TITLE).md generate: - go generate -v ./internal/blob ./internal/storage ./internal/storage/types ./pkg/node ./internal/binance + go generate -v ./internal/blob ./internal/storage ./internal/storage/types ./pkg/node ./internal/binance ./internal/tvl/l2beat ./internal/tvl/lama api-docs: cd cmd/api && swag init --md markdown -parseDependency --parseInternal --parseDepth 1 --outputTypes json @@ -78,4 +81,4 @@ cover: license-header: update-license -path=./ -license=./HEADER -.PHONY: init indexer api build clean compose lint test adr mock api-docs check-licenses cover license-header +.PHONY: init indexer api build clean compose lint test adr mock api-docs check-licenses cover license-header tvl diff --git a/internal/storage/mock/namespace.go b/internal/storage/mock/namespace.go index b952ee33..ef60030c 100644 --- a/internal/storage/mock/namespace.go +++ b/internal/storage/mock/namespace.go @@ -25,7 +25,6 @@ import ( type MockINamespace struct { ctrl *gomock.Controller recorder *MockINamespaceMockRecorder - isgomock struct{} } // MockINamespaceMockRecorder is the mock recorder for MockINamespace. diff --git a/internal/storage/mock/rollup.go b/internal/storage/mock/rollup.go index 96a2201f..1818fb52 100644 --- a/internal/storage/mock/rollup.go +++ b/internal/storage/mock/rollup.go @@ -744,80 +744,80 @@ func (c *MockIRollupSeriesCall) DoAndReturn(f func(context.Context, uint64, stor return c } -// Tvl mocks base method. -func (m *MockIRollup) Tvl(ctx context.Context, rollupId uint64, timeframe storage.Timeframe, req storage.SeriesRequest) ([]storage.HistogramItem, error) { +// Tags mocks base method. +func (m *MockIRollup) Tags(ctx context.Context) ([]string, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Tvl", ctx, rollupId, timeframe, req) - ret0, _ := ret[0].([]storage.HistogramItem) + ret := m.ctrl.Call(m, "Tags", ctx) + ret0, _ := ret[0].([]string) ret1, _ := ret[1].(error) return ret0, ret1 } -// Tvl indicates an expected call of Tvl. -func (mr *MockIRollupMockRecorder) Tvl(ctx, rollupId, timeframe, req any) *MockIRollupTvlCall { +// Tags indicates an expected call of Tags. +func (mr *MockIRollupMockRecorder) Tags(ctx any) *MockIRollupTagsCall { mr.mock.ctrl.T.Helper() - call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Tvl", reflect.TypeOf((*MockIRollup)(nil).Tvl), ctx, rollupId, timeframe, req) - return &MockIRollupTvlCall{Call: call} + call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Tags", reflect.TypeOf((*MockIRollup)(nil).Tags), ctx) + return &MockIRollupTagsCall{Call: call} } -// MockIRollupTvlCall wrap *gomock.Call -type MockIRollupTvlCall struct { +// MockIRollupTagsCall wrap *gomock.Call +type MockIRollupTagsCall struct { *gomock.Call } // Return rewrite *gomock.Call.Return -func (c *MockIRollupTvlCall) Return(items []storage.HistogramItem, err error) *MockIRollupTvlCall { - c.Call = c.Call.Return(items, err) +func (c *MockIRollupTagsCall) Return(arg0 []string, arg1 error) *MockIRollupTagsCall { + c.Call = c.Call.Return(arg0, arg1) return c } // Do rewrite *gomock.Call.Do -func (c *MockIRollupTvlCall) Do(f func(context.Context, uint64, storage.Timeframe, storage.SeriesRequest) ([]storage.HistogramItem, error)) *MockIRollupTvlCall { +func (c *MockIRollupTagsCall) Do(f func(context.Context) ([]string, error)) *MockIRollupTagsCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn -func (c *MockIRollupTvlCall) DoAndReturn(f func(context.Context, uint64, storage.Timeframe, storage.SeriesRequest) ([]storage.HistogramItem, error)) *MockIRollupTvlCall { +func (c *MockIRollupTagsCall) DoAndReturn(f func(context.Context) ([]string, error)) *MockIRollupTagsCall { c.Call = c.Call.DoAndReturn(f) return c } -// Tags mocks base method. -func (m *MockIRollup) Tags(ctx context.Context) ([]string, error) { +// Tvl mocks base method. +func (m *MockIRollup) Tvl(ctx context.Context, rollupId uint64, timeframe storage.Timeframe, req storage.SeriesRequest) ([]storage.HistogramItem, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Tags", ctx) - ret0, _ := ret[0].([]string) + ret := m.ctrl.Call(m, "Tvl", ctx, rollupId, timeframe, req) + ret0, _ := ret[0].([]storage.HistogramItem) ret1, _ := ret[1].(error) return ret0, ret1 } -// Tags indicates an expected call of Tags. -func (mr *MockIRollupMockRecorder) Tags(ctx any) *MockIRollupTagsCall { +// Tvl indicates an expected call of Tvl. +func (mr *MockIRollupMockRecorder) Tvl(ctx, rollupId, timeframe, req any) *MockIRollupTvlCall { mr.mock.ctrl.T.Helper() - call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Tags", reflect.TypeOf((*MockIRollup)(nil).Tags), ctx) - return &MockIRollupTagsCall{Call: call} + call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Tvl", reflect.TypeOf((*MockIRollup)(nil).Tvl), ctx, rollupId, timeframe, req) + return &MockIRollupTvlCall{Call: call} } -// MockIRollupTagsCall wrap *gomock.Call -type MockIRollupTagsCall struct { +// MockIRollupTvlCall wrap *gomock.Call +type MockIRollupTvlCall struct { *gomock.Call } // Return rewrite *gomock.Call.Return -func (c *MockIRollupTagsCall) Return(arg0 []string, arg1 error) *MockIRollupTagsCall { - c.Call = c.Call.Return(arg0, arg1) +func (c *MockIRollupTvlCall) Return(items []storage.HistogramItem, err error) *MockIRollupTvlCall { + c.Call = c.Call.Return(items, err) return c } // Do rewrite *gomock.Call.Do -func (c *MockIRollupTagsCall) Do(f func(context.Context) ([]string, error)) *MockIRollupTagsCall { +func (c *MockIRollupTvlCall) Do(f func(context.Context, uint64, storage.Timeframe, storage.SeriesRequest) ([]storage.HistogramItem, error)) *MockIRollupTvlCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn -func (c *MockIRollupTagsCall) DoAndReturn(f func(context.Context) ([]string, error)) *MockIRollupTagsCall { +func (c *MockIRollupTvlCall) DoAndReturn(f func(context.Context, uint64, storage.Timeframe, storage.SeriesRequest) ([]storage.HistogramItem, error)) *MockIRollupTvlCall { c.Call = c.Call.DoAndReturn(f) return c } diff --git a/internal/storage/mock/stats.go b/internal/storage/mock/stats.go index b948cbfb..afd538e2 100644 --- a/internal/storage/mock/stats.go +++ b/internal/storage/mock/stats.go @@ -26,7 +26,6 @@ import ( type MockIStats struct { ctrl *gomock.Controller recorder *MockIStatsMockRecorder - isgomock struct{} } // MockIStatsMockRecorder is the mock recorder for MockIStats. diff --git a/internal/storage/mock/tvl.go b/internal/storage/mock/tvl.go index 0e20e4b5..8b9c15c8 100644 --- a/internal/storage/mock/tvl.go +++ b/internal/storage/mock/tvl.go @@ -25,7 +25,6 @@ import ( type MockITvl struct { ctrl *gomock.Controller recorder *MockITvlMockRecorder - isgomock struct{} } // MockITvlMockRecorder is the mock recorder for MockITvl. diff --git a/internal/storage/tvl.go b/internal/storage/tvl.go index ae732442..193f240f 100644 --- a/internal/storage/tvl.go +++ b/internal/storage/tvl.go @@ -5,20 +5,10 @@ package storage import ( "context" - "github.com/shopspring/decimal" - "github.com/uptrace/bun" "time" -) -type TvlTimeframe string - -const ( - TvlTimeframeWeek TvlTimeframe = "7d" - TvlTimeframeMonth TvlTimeframe = "30d" - TvlTimeframe3Month TvlTimeframe = "90d" - TvlTimeframe6Month TvlTimeframe = "180d" - TvlTimeframeYear TvlTimeframe = "1y" - TvlTimeframeMax TvlTimeframe = "max" + "github.com/shopspring/decimal" + "github.com/uptrace/bun" ) //go:generate mockgen -source=$GOFILE -destination=mock/$GOFILE -package=mock -typed diff --git a/internal/test_suite/helpers.go b/internal/test_suite/helpers.go index fbcfc2f0..1c423311 100644 --- a/internal/test_suite/helpers.go +++ b/internal/test_suite/helpers.go @@ -3,7 +3,13 @@ package testsuite -import "encoding/hex" +import ( + "crypto/rand" + "encoding/hex" + "math/big" + + "github.com/shopspring/decimal" +) // Ptr - returns pointer of value for testing purpose // @@ -22,3 +28,11 @@ func MustHexDecode(s string) []byte { } return data } + +// RandomDecimal - returns random decimal value +// +// data := RandomDecimal() +func RandomDecimal() decimal.Decimal { + val, _ := rand.Int(rand.Reader, big.NewInt(1000)) + return decimal.NewFromBigInt(val, 1) +} diff --git a/internal/tvl/l2beat/interface.go b/internal/tvl/l2beat/interface.go index 3c5768d6..da391751 100644 --- a/internal/tvl/l2beat/interface.go +++ b/internal/tvl/l2beat/interface.go @@ -5,10 +5,9 @@ package l2beat import ( "context" - "github.com/celenium-io/celestia-indexer/internal/storage" ) //go:generate mockgen -source=$GOFILE -destination=mock/$GOFILE -package=mock -typed type IApi interface { - TVL(ctx context.Context, rollupName string, timeframe storage.TvlTimeframe) (result TVLResponse, err error) + TVL(ctx context.Context, rollupName string, timeframe TvlTimeframe) (result TVLResponse, err error) } diff --git a/internal/tvl/l2beat/mock/interface.go b/internal/tvl/l2beat/mock/interface.go index 5759fc96..501ed8eb 100644 --- a/internal/tvl/l2beat/mock/interface.go +++ b/internal/tvl/l2beat/mock/interface.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: 2024 PK Lab AG +// SPDX-License-Identifier: MIT + // Code generated by MockGen. DO NOT EDIT. // Source: interface.go // @@ -13,7 +16,6 @@ import ( context "context" reflect "reflect" - storage "github.com/celenium-io/celestia-indexer/internal/storage" l2beat "github.com/celenium-io/celestia-indexer/internal/tvl/l2beat" gomock "go.uber.org/mock/gomock" ) @@ -22,7 +24,6 @@ import ( type MockIApi struct { ctrl *gomock.Controller recorder *MockIApiMockRecorder - isgomock struct{} } // MockIApiMockRecorder is the mock recorder for MockIApi. @@ -43,7 +44,7 @@ func (m *MockIApi) EXPECT() *MockIApiMockRecorder { } // TVL mocks base method. -func (m *MockIApi) TVL(ctx context.Context, rollupName string, timeframe storage.TvlTimeframe) (l2beat.TVLResponse, error) { +func (m *MockIApi) TVL(ctx context.Context, rollupName string, timeframe l2beat.TvlTimeframe) (l2beat.TVLResponse, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "TVL", ctx, rollupName, timeframe) ret0, _ := ret[0].(l2beat.TVLResponse) @@ -70,13 +71,13 @@ func (c *MockIApiTVLCall) Return(result l2beat.TVLResponse, err error) *MockIApi } // Do rewrite *gomock.Call.Do -func (c *MockIApiTVLCall) Do(f func(context.Context, string, storage.TvlTimeframe) (l2beat.TVLResponse, error)) *MockIApiTVLCall { +func (c *MockIApiTVLCall) Do(f func(context.Context, string, l2beat.TvlTimeframe) (l2beat.TVLResponse, error)) *MockIApiTVLCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn -func (c *MockIApiTVLCall) DoAndReturn(f func(context.Context, string, storage.TvlTimeframe) (l2beat.TVLResponse, error)) *MockIApiTVLCall { +func (c *MockIApiTVLCall) DoAndReturn(f func(context.Context, string, l2beat.TvlTimeframe) (l2beat.TVLResponse, error)) *MockIApiTVLCall { c.Call = c.Call.DoAndReturn(f) return c } diff --git a/internal/tvl/l2beat/timeframe.go b/internal/tvl/l2beat/timeframe.go new file mode 100644 index 00000000..36a5c544 --- /dev/null +++ b/internal/tvl/l2beat/timeframe.go @@ -0,0 +1,17 @@ +// SPDX-FileCopyrightText: 2024 PK Lab AG +// SPDX-License-Identifier: MIT + +package l2beat + +/* + ENUM( + 7d, + 30d, + 90d, + 180d, + 1y, + max + ) +*/ +//go:generate go-enum --marshal --values --names +type TvlTimeframe string diff --git a/internal/tvl/l2beat/timeframe_enum.go b/internal/tvl/l2beat/timeframe_enum.go new file mode 100644 index 00000000..fa6dfc95 --- /dev/null +++ b/internal/tvl/l2beat/timeframe_enum.go @@ -0,0 +1,104 @@ +// SPDX-FileCopyrightText: 2024 PK Lab AG +// SPDX-License-Identifier: MIT + +// Code generated by go-enum DO NOT EDIT. +// Version: 0.5.7 +// Revision: bf63e108589bbd2327b13ec2c5da532aad234029 +// Build Date: 2023-07-25T23:27:55Z +// Built By: goreleaser + +package l2beat + +import ( + "fmt" + "strings" +) + +const ( + // TvlTimeframe7D is a TvlTimeframe of type 7d. + TvlTimeframe7D TvlTimeframe = "7d" + // TvlTimeframe30D is a TvlTimeframe of type 30d. + TvlTimeframe30D TvlTimeframe = "30d" + // TvlTimeframe90D is a TvlTimeframe of type 90d. + TvlTimeframe90D TvlTimeframe = "90d" + // TvlTimeframe180D is a TvlTimeframe of type 180d. + TvlTimeframe180D TvlTimeframe = "180d" + // TvlTimeframe1Y is a TvlTimeframe of type 1y. + TvlTimeframe1Y TvlTimeframe = "1y" + // TvlTimeframeMax is a TvlTimeframe of type max. + TvlTimeframeMax TvlTimeframe = "max" +) + +var ErrInvalidTvlTimeframe = fmt.Errorf("not a valid TvlTimeframe, try [%s]", strings.Join(_TvlTimeframeNames, ", ")) + +var _TvlTimeframeNames = []string{ + string(TvlTimeframe7D), + string(TvlTimeframe30D), + string(TvlTimeframe90D), + string(TvlTimeframe180D), + string(TvlTimeframe1Y), + string(TvlTimeframeMax), +} + +// TvlTimeframeNames returns a list of possible string values of TvlTimeframe. +func TvlTimeframeNames() []string { + tmp := make([]string, len(_TvlTimeframeNames)) + copy(tmp, _TvlTimeframeNames) + return tmp +} + +// TvlTimeframeValues returns a list of the values for TvlTimeframe +func TvlTimeframeValues() []TvlTimeframe { + return []TvlTimeframe{ + TvlTimeframe7D, + TvlTimeframe30D, + TvlTimeframe90D, + TvlTimeframe180D, + TvlTimeframe1Y, + TvlTimeframeMax, + } +} + +// String implements the Stringer interface. +func (x TvlTimeframe) String() string { + return string(x) +} + +// IsValid provides a quick way to determine if the typed value is +// part of the allowed enumerated values +func (x TvlTimeframe) IsValid() bool { + _, err := ParseTvlTimeframe(string(x)) + return err == nil +} + +var _TvlTimeframeValue = map[string]TvlTimeframe{ + "7d": TvlTimeframe7D, + "30d": TvlTimeframe30D, + "90d": TvlTimeframe90D, + "180d": TvlTimeframe180D, + "1y": TvlTimeframe1Y, + "max": TvlTimeframeMax, +} + +// ParseTvlTimeframe attempts to convert a string to a TvlTimeframe. +func ParseTvlTimeframe(name string) (TvlTimeframe, error) { + if x, ok := _TvlTimeframeValue[name]; ok { + return x, nil + } + return TvlTimeframe(""), fmt.Errorf("%s is %w", name, ErrInvalidTvlTimeframe) +} + +// MarshalText implements the text marshaller method. +func (x TvlTimeframe) MarshalText() ([]byte, error) { + return []byte(string(x)), nil +} + +// UnmarshalText implements the text unmarshaller method. +func (x *TvlTimeframe) UnmarshalText(text []byte) error { + tmp, err := ParseTvlTimeframe(string(text)) + if err != nil { + return err + } + *x = tmp + return nil +} diff --git a/internal/tvl/l2beat/tvl.go b/internal/tvl/l2beat/tvl.go index 9bda75b7..41810a80 100644 --- a/internal/tvl/l2beat/tvl.go +++ b/internal/tvl/l2beat/tvl.go @@ -7,59 +7,61 @@ import ( "context" "encoding/json" "fmt" - "github.com/celenium-io/celestia-indexer/internal/storage" + "time" + + "github.com/pkg/errors" + "github.com/shopspring/decimal" ) -type Data struct { - Json [][]interface{} `json:"json"` +type TVLResponse struct { + Data Data `json:"data"` + Success bool `json:"success"` + Error string `json:"error"` } type Result struct { Data Data `json:"data"` } -type TVLResponse []struct { - Result Result `json:"result"` -} - -type RequestData map[string]struct { - Json JsonData `json:"json"` +type Data struct { + Usd float64 `json:"usdValue"` + Eth float64 `json:"ethValue"` + Chart Chart `json:"chart"` } -type JsonData struct { - Filter Filter `json:"filter"` - Range string `json:"range"` - ExcludeAssociatedTokens bool `json:"excludeAssociatedTokens"` +type Chart struct { + Types []string `json:"types"` + Data []Item `json:"data"` } -type Filter struct { - Type string `json:"type"` - ProjectIds []string `json:"projectIds"` +type Item struct { + Time time.Time + Native decimal.Decimal + Canonical decimal.Decimal + External decimal.Decimal + EthPrice decimal.Decimal } -func (api API) TVL(ctx context.Context, rollupName string, timeframe storage.TvlTimeframe) (result TVLResponse, err error) { - data := RequestData{ - "0": { - JsonData{ - Filter: Filter{ - Type: "projects", - ProjectIds: []string{rollupName}, - }, - Range: string(timeframe), - ExcludeAssociatedTokens: false, - }, - }, +func (item *Item) UnmarshalJSON(data []byte) error { + var items []float64 + if err := json.Unmarshal(data, &items); err != nil { + return err } - - var dataString, err1 = json.Marshal(data) - if err1 != nil { - return nil, fmt.Errorf("serialization error: %v", err) + if len(items) != 5 { + return errors.Errorf("invalid chart item: %s", string(data)) } + item.Time = time.Unix(int64(items[0]), 0).UTC() + item.Native = decimal.NewFromFloat(items[1]) + item.Canonical = decimal.NewFromFloat(items[2]) + item.External = decimal.NewFromFloat(items[3]) + item.EthPrice = decimal.NewFromFloat(items[4]) + return nil +} +func (api API) TVL(ctx context.Context, rollupName string, timeframe TvlTimeframe) (result TVLResponse, err error) { args := make(map[string]string) - args["batch"] = "1" - args["input"] = string(dataString) + args["range"] = timeframe.String() - err = api.get(ctx, "trpc/tvl.chart", args, &result) + err = api.get(ctx, fmt.Sprintf("scaling/tvl/%s", rollupName), args, &result) return } diff --git a/internal/tvl/l2beat/tvl_test.go b/internal/tvl/l2beat/tvl_test.go new file mode 100644 index 00000000..1a3e61dc --- /dev/null +++ b/internal/tvl/l2beat/tvl_test.go @@ -0,0 +1,40 @@ +// SPDX-FileCopyrightText: 2024 PK Lab AG +// SPDX-License-Identifier: MIT + +package l2beat + +import ( + "testing" + "time" + + "github.com/shopspring/decimal" + "github.com/stretchr/testify/require" +) + +func TestItem_UnmarshalJSON(t *testing.T) { + tests := []struct { + name string + data []byte + want Item + }{ + { + name: "test 1", + data: []byte(`[1735516800,7731223048.1,5923475975.24,4919978970.45,3347.85]`), + want: Item{ + Time: time.Date(2024, 12, 30, 0, 0, 0, 0, time.UTC), + Native: decimal.RequireFromString("7731223048.1"), + Canonical: decimal.RequireFromString("5923475975.24"), + External: decimal.RequireFromString("4919978970.45"), + EthPrice: decimal.RequireFromString("3347.85"), + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + var item Item + err := item.UnmarshalJSON(tt.data) + require.NoError(t, err) + require.Equal(t, tt.want, item) + }) + } +} diff --git a/internal/tvl/lama/mock/interface.go b/internal/tvl/lama/mock/interface.go index c161da35..41d49fe8 100644 --- a/internal/tvl/lama/mock/interface.go +++ b/internal/tvl/lama/mock/interface.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: 2024 PK Lab AG +// SPDX-License-Identifier: MIT + // Code generated by MockGen. DO NOT EDIT. // Source: interface.go // @@ -21,7 +24,6 @@ import ( type MockIApi struct { ctrl *gomock.Controller recorder *MockIApiMockRecorder - isgomock struct{} } // MockIApiMockRecorder is the mock recorder for MockIApi. diff --git a/pkg/tvl/module.go b/pkg/tvl/module.go index 9c8f48de..2a57dcec 100644 --- a/pkg/tvl/module.go +++ b/pkg/tvl/module.go @@ -5,17 +5,19 @@ package tvl import ( "context" - "fmt" + "net/url" + "strings" + "time" + "github.com/celenium-io/celestia-indexer/internal/storage" "github.com/celenium-io/celestia-indexer/internal/tvl/l2beat" "github.com/celenium-io/celestia-indexer/internal/tvl/lama" "github.com/dipdup-net/go-lib/config" "github.com/dipdup-net/indexer-sdk/pkg/modules" strg "github.com/dipdup-net/indexer-sdk/pkg/storage" + "github.com/pkg/errors" "github.com/rs/zerolog" "github.com/shopspring/decimal" - "strings" - "time" ) const ( @@ -58,7 +60,7 @@ func (m *Module) Start(ctx context.Context) { m.G.GoCtx(ctx, m.receive) } -func (m *Module) getTvl(ctx context.Context, timeframe storage.TvlTimeframe) { +func (m *Module) getTvl(ctx context.Context, timeframe l2beat.TvlTimeframe) { rollups, err := m.rollup.List(ctx, rollupLimit, 0, strg.SortOrderAsc) if err != nil { m.Log.Err(err).Msg("receiving rollups") @@ -79,43 +81,28 @@ func (m *Module) getTvl(ctx context.Context, timeframe storage.TvlTimeframe) { m.Log.Info().Msg("Sync rollup TVL is completed") } -func (m *Module) save(ctx context.Context, rollup *storage.Rollup, timeframe storage.TvlTimeframe) error { +func (m *Module) save(ctx context.Context, rollup *storage.Rollup, timeframe l2beat.TvlTimeframe) error { if len(rollup.L2Beat) > 0 { - url := rollup.L2Beat - lastIndex := strings.LastIndex(url, "/") - - if lastIndex == -1 { - return fmt.Errorf("incorrect L2Beat url") + if _, err := url.Parse(rollup.L2Beat); err != nil { + return errors.Wrap(err, "invalid L2Beat url") } + urlParts := strings.Split(rollup.L2Beat, "/") + rollupProject := urlParts[len(urlParts)-1] - rollupProject := url[lastIndex+1:] tvl, err := m.rollupTvlFromL2Beat(ctx, rollupProject, timeframe) - if err != nil { m.Log.Err(err).Msg("receiving TVL from L2Beat") return err } m.Log.Info().Str("rollup", rollup.Name).Msg("receiving TVL from L2Beat") - tvlResponse := tvl[0].Result.Data.Json - tvlModels := make([]*storage.Tvl, 0) - for _, t := range tvlResponse { - if _, ok := t[0].(float64); !ok { - return fmt.Errorf("incorrect value type of TVL timestamp") - } - - for i := 1; i <= 3; i++ { - if _, ok := t[i].(float64); !ok { - return fmt.Errorf("incorrect value type of TVL") - } - } - rollupTvl := (t[1].(float64) + t[2].(float64) + t[3].(float64)) / 100 - tvlTs := time.Unix(int64(t[0].(float64)), 0) - if tvlTs.After(syncTimestamp) { + tvlModels := make([]*storage.Tvl, 0) + for _, t := range tvl.Data.Chart.Data { + if t.Time.After(syncTimestamp) { tvlModels = append(tvlModels, &storage.Tvl{ - Value: decimal.NewFromFloat(rollupTvl), - Time: tvlTs, + Value: decimal.Sum(t.Canonical, t.External, t.Native), + Time: t.Time, Rollup: rollup, RollupId: rollup.Id, }) @@ -178,7 +165,7 @@ func (m *Module) receive(ctx context.Context) { } syncTimestamp = syncTime - m.getTvl(ctx, storage.TvlTimeframeMax) + m.getTvl(ctx, l2beat.TvlTimeframeMax) ticker := time.NewTicker(time.Hour * 24) defer ticker.Stop() @@ -187,7 +174,7 @@ func (m *Module) receive(ctx context.Context) { case <-ctx.Done(): return case <-ticker.C: - m.getTvl(ctx, storage.TvlTimeframe6Month) + m.getTvl(ctx, l2beat.TvlTimeframe180D) } } } @@ -198,7 +185,7 @@ func (m *Module) rollupTvlFromLama(ctx context.Context, rollupName string) ([]la return m.lamaApi.TVL(requestTimeout, rollupName) } -func (m *Module) rollupTvlFromL2Beat(ctx context.Context, rollupName string, timeframe storage.TvlTimeframe) (l2beat.TVLResponse, error) { +func (m *Module) rollupTvlFromL2Beat(ctx context.Context, rollupName string, timeframe l2beat.TvlTimeframe) (l2beat.TVLResponse, error) { requestTimeout, cancel := context.WithTimeout(ctx, time.Second*10) defer cancel() return m.l2beatApi.TVL(requestTimeout, rollupName, timeframe) diff --git a/pkg/tvl/module_test.go b/pkg/tvl/module_test.go index 234d2937..46721c78 100644 --- a/pkg/tvl/module_test.go +++ b/pkg/tvl/module_test.go @@ -5,8 +5,12 @@ package tvl import ( "context" + "testing" + "time" + "github.com/celenium-io/celestia-indexer/internal/storage" "github.com/celenium-io/celestia-indexer/internal/storage/mock" + testsuite "github.com/celenium-io/celestia-indexer/internal/test_suite" "github.com/celenium-io/celestia-indexer/internal/tvl/l2beat" l2beatMock "github.com/celenium-io/celestia-indexer/internal/tvl/l2beat/mock" "github.com/celenium-io/celestia-indexer/internal/tvl/lama" @@ -15,8 +19,6 @@ import ( strg "github.com/dipdup-net/indexer-sdk/pkg/storage" "github.com/stretchr/testify/require" "go.uber.org/mock/gomock" - "testing" - "time" ) func TestReceiver_tvl(t *testing.T) { @@ -49,18 +51,25 @@ func TestReceiver_tvl(t *testing.T) { Return([]lama.TVLResponse{{Date: 1733529600, TVL: 12345678.90}}, nil). Times(1) - l2beatTestResponse := make([][]interface{}, 2) + l2beatTestResponse := make([]l2beat.Item, 2) for i := range l2beatTestResponse { - l2beatTestResponse[i] = make([]interface{}, 4) - for j := range l2beatTestResponse[i] { - l2beatTestResponse[i][j] = float64(i * j) - } - l2beatTestResponse[i][0] = float64(time.Now().Unix()) + l2beatTestResponse[i].Time = time.Now() + l2beatTestResponse[i].Canonical = testsuite.RandomDecimal() + l2beatTestResponse[i].External = testsuite.RandomDecimal() + l2beatTestResponse[i].Native = testsuite.RandomDecimal() + l2beatTestResponse[i].EthPrice = testsuite.RandomDecimal() } l2BeatApi.EXPECT(). - TVL(gomock.Any(), gomock.Any(), storage.TvlTimeframeMonth). - Return(l2beat.TVLResponse{{l2beat.Result{Data: l2beat.Data{Json: l2beatTestResponse}}}}, nil). + TVL(gomock.Any(), gomock.Any(), l2beat.TvlTimeframe30D). + Return(l2beat.TVLResponse{ + Success: true, + Data: l2beat.Data{ + Chart: l2beat.Chart{ + Data: l2beatTestResponse, + }, + }, + }, nil). Times(1) tvlMock.EXPECT(). @@ -76,7 +85,7 @@ func TestReceiver_tvl(t *testing.T) { require.Equal(t, testRollups, result) for i := range testRollups { - err = module.save(ctx, testRollups[i], storage.TvlTimeframeMonth) + err = module.save(ctx, testRollups[i], l2beat.TvlTimeframe30D) require.NoError(t, err) } }