Skip to content

Commit

Permalink
Merge pull request #50 from gateway-fm/SNX-31
Browse files Browse the repository at this point in the history
SNX-31: add Core contract usd minted event
  • Loading branch information
asolovov authored Jan 16, 2024
2 parents a69c2b7 + 4f3d1ab commit 426980b
Show file tree
Hide file tree
Showing 6 changed files with 257 additions and 0 deletions.
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(),
}
}

0 comments on commit 426980b

Please sign in to comment.