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

SNX-31: add Core contract usd minted event #50

Merged
merged 1 commit into from
Jan 16, 2024
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
4 changes: 4 additions & 0 deletions events/events.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,10 @@ type IEvents interface {
// ListenAccountPermissionGranted is used to listen to all 'PermissionGranted' contract events and return them as models.PermissionChanged
// struct and return errors on ErrChan chanel
ListenAccountPermissionGranted() (*AccountPermissionGrantedSubscription, error)

// ListenUSDMinted is used to listen to all 'USDMinted' Core contract events and return them as models.USDMinted
// struct and return errors on ErrChan chanel
ListenUSDMinted() (*USDMintedSubscription, error)
}

// Events implements IEvents interface
Expand Down
83 changes: 83 additions & 0 deletions events/usdMinted.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
package events

import (
"context"
"math/big"

"github.com/ethereum/go-ethereum/ethclient"
"github.com/ethereum/go-ethereum/event"

"github.com/gateway-fm/perpsv3-Go/contracts/core"
"github.com/gateway-fm/perpsv3-Go/errors"
"github.com/gateway-fm/perpsv3-Go/models"
"github.com/gateway-fm/perpsv3-Go/pkg/logger"
)

// USDMintedSubscription is a struct for listening to all 'PositionLiquidated' contract events and return them as models.USDMinted struct
type USDMintedSubscription struct {
*basicSubscription
USDMintedsChan chan *models.USDMinted
contractEventChan chan *core.CoreUsdMinted
}

func (e *Events) ListenUSDMinted() (*USDMintedSubscription, error) {
contractEventChan := make(chan *core.CoreUsdMinted)

contractSub, err := e.core.WatchUsdMinted(nil, contractEventChan, nil, nil, nil)
if err != nil {
logger.Log().WithField("layer", "Events-ListenUSDMinteds").Errorf("error watch usd minted: %v", err.Error())
return nil, errors.GetEventListenErr(err, "USDMinted")
}

orderSub := newUSDMintedSubscription(contractSub, contractEventChan)

go orderSub.listen(e.rpcClient)

return orderSub, nil
}

// newUSDMintedSubscription is used to create new USDMintedSubscription instance
func newUSDMintedSubscription(eventSub event.Subscription, contractEventChan chan *core.CoreUsdMinted) *USDMintedSubscription {
return &USDMintedSubscription{
basicSubscription: newBasicSubscription(eventSub),
contractEventChan: contractEventChan,
USDMintedsChan: make(chan *models.USDMinted),
}
}

// listen is used to run a goroutine
func (s *USDMintedSubscription) listen(rpcClient *ethclient.Client) {
defer func() {
close(s.USDMintedsChan)
close(s.contractEventChan)
}()

for {
select {
case <-s.stop:
return
case err := <-s.eventSub.Err():
if err != nil {
logger.Log().WithField("layer", "Events-PositionLiquidated").Errorf("error listening position liquidated: %v", err.Error())
s.ErrChan <- err
}
return
case positionLiquidated := <-s.contractEventChan:
block, err := rpcClient.HeaderByNumber(context.Background(), big.NewInt(int64(positionLiquidated.Raw.BlockNumber)))
time := uint64(0)
if err != nil {
logger.Log().WithField("layer", "Events-PositionLiquidated").Warningf(
"error fetching block number %v: %v; order event time set to 0 ",
positionLiquidated.Raw.BlockNumber, err.Error(),
)
s.ErrChan <- err
} else {
time = block.Time
}

order := models.GetUSDMintedFromEvent(positionLiquidated, time)

s.USDMintedsChan <- order
}
}
}
39 changes: 39 additions & 0 deletions models/usdMinted.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package models

import (
"math/big"

"github.com/ethereum/go-ethereum/common"

"github.com/gateway-fm/perpsv3-Go/contracts/core"
"github.com/gateway-fm/perpsv3-Go/pkg/logger"
)

// USDMinted is a `usdMinted` Core smart-contract event struct
type USDMinted struct {
AccountId *big.Int
PoolId *big.Int
CollateralType common.Address
Amount *big.Int
Sender common.Address
BlockNumber uint64
BlockTimestamp uint64
}

// GetUSDMintedFromEvent is used to get USDMinted struct from given contract event
func GetUSDMintedFromEvent(event *core.CoreUsdMinted, time uint64) *USDMinted {
if event == nil {
logger.Log().WithField("layer", "Models-USDMinted").Warning("nil event received")
return &USDMinted{}
}

return &USDMinted{
AccountId: event.AccountId,
PoolId: event.PoolId,
CollateralType: event.CollateralType,
Amount: event.Amount,
Sender: event.Sender,
BlockNumber: event.Raw.BlockNumber,
BlockTimestamp: time,
}
}
20 changes: 20 additions & 0 deletions perpsv3.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,10 @@ type IPerpsv3 interface {
// limit. For most public RPC providers the value for limit is 20 000 blocks
RetrieveAccountLiquidationsLimit(limit uint64) ([]*models.AccountLiquidated, error)

// RetrieveUSDMintedLimit is used to get all `usdMinted` events from the Core contract with given block search
// limit. For most public RPC providers the value for limit is 20 000 blocks
RetrieveUSDMintedLimit(limit uint64) ([]*models.USDMinted, error)

// ListenTrades is used to subscribe on the contract "OrderSettled" event. The goroutine will return events on the
// TradesChan chanel and errors on the ErrChan chanel.
// To close the subscription use events.TradeSubscription `Close` function
Expand Down Expand Up @@ -112,6 +116,10 @@ type IPerpsv3 interface {
// struct and return errors on ErrChan chanel
ListenAccountPermissionGranted() (*events.AccountPermissionGrantedSubscription, error)

// ListenUSDMinted is used to listen to all 'USDMinted' Core contract events and return them as models.USDMinted
// struct and return errors on ErrChan chanel
ListenUSDMinted() (*events.USDMintedSubscription, error)

// GetPosition is used to get position data struct from latest block with given params
// Function can return contract error if market ID is invalid
GetPosition(accountID *big.Int, marketID *big.Int) (*models.Position, error)
Expand Down Expand Up @@ -248,6 +256,10 @@ func (p *Perpsv3) RetrieveAccountLiquidationsLimit(limit uint64) ([]*models.Acco
return p.service.RetrieveAccountLiquidationsLimit(limit)
}

func (p *Perpsv3) RetrieveUSDMintedLimit(limit uint64) ([]*models.USDMinted, error) {
return p.service.RetrieveUSDMintedLimit(limit)
}

func (p *Perpsv3) ListenTrades() (*events.TradeSubscription, error) {
return p.events.ListenTrades()
}
Expand Down Expand Up @@ -284,6 +296,10 @@ func (p *Perpsv3) ListenAccountPermissionGranted() (*events.AccountPermissionGra
return p.events.ListenAccountPermissionGranted()
}

func (p *Perpsv3) ListenUSDMinted() (*events.USDMintedSubscription, error) {
return p.events.ListenUSDMinted()
}

func (p *Perpsv3) GetPosition(accountID *big.Int, marketID *big.Int) (*models.Position, error) {
return p.service.GetPosition(accountID, marketID)
}
Expand Down Expand Up @@ -453,3 +469,7 @@ func getAddr(addr string, name string) (common.Address, error) {
//
// return lib, nil
//}
// }
//
// return lib, nil
//}
94 changes: 94 additions & 0 deletions services/pools.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
package services

import (
"context"
"math/big"

"github.com/ethereum/go-ethereum/accounts/abi/bind"

"github.com/gateway-fm/perpsv3-Go/contracts/core"
"github.com/gateway-fm/perpsv3-Go/errors"
"github.com/gateway-fm/perpsv3-Go/models"
"github.com/gateway-fm/perpsv3-Go/pkg/logger"
)

func (s *Service) RetrieveUSDMintedLimit(limit uint64) ([]*models.USDMinted, error) {
iterations, last, err := s.getIterationsForLimitQuery(limit)
if err != nil {
return nil, err
}

var mints []*models.USDMinted

logger.Log().WithField("layer", "Service-RetrieveUSDMintedLimit").Infof(
"fetching USDMinted with limit: %v to block: %v total iterations: %v...",
limit, last, iterations,
)

fromBlock := s.coreFirstBlock
toBlock := fromBlock + limit
for i := uint64(1); i <= iterations; i++ {
if i%10 == 0 || i == iterations {
logger.Log().WithField("layer", "Service-RetrieveUSDMintedLimit").Infof("-- iteration %v", i)
}
opts := s.getFilterOptsCore(fromBlock, &toBlock)

res, err := s.retrieveUSDMinted(opts)
if err != nil {
return nil, err
}

mints = append(mints, res...)

fromBlock = toBlock + 1

if i == iterations-1 {
toBlock = last
} else {
toBlock = fromBlock + limit
}
}

logger.Log().WithField("layer", "Service-RetrieveLiquidationsLimit").Infof("task completed successfully")

return mints, nil
}

func (s *Service) retrieveUSDMinted(opts *bind.FilterOpts) ([]*models.USDMinted, error) {
iterator, err := s.core.FilterUsdMinted(opts, nil, nil, nil)
if err != nil {
logger.Log().WithField("layer", "Service-RetrieveUSDMintedLimit").Errorf("error get iterator: %v", err.Error())
return nil, errors.GetFilterErr(err, "core")
}

var mints []*models.USDMinted

for iterator.Next() {
if iterator.Error() != nil {
logger.Log().WithField("layer", "Service-RetrieveUSDMintedLimit").Errorf("iterator error: %v", iterator.Error().Error())
return nil, errors.GetFilterErr(iterator.Error(), "core")
}

mint, err := s.getUSDMinted(iterator.Event, iterator.Event.Raw.BlockNumber)
if err != nil {
return nil, err
}

mints = append(mints, mint)
}

return mints, nil
}

// getLiquidation is used to get models.Liquidation from given event and block number
func (s *Service) getUSDMinted(event *core.CoreUsdMinted, blockN uint64) (*models.USDMinted, error) {
block, err := s.rpcClient.HeaderByNumber(context.Background(), big.NewInt(int64(blockN)))
if err != nil {
logger.Log().WithField("layer", "Service-RetrieveUSDMintedLimit").Errorf(
"get block:%v by number error: %v", blockN, err.Error(),
)
return nil, errors.GetRPCProviderErr(err, "HeaderByNumber")
}

return models.GetUSDMintedFromEvent(event, block.Time), nil
}
17 changes: 17 additions & 0 deletions services/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,10 @@ type IService interface {
// limit. For most public RPC providers the value for limit is 20 000 blocks
RetrieveAccountLiquidationsLimit(limit uint64) ([]*models.AccountLiquidated, error)

// RetrieveUSDMintedLimit is used to get all `usdMinted` events from the Core contract with given block search
// limit. For most public RPC providers the value for limit is 20 000 blocks
RetrieveUSDMintedLimit(limit uint64) ([]*models.USDMinted, error)

// GetPosition is used to get "Position" data struct from the latest block from the perps market with given data
GetPosition(accountID *big.Int, marketID *big.Int) (*models.Position, error)

Expand Down Expand Up @@ -203,3 +207,16 @@ func (s *Service) getFilterOptsPerpsMarket(fromBlock uint64, toBLock *uint64) *b
Context: context.Background(),
}
}

// getFilterOptsPerpsMarket is used to get options for event filtering on perps market contract
func (s *Service) getFilterOptsCore(fromBlock uint64, toBLock *uint64) *bind.FilterOpts {
if fromBlock == 0 {
fromBlock = s.coreFirstBlock
}

return &bind.FilterOpts{
Start: fromBlock,
End: toBLock,
Context: context.Background(),
}
}