Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

API-132: Implement getMarketSummary Method #16

Merged
merged 2 commits into from
Sep 13, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 30 additions & 0 deletions mocks/service/mockService.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

31 changes: 31 additions & 0 deletions models/marketData.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,24 @@ type MarketMetadata struct {
Symbol string
}

// MarketSummary is a market summary data struct
// - MarketID - Represents the ID of the market
// - Skew - Represents the skew of the market
// - Size - Represents the size of the market
// - MaxOpenInterest - Represents the maximum open interest of the market
// - CurrentFundingRate - Represents the current funding rate of the market
// - CurrentFundingVelocity - Represents the current funding velocity of the market
// - IndexPrice - Represents the index price of the market
type MarketSummary struct {
MarketID *big.Int
Skew *big.Int
Size *big.Int
MaxOpenInterest *big.Int
CurrentFundingRate *big.Int
CurrentFundingVelocity *big.Int
IndexPrice *big.Int
}

// GetMarketUpdateFromEvent is used to get MarketUpdate struct from given event and block timestamp
func GetMarketUpdateFromEvent(event *perpsMarketGoerli.PerpsMarketGoerliMarketUpdated, time uint64) *MarketUpdate {
if event == nil {
Expand Down Expand Up @@ -104,3 +122,16 @@ func GetMarketMetadataFromContractResponse(id *big.Int, name string, symbol stri
Symbol: symbol,
}
}

// GetMarketSummaryFromContractModel is used to get MarketSummary from contract data struct
func GetMarketSummaryFromContractModel(summary perpsMarketGoerli.IPerpsMarketModuleMarketSummary, marketID *big.Int) *MarketSummary {
return &MarketSummary{
MarketID: marketID,
Skew: summary.Skew,
Size: summary.Size,
MaxOpenInterest: summary.MaxOpenInterest,
CurrentFundingRate: summary.CurrentFundingRate,
CurrentFundingVelocity: summary.CurrentFundingVelocity,
IndexPrice: summary.IndexPrice,
}
}
43 changes: 43 additions & 0 deletions models/marketData_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -183,3 +183,46 @@ func TestGetMarketMetadataFromContractResponse(t *testing.T) {
})
}
}

func TestGetMarketSummaryFromContractModel(t *testing.T) {
testCases := []struct {
name string
summary perpsMarketGoerli.IPerpsMarketModuleMarketSummary
marketID *big.Int
want *MarketSummary
}{
{
name: "nil object",
want: &MarketSummary{},
},
{
name: "full object",
summary: perpsMarketGoerli.IPerpsMarketModuleMarketSummary{
Skew: big.NewInt(100),
Size: big.NewInt(200),
MaxOpenInterest: big.NewInt(300),
CurrentFundingRate: big.NewInt(400),
CurrentFundingVelocity: big.NewInt(500),
IndexPrice: big.NewInt(600),
},
marketID: big.NewInt(10),
want: &MarketSummary{
MarketID: big.NewInt(10),
Skew: big.NewInt(100),
Size: big.NewInt(200),
MaxOpenInterest: big.NewInt(300),
CurrentFundingRate: big.NewInt(400),
CurrentFundingVelocity: big.NewInt(500),
IndexPrice: big.NewInt(600),
},
},
}

for _, tt := range testCases {
t.Run(tt.name, func(t *testing.T) {
res := GetMarketSummaryFromContractModel(tt.summary, tt.marketID)

require.Equal(t, tt.want, res)
})
}
}
14 changes: 14 additions & 0 deletions perpsv3.go
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,12 @@ type IPerpsv3 interface {
// in the smart contract
GetMarketMetadata(marketID *big.Int) (*models.MarketMetadata, error)

// GetMarketSummary is used to get market summary by given market ID. Given market id cannot be nil
GetMarketSummary(marketID *big.Int) (*models.MarketSummary, error)

// GetMarketIDs is used to get market IDs from the smart contract
GetMarketIDs() ([]*big.Int, error)

// FormatAccount is used to get account, and it's additional data from the contract by given account id
FormatAccount(id *big.Int) (*models.Account, error)

Expand Down Expand Up @@ -179,6 +185,14 @@ func (p *Perpsv3) GetMarketMetadata(marketID *big.Int) (*models.MarketMetadata,
return p.service.GetMarketMetadata(marketID)
}

func (p *Perpsv3) GetMarketSummary(marketID *big.Int) (*models.MarketSummary, error) {
return p.service.GetMarketSummary(marketID)
}

func (p *Perpsv3) GetMarketIDs() ([]*big.Int, error) {
return p.service.GetMarketIDs()
}

func (p *Perpsv3) FormatAccounts() ([]*models.Account, error) {
return p.service.FormatAccounts()
}
Expand Down
50 changes: 50 additions & 0 deletions perpsv3_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1201,3 +1201,53 @@ func TestPerpsv3_FormatAccountsLimit(t *testing.T) {
})
}
}

func TestPerpsv3_GetMarketSummary(t *testing.T) {
testCases := []struct {
name string
marketID *big.Int
summary *models.MarketSummary
wantErr error
}{
{
name: "no error",
marketID: big.NewInt(100),
summary: &models.MarketSummary{
MarketID: big.NewInt(100),
Size: big.NewInt(200),
Skew: big.NewInt(300),
MaxOpenInterest: big.NewInt(400),
CurrentFundingRate: big.NewInt(500),
CurrentFundingVelocity: big.NewInt(600),
IndexPrice: big.NewInt(700),
},
},
{
name: "error",
marketID: big.NewInt(100),
wantErr: errors.InvalidArgumentErr,
},
}

for _, tt := range testCases {
t.Run(tt.name, func(t *testing.T) {
ctrl := gomock.NewController(t)
defer ctrl.Finish()
mockService := mock_services.NewMockIService(ctrl)

p, _ := createTest(config.GetGoerliDefaultPerpsvConfig())
p.service = mockService

mockService.EXPECT().GetMarketSummary(tt.marketID).Return(tt.summary, tt.wantErr)

res, err := p.GetMarketSummary(tt.marketID)

if tt.wantErr == nil {
require.NoError(t, err)
require.Equal(t, tt.summary, res)
} else {
require.ErrorIs(t, tt.wantErr, err)
}
})
}
}
30 changes: 29 additions & 1 deletion services/marketData.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,6 @@ func (s *Service) GetMarketMetadata(marketID *big.Int) (*models.MarketMetadata,
logger.Log().WithField("layer", "Service-GetMarketMetadata").Errorf("received nil market id")
return nil, errors.GetInvalidArgumentErr("market id cannot be nil")
}

res, err := s.perpsMarket.Metadata(nil, marketID)
if err != nil {
logger.Log().WithField("layer", "Service-GetMarketMetadata").Errorf("error from the contract: %v", err.Error())
Expand All @@ -79,6 +78,35 @@ func (s *Service) GetMarketMetadata(marketID *big.Int) (*models.MarketMetadata,
return models.GetMarketMetadataFromContractResponse(marketID, res.Name, res.Symbol), nil
}

func (s *Service) GetMarketSummary(marketID *big.Int) (*models.MarketSummary, error) {
if marketID == nil {
logger.Log().WithField("layer", "Service-GetMarketSummary").Errorf("received nil market id")
return nil, errors.GetInvalidArgumentErr("market id cannot be nil")
}

res, err := s.perpsMarket.GetMarketSummary(nil, marketID)
if err != nil {
if err.Error() == "execution reverted" {
logger.Log().WithField("layer", "Service-GetMarketSummary").Errorf("contract error, market does not exist")
return nil, errors.GetInvalidArgumentErr("market does not exist")
} else {
logger.Log().WithField("layer", "Service-GetMarketSummary").Errorf("error from the contract: %v", err.Error())
return nil, errors.GetReadContractErr(err, "perpsMarket", "getMarketSummary")
}
}

return models.GetMarketSummaryFromContractModel(res, marketID), nil
}

func (s *Service) GetMarketIDs() ([]*big.Int, error) {
res, err := s.perpsMarket.GetMarkets(nil)
if err != nil {
return nil, errors.GetReadContractErr(err, "perpsMarket", "getMarkets")
}

return res, nil
}

// retrieveMarketUpdates is used to get retrieve market updates with given filter options
func (s *Service) retrieveMarketUpdates(opts *bind.FilterOpts) ([]*models.MarketUpdate, error) {
iterator, err := s.perpsMarket.FilterMarketUpdated(opts)
Expand Down
96 changes: 96 additions & 0 deletions services/marketData_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -161,3 +161,99 @@ func TestService_GetMarketMetadata_OnChain(t *testing.T) {
})
}
}

func TestService_GetMarketSummary(t *testing.T) {
rpc := os.Getenv("TEST_RPC")
if rpc == "" {
log.Fatal("no rpc in env vars")
}

rpcClient, _ := ethclient.Dial(rpc)

coreC, _ := coreGoerli.NewCoreGoerli(common.HexToAddress("0x76490713314fCEC173f44e99346F54c6e92a8E42"), rpcClient)
spot, _ := spotMarketGoerli.NewSpotMarketGoerli(common.HexToAddress("0x5FF4b3aacdeC86782d8c757FAa638d8790799E83"), rpcClient)
perps, _ := perpsMarketGoerli.NewPerpsMarketGoerli(common.HexToAddress("0xf272382cB3BE898A8CdB1A23BE056fA2Fcf4513b"), rpcClient)

testCases := []struct {
name string
marketID *big.Int
wantErr error
}{
{
name: "nil id",
wantErr: errors.InvalidArgumentErr,
},
{
name: "id 100",
marketID: big.NewInt(100),
},
{
name: "id 200",
marketID: big.NewInt(200),
},
{
name: "id 300",
marketID: big.NewInt(300),
wantErr: errors.InvalidArgumentErr,
},
}
for _, tt := range testCases {
t.Run(tt.name, func(t *testing.T) {
s := NewService(rpcClient, coreC, 11664658, spot, 10875051, perps, 0)

res, err := s.GetMarketSummary(tt.marketID)

if tt.wantErr == nil {
require.Equal(t, tt.marketID, res.MarketID)
require.NotNil(t, res.Size)
require.NotNil(t, res.Skew)
require.NotNil(t, res.MaxOpenInterest)
require.NotNil(t, res.CurrentFundingVelocity)
require.NotNil(t, res.CurrentFundingRate)
require.NotNil(t, res.IndexPrice)
require.NoError(t, err)
} else {
require.Error(t, err)
require.ErrorIs(t, err, tt.wantErr)
}
})
}
}

func TestService_GetMarketIDs(t *testing.T) {
rpc := os.Getenv("TEST_RPC")
if rpc == "" {
log.Fatal("no rpc in env vars")
}

rpcClient, _ := ethclient.Dial(rpc)

coreC, _ := coreGoerli.NewCoreGoerli(common.HexToAddress("0x76490713314fCEC173f44e99346F54c6e92a8E42"), rpcClient)
spot, _ := spotMarketGoerli.NewSpotMarketGoerli(common.HexToAddress("0x5FF4b3aacdeC86782d8c757FAa638d8790799E83"), rpcClient)
perps, _ := perpsMarketGoerli.NewPerpsMarketGoerli(common.HexToAddress("0xf272382cB3BE898A8CdB1A23BE056fA2Fcf4513b"), rpcClient)

testCases := []struct {
name string
want []*big.Int
wantErr error
}{
{
name: "get ids",
want: []*big.Int{big.NewInt(100), big.NewInt(200)},
},
}
for _, tt := range testCases {
t.Run(tt.name, func(t *testing.T) {
s := NewService(rpcClient, coreC, 11664658, spot, 10875051, perps, 0)

res, err := s.GetMarketIDs()

if tt.wantErr == nil {
require.Equal(t, tt.want, res)
} else {
require.Error(t, err)
require.ErrorIs(t, err, tt.wantErr)
}
})
}
}
6 changes: 6 additions & 0 deletions services/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,12 @@ type IService interface {
// GetMarketMetadata is used to get market metadata by given market ID
GetMarketMetadata(marketID *big.Int) (*models.MarketMetadata, error)

// GetMarketSummary is used to get market summary by given market ID
GetMarketSummary(marketID *big.Int) (*models.MarketSummary, error)

// GetMarketIDs is used to get market IDs from the smart contract
GetMarketIDs() ([]*big.Int, error)

// FormatAccount is used to get account, and it's additional data from the contract by given account id
FormatAccount(id *big.Int) (*models.Account, error)

Expand Down
Loading