diff --git a/contracts/core/contract.go b/contracts/core/contract.go index ca059da..e22d9de 100644 --- a/contracts/core/contract.go +++ b/contracts/core/contract.go @@ -9787,7 +9787,7 @@ func (_Core *CoreFilterer) FilterRewardsClaimed(opts *bind.FilterOpts, accountId collateralTypeRule = append(collateralTypeRule, collateralTypeItem) } - logs, sub, err := _Core.contract.FilterLogs(opts, "RewardsClaimed", accountIdRule, poolIdRule, collateralTypeRule) + logs, sub, err := _Core.contract.FilterLogs(opts, "RewardsClaimed") if err != nil { return nil, err } @@ -9812,7 +9812,7 @@ func (_Core *CoreFilterer) WatchRewardsClaimed(opts *bind.WatchOpts, sink chan<- collateralTypeRule = append(collateralTypeRule, collateralTypeItem) } - logs, sub, err := _Core.contract.WatchLogs(opts, "RewardsClaimed", accountIdRule, poolIdRule, collateralTypeRule) + logs, sub, err := _Core.contract.WatchLogs(opts, "RewardsClaimed") if err != nil { return nil, err } @@ -9948,7 +9948,7 @@ func (_Core *CoreFilterer) FilterRewardsDistributed(opts *bind.FilterOpts, poolI collateralTypeRule = append(collateralTypeRule, collateralTypeItem) } - logs, sub, err := _Core.contract.FilterLogs(opts, "RewardsDistributed", poolIdRule, collateralTypeRule) + logs, sub, err := _Core.contract.FilterLogs(opts, "RewardsDistributed") if err != nil { return nil, err } @@ -9969,7 +9969,7 @@ func (_Core *CoreFilterer) WatchRewardsDistributed(opts *bind.WatchOpts, sink ch collateralTypeRule = append(collateralTypeRule, collateralTypeItem) } - logs, sub, err := _Core.contract.WatchLogs(opts, "RewardsDistributed", poolIdRule, collateralTypeRule) + logs, sub, err := _Core.contract.WatchLogs(opts, "RewardsDistributed") if err != nil { return nil, err } diff --git a/events/events.go b/events/events.go index 83a2a59..e0bfd42 100644 --- a/events/events.go +++ b/events/events.go @@ -68,6 +68,14 @@ type IEvents interface { // ListenCollateralDeposited is used to listen to all 'Deposited' Core contract events and return them as models.CollateralDeposited // struct and return errors on ErrChan chanel ListenCollateralDeposited() (*CollateralDepositedSubscription, error) + + // ListenRewardDistributed is used to listen to all 'RewardDistributed' Core contract events and return them as models.RewardDistributed + // struct and return errors on ErrChan chanel + ListenRewardDistributed() (*RewardDistributedSubscription, error) + + // ListenRewardClaimed is used to listen to all 'RewardClaimed' Core contract events and return them as models.RewardClaimed + // struct and return errors on ErrChan chanel + ListenRewardClaimed() (*RewardClaimedSubscription, error) } // Events implements IEvents interface diff --git a/events/rewardClaimed.go b/events/rewardClaimed.go new file mode 100644 index 0000000..c881a84 --- /dev/null +++ b/events/rewardClaimed.go @@ -0,0 +1,82 @@ +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" +) + +// RewardClaimedSubscription is a struct for listening to all 'RewardClaimed' contract events and return them as models.RewardClaimed struct +type RewardClaimedSubscription struct { + *basicSubscription + RewardClaimedChan chan *models.RewardClaimed + contractEventChan chan *core.CoreRewardsClaimed +} + +func (e *Events) ListenRewardClaimed() (*RewardClaimedSubscription, error) { + contractEventChan := make(chan *core.CoreRewardsClaimed) + + contractSub, err := e.core.WatchRewardsClaimed(nil, contractEventChan, nil, nil, nil) + if err != nil { + logger.Log().WithField("layer", "Events-ListenRewardClaimed").Errorf("error watch reward claimed: %v", err.Error()) + return nil, errors.GetEventListenErr(err, "RewardClaimed") + } + + rewardSub := newRewardClaimedSubscription(contractSub, contractEventChan) + + go rewardSub.listen(e.rpcClient) + + return rewardSub, nil +} + +// newRewardClaimedSubscription is used to create new RewardClaimedSubscription instance +func newRewardClaimedSubscription(eventSub event.Subscription, contractEventChan chan *core.CoreRewardsClaimed) *RewardClaimedSubscription { + return &RewardClaimedSubscription{ + basicSubscription: newBasicSubscription(eventSub), + contractEventChan: contractEventChan, + RewardClaimedChan: make(chan *models.RewardClaimed), + } +} + +// listen is used to run a goroutine +func (s *RewardClaimedSubscription) listen(rpcClient *ethclient.Client) { + defer func() { + close(s.RewardClaimedChan) + close(s.contractEventChan) + }() + + for { + select { + case <-s.stop: + return + case err := <-s.eventSub.Err(): + if err != nil { + logger.Log().WithField("layer", "Events-RewardClaimed").Errorf("error listening reward claimed: %v", err.Error()) + s.ErrChan <- err + } + return + case eventRewardClaimed := <-s.contractEventChan: + block, err := rpcClient.HeaderByNumber(context.Background(), big.NewInt(int64(eventRewardClaimed.Raw.BlockNumber))) + time := uint64(0) + if err != nil { + logger.Log().WithField("layer", "Events-RewardClaimed").Warningf( + "error fetching block number %v: %v; order event time set to 0 ", + eventRewardClaimed.Raw.BlockNumber, err.Error(), + ) + s.ErrChan <- err + } else { + time = block.Time + } + + claimed := models.GetRewardClaimedFromEvent(eventRewardClaimed, time) + + s.RewardClaimedChan <- claimed + } + } +} diff --git a/events/rewardDistributed.go b/events/rewardDistributed.go new file mode 100644 index 0000000..fb00501 --- /dev/null +++ b/events/rewardDistributed.go @@ -0,0 +1,82 @@ +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" +) + +// RewardDistributedSubscription is a struct for listening to all 'RewardDistributed' contract events and return them as models.RewardDistributed struct +type RewardDistributedSubscription struct { + *basicSubscription + RewardDistributedChan chan *models.RewardDistributed + contractEventChan chan *core.CoreRewardsDistributed +} + +func (e *Events) ListenRewardDistributed() (*RewardDistributedSubscription, error) { + contractEventChan := make(chan *core.CoreRewardsDistributed) + + contractSub, err := e.core.WatchRewardsDistributed(nil, contractEventChan, nil, nil) + if err != nil { + logger.Log().WithField("layer", "Events-ListenRewardDistributed").Errorf("error watch reward claimed: %v", err.Error()) + return nil, errors.GetEventListenErr(err, "RewardDistributed") + } + + rewardSub := newRewardDistributedSubscription(contractSub, contractEventChan) + + go rewardSub.listen(e.rpcClient) + + return rewardSub, nil +} + +// newRewardDistributedSubscription is used to create new RewardDistributedSubscription instance +func newRewardDistributedSubscription(eventSub event.Subscription, contractEventChan chan *core.CoreRewardsDistributed) *RewardDistributedSubscription { + return &RewardDistributedSubscription{ + basicSubscription: newBasicSubscription(eventSub), + contractEventChan: contractEventChan, + RewardDistributedChan: make(chan *models.RewardDistributed), + } +} + +// listen is used to run a goroutine +func (s *RewardDistributedSubscription) listen(rpcClient *ethclient.Client) { + defer func() { + close(s.RewardDistributedChan) + close(s.contractEventChan) + }() + + for { + select { + case <-s.stop: + return + case err := <-s.eventSub.Err(): + if err != nil { + logger.Log().WithField("layer", "Events-RewardDistributed").Errorf("error listening reward claimed: %v", err.Error()) + s.ErrChan <- err + } + return + case eventRewardDistributed := <-s.contractEventChan: + block, err := rpcClient.HeaderByNumber(context.Background(), big.NewInt(int64(eventRewardDistributed.Raw.BlockNumber))) + time := uint64(0) + if err != nil { + logger.Log().WithField("layer", "Events-RewardDistributed").Warningf( + "error fetching block number %v: %v; order event time set to 0 ", + eventRewardDistributed.Raw.BlockNumber, err.Error(), + ) + s.ErrChan <- err + } else { + time = block.Time + } + + distributed := models.GetRewardDistributedFromEvent(eventRewardDistributed, time) + + s.RewardDistributedChan <- distributed + } + } +} diff --git a/mocks/events/mockEvents.go b/mocks/events/mockEvents.go index 52ff70c..c382f91 100644 --- a/mocks/events/mockEvents.go +++ b/mocks/events/mockEvents.go @@ -94,6 +94,51 @@ func (mr *MockIEventsMockRecorder) ListenAccountPermissionRevoked() *gomock.Call return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListenAccountPermissionRevoked", reflect.TypeOf((*MockIEvents)(nil).ListenAccountPermissionRevoked)) } +// ListenCollateralDeposited mocks base method. +func (m *MockIEvents) ListenCollateralDeposited() (*events.CollateralDepositedSubscription, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "ListenCollateralDeposited") + ret0, _ := ret[0].(*events.CollateralDepositedSubscription) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// ListenCollateralDeposited indicates an expected call of ListenCollateralDeposited. +func (mr *MockIEventsMockRecorder) ListenCollateralDeposited() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListenCollateralDeposited", reflect.TypeOf((*MockIEvents)(nil).ListenCollateralDeposited)) +} + +// ListenCollateralWithdrawn mocks base method. +func (m *MockIEvents) ListenCollateralWithdrawn() (*events.CollateralWithdrawnSubscription, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "ListenCollateralWithdrawn") + ret0, _ := ret[0].(*events.CollateralWithdrawnSubscription) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// ListenCollateralWithdrawn indicates an expected call of ListenCollateralWithdrawn. +func (mr *MockIEventsMockRecorder) ListenCollateralWithdrawn() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListenCollateralWithdrawn", reflect.TypeOf((*MockIEvents)(nil).ListenCollateralWithdrawn)) +} + +// ListenDelegationUpdated mocks base method. +func (m *MockIEvents) ListenDelegationUpdated() (*events.DelegationUpdatedSubscription, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "ListenDelegationUpdated") + ret0, _ := ret[0].(*events.DelegationUpdatedSubscription) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// ListenDelegationUpdated indicates an expected call of ListenDelegationUpdated. +func (mr *MockIEventsMockRecorder) ListenDelegationUpdated() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListenDelegationUpdated", reflect.TypeOf((*MockIEvents)(nil).ListenDelegationUpdated)) +} + // ListenLiquidations mocks base method. func (m *MockIEvents) ListenLiquidations() (*events.LiquidationSubscription, error) { m.ctrl.T.Helper() @@ -154,6 +199,36 @@ func (mr *MockIEventsMockRecorder) ListenOrders() *gomock.Call { return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListenOrders", reflect.TypeOf((*MockIEvents)(nil).ListenOrders)) } +// ListenRewardClaimed mocks base method. +func (m *MockIEvents) ListenRewardClaimed() (*events.RewardClaimedSubscription, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "ListenRewardClaimed") + ret0, _ := ret[0].(*events.RewardClaimedSubscription) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// ListenRewardClaimed indicates an expected call of ListenRewardClaimed. +func (mr *MockIEventsMockRecorder) ListenRewardClaimed() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListenRewardClaimed", reflect.TypeOf((*MockIEvents)(nil).ListenRewardClaimed)) +} + +// ListenRewardDistributed mocks base method. +func (m *MockIEvents) ListenRewardDistributed() (*events.RewardDistributedSubscription, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "ListenRewardDistributed") + ret0, _ := ret[0].(*events.RewardDistributedSubscription) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// ListenRewardDistributed indicates an expected call of ListenRewardDistributed. +func (mr *MockIEventsMockRecorder) ListenRewardDistributed() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListenRewardDistributed", reflect.TypeOf((*MockIEvents)(nil).ListenRewardDistributed)) +} + // ListenTrades mocks base method. func (m *MockIEvents) ListenTrades() (*events.TradeSubscription, error) { m.ctrl.T.Helper() @@ -168,3 +243,33 @@ func (mr *MockIEventsMockRecorder) ListenTrades() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListenTrades", reflect.TypeOf((*MockIEvents)(nil).ListenTrades)) } + +// ListenUSDBurned mocks base method. +func (m *MockIEvents) ListenUSDBurned() (*events.USDBurnedSubscription, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "ListenUSDBurned") + ret0, _ := ret[0].(*events.USDBurnedSubscription) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// ListenUSDBurned indicates an expected call of ListenUSDBurned. +func (mr *MockIEventsMockRecorder) ListenUSDBurned() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListenUSDBurned", reflect.TypeOf((*MockIEvents)(nil).ListenUSDBurned)) +} + +// ListenUSDMinted mocks base method. +func (m *MockIEvents) ListenUSDMinted() (*events.USDMintedSubscription, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "ListenUSDMinted") + ret0, _ := ret[0].(*events.USDMintedSubscription) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// ListenUSDMinted indicates an expected call of ListenUSDMinted. +func (mr *MockIEventsMockRecorder) ListenUSDMinted() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListenUSDMinted", reflect.TypeOf((*MockIEvents)(nil).ListenUSDMinted)) +} diff --git a/mocks/service/mockService.go b/mocks/service/mockService.go index 15a069c..c395048 100644 --- a/mocks/service/mockService.go +++ b/mocks/service/mockService.go @@ -8,6 +8,7 @@ import ( big "math/big" reflect "reflect" + common "github.com/ethereum/go-ethereum/common" models "github.com/gateway-fm/perpsv3-Go/models" gomock "github.com/golang/mock/gomock" ) @@ -140,6 +141,21 @@ func (mr *MockIServiceMockRecorder) GetCollateralAmount(accountId, marketId inte return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetCollateralAmount", reflect.TypeOf((*MockIService)(nil).GetCollateralAmount), accountId, marketId) } +// GetCollateralPrice mocks base method. +func (m *MockIService) GetCollateralPrice(blockNumber *big.Int, collateralType common.Address) (*models.CollateralPrice, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetCollateralPrice", blockNumber, collateralType) + ret0, _ := ret[0].(*models.CollateralPrice) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GetCollateralPrice indicates an expected call of GetCollateralPrice. +func (mr *MockIServiceMockRecorder) GetCollateralPrice(blockNumber, collateralType interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetCollateralPrice", reflect.TypeOf((*MockIService)(nil).GetCollateralPrice), blockNumber, collateralType) +} + // GetFoundingRate mocks base method. func (m *MockIService) GetFoundingRate(marketId *big.Int) (*big.Int, error) { m.ctrl.T.Helper() @@ -275,6 +291,51 @@ func (mr *MockIServiceMockRecorder) RetrieveAccountLiquidationsLimit(limit inter return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RetrieveAccountLiquidationsLimit", reflect.TypeOf((*MockIService)(nil).RetrieveAccountLiquidationsLimit), limit) } +// RetrieveCollateralDepositedLimit mocks base method. +func (m *MockIService) RetrieveCollateralDepositedLimit(limit uint64) ([]*models.CollateralDeposited, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "RetrieveCollateralDepositedLimit", limit) + ret0, _ := ret[0].([]*models.CollateralDeposited) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// RetrieveCollateralDepositedLimit indicates an expected call of RetrieveCollateralDepositedLimit. +func (mr *MockIServiceMockRecorder) RetrieveCollateralDepositedLimit(limit interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RetrieveCollateralDepositedLimit", reflect.TypeOf((*MockIService)(nil).RetrieveCollateralDepositedLimit), limit) +} + +// RetrieveCollateralWithdrawnLimit mocks base method. +func (m *MockIService) RetrieveCollateralWithdrawnLimit(limit uint64) ([]*models.CollateralWithdrawn, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "RetrieveCollateralWithdrawnLimit", limit) + ret0, _ := ret[0].([]*models.CollateralWithdrawn) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// RetrieveCollateralWithdrawnLimit indicates an expected call of RetrieveCollateralWithdrawnLimit. +func (mr *MockIServiceMockRecorder) RetrieveCollateralWithdrawnLimit(limit interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RetrieveCollateralWithdrawnLimit", reflect.TypeOf((*MockIService)(nil).RetrieveCollateralWithdrawnLimit), limit) +} + +// RetrieveDelegationUpdatedLimit mocks base method. +func (m *MockIService) RetrieveDelegationUpdatedLimit(limit uint64) ([]*models.DelegationUpdated, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "RetrieveDelegationUpdatedLimit", limit) + ret0, _ := ret[0].([]*models.DelegationUpdated) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// RetrieveDelegationUpdatedLimit indicates an expected call of RetrieveDelegationUpdatedLimit. +func (mr *MockIServiceMockRecorder) RetrieveDelegationUpdatedLimit(limit interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RetrieveDelegationUpdatedLimit", reflect.TypeOf((*MockIService)(nil).RetrieveDelegationUpdatedLimit), limit) +} + // RetrieveLiquidations mocks base method. func (m *MockIService) RetrieveLiquidations(fromBlock uint64, toBLock *uint64) ([]*models.Liquidation, error) { m.ctrl.T.Helper() @@ -395,6 +456,36 @@ func (mr *MockIServiceMockRecorder) RetrieveOrdersLimit(limit interface{}) *gomo return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RetrieveOrdersLimit", reflect.TypeOf((*MockIService)(nil).RetrieveOrdersLimit), limit) } +// RetrieveRewardClaimedLimit mocks base method. +func (m *MockIService) RetrieveRewardClaimedLimit(limit uint64) ([]*models.RewardClaimed, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "RetrieveRewardClaimedLimit", limit) + ret0, _ := ret[0].([]*models.RewardClaimed) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// RetrieveRewardClaimedLimit indicates an expected call of RetrieveRewardClaimedLimit. +func (mr *MockIServiceMockRecorder) RetrieveRewardClaimedLimit(limit interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RetrieveRewardClaimedLimit", reflect.TypeOf((*MockIService)(nil).RetrieveRewardClaimedLimit), limit) +} + +// RetrieveRewardDistributedLimit mocks base method. +func (m *MockIService) RetrieveRewardDistributedLimit(limit uint64) ([]*models.RewardDistributed, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "RetrieveRewardDistributedLimit", limit) + ret0, _ := ret[0].([]*models.RewardDistributed) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// RetrieveRewardDistributedLimit indicates an expected call of RetrieveRewardDistributedLimit. +func (mr *MockIServiceMockRecorder) RetrieveRewardDistributedLimit(limit interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RetrieveRewardDistributedLimit", reflect.TypeOf((*MockIService)(nil).RetrieveRewardDistributedLimit), limit) +} + // RetrieveTrades mocks base method. func (m *MockIService) RetrieveTrades(fromBlock uint64, toBLock *uint64) ([]*models.Trade, error) { m.ctrl.T.Helper() @@ -424,3 +515,33 @@ func (mr *MockIServiceMockRecorder) RetrieveTradesLimit(limit interface{}) *gomo mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RetrieveTradesLimit", reflect.TypeOf((*MockIService)(nil).RetrieveTradesLimit), limit) } + +// RetrieveUSDBurnedLimit mocks base method. +func (m *MockIService) RetrieveUSDBurnedLimit(limit uint64) ([]*models.USDBurned, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "RetrieveUSDBurnedLimit", limit) + ret0, _ := ret[0].([]*models.USDBurned) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// RetrieveUSDBurnedLimit indicates an expected call of RetrieveUSDBurnedLimit. +func (mr *MockIServiceMockRecorder) RetrieveUSDBurnedLimit(limit interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RetrieveUSDBurnedLimit", reflect.TypeOf((*MockIService)(nil).RetrieveUSDBurnedLimit), limit) +} + +// RetrieveUSDMintedLimit mocks base method. +func (m *MockIService) RetrieveUSDMintedLimit(limit uint64) ([]*models.USDMinted, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "RetrieveUSDMintedLimit", limit) + ret0, _ := ret[0].([]*models.USDMinted) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// RetrieveUSDMintedLimit indicates an expected call of RetrieveUSDMintedLimit. +func (mr *MockIServiceMockRecorder) RetrieveUSDMintedLimit(limit interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RetrieveUSDMintedLimit", reflect.TypeOf((*MockIService)(nil).RetrieveUSDMintedLimit), limit) +} diff --git a/models/rewardClaimed.go b/models/rewardClaimed.go new file mode 100644 index 0000000..00e0862 --- /dev/null +++ b/models/rewardClaimed.go @@ -0,0 +1,36 @@ +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" +) + +type RewardClaimed struct { + AccountId *big.Int + PoolId *big.Int + CollateralType common.Address + Distributor common.Address + Amount *big.Int + BlockNumber uint64 + BlockTimestamp uint64 +} + +func GetRewardClaimedFromEvent(event *core.CoreRewardsClaimed, time uint64) *RewardClaimed { + if event == nil { + logger.Log().WithField("layer", "Models-RewardClaimed").Warning("nil event received") + return &RewardClaimed{} + } + + return &RewardClaimed{ + AccountId: event.AccountId, + PoolId: event.PoolId, + CollateralType: event.CollateralType, + Distributor: event.Distributor, + Amount: event.Amount, + BlockNumber: event.Raw.BlockNumber, + BlockTimestamp: time, + } +} diff --git a/models/rewardDistributed.go b/models/rewardDistributed.go new file mode 100644 index 0000000..c1c9e61 --- /dev/null +++ b/models/rewardDistributed.go @@ -0,0 +1,38 @@ +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" +) + +type RewardDistributed struct { + PoolId *big.Int + CollateralType common.Address + Distributor common.Address + Amount *big.Int + Start *big.Int + Duration *big.Int + BlockNumber uint64 + BlockTimestamp uint64 +} + +func GetRewardDistributedFromEvent(event *core.CoreRewardsDistributed, time uint64) *RewardDistributed { + if event == nil { + logger.Log().WithField("layer", "Models-RewardDistributed").Warning("nil event received") + return &RewardDistributed{} + } + + return &RewardDistributed{ + PoolId: event.PoolId, + CollateralType: event.CollateralType, + Distributor: event.Distributor, + Amount: event.Amount, + Start: event.Start, + Duration: event.Duration, + BlockNumber: event.Raw.BlockNumber, + BlockTimestamp: time, + } +} diff --git a/perpsv3.go b/perpsv3.go index c128a3e..89b6394 100644 --- a/perpsv3.go +++ b/perpsv3.go @@ -92,6 +92,14 @@ type IPerpsv3 interface { // limit. For most public RPC providers the value for limit is 20 000 blocks RetrieveCollateralDepositedLimit(limit uint64) ([]*models.CollateralDeposited, error) + // RetrieveRewardClaimedLimit is used to get all `RewardClaimed` events from the Core contract with given block search + // limit. For most public RPC providers the value for limit is 20 000 blocks + RetrieveRewardClaimedLimit(limit uint64) ([]*models.RewardClaimed, error) + + // RetrieveRewardDistributedLimit is used to get all `RewardDistributed` events from the Core contract with given block search + // limit. For most public RPC providers the value for limit is 20 000 blocks + RetrieveRewardDistributedLimit(limit uint64) ([]*models.RewardDistributed, 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 @@ -153,6 +161,14 @@ type IPerpsv3 interface { // struct and return errors on ErrChan chanel ListenCollateralDeposited() (*events.CollateralDepositedSubscription, error) + // ListenRewardDistributed is used to listen to all 'RewardDistributed' Core contract events and return them as models.RewardDistributed + // struct and return errors on ErrChan chanel + ListenRewardDistributed() (*events.RewardDistributedSubscription, error) + + // ListenRewardClaimed is used to listen to all 'RewardClaimed' Core contract events and return them as models.RewardClaimed + // struct and return errors on ErrChan chanel + ListenRewardClaimed() (*events.RewardClaimedSubscription, 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) @@ -312,6 +328,14 @@ func (p *Perpsv3) RetrieveCollateralDepositedLimit(limit uint64) ([]*models.Coll return p.service.RetrieveCollateralDepositedLimit(limit) } +func (p *Perpsv3) RetrieveRewardClaimedLimit(limit uint64) ([]*models.RewardClaimed, error) { + return p.service.RetrieveRewardClaimedLimit(limit) +} + +func (p *Perpsv3) RetrieveRewardDistributedLimit(limit uint64) ([]*models.RewardDistributed, error) { + return p.service.RetrieveRewardDistributedLimit(limit) +} + func (p *Perpsv3) ListenTrades() (*events.TradeSubscription, error) { return p.events.ListenTrades() } @@ -368,6 +392,14 @@ func (p *Perpsv3) ListenCollateralDeposited() (*events.CollateralDepositedSubscr return p.events.ListenCollateralDeposited() } +func (p *Perpsv3) ListenRewardDistributed() (*events.RewardDistributedSubscription, error) { + return p.events.ListenRewardDistributed() +} + +func (p *Perpsv3) ListenRewardClaimed() (*events.RewardClaimedSubscription, error) { + return p.events.ListenRewardClaimed() +} + func (p *Perpsv3) GetPosition(accountID *big.Int, marketID *big.Int) (*models.Position, error) { return p.service.GetPosition(accountID, marketID) } diff --git a/services/rewards.go b/services/rewards.go new file mode 100644 index 0000000..95cc3dc --- /dev/null +++ b/services/rewards.go @@ -0,0 +1,174 @@ +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) RetrieveRewardClaimedLimit(limit uint64) ([]*models.RewardClaimed, error) { + iterations, last, err := s.getIterationsForLimitQuery(limit) + if err != nil { + return nil, err + } + + var claims []*models.RewardClaimed + + logger.Log().WithField("layer", "Service-RetrieveRewardClaimedLimit").Infof( + "fetching RewardClaimed 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-RetrieveRewardClaimedLimit").Infof("-- iteration %v", i) + } + opts := s.getFilterOptsCore(fromBlock, &toBlock) + + res, err := s.retrieveRewardClaimed(opts) + if err != nil { + return nil, err + } + + claims = append(claims, res...) + + fromBlock = toBlock + 1 + + if i == iterations-1 { + toBlock = last + } else { + toBlock = fromBlock + limit + } + } + + logger.Log().WithField("layer", "Service-RetrieveRewardClaimedLimit").Infof("task completed successfully") + + return claims, nil +} + +func (s *Service) retrieveRewardClaimed(opts *bind.FilterOpts) ([]*models.RewardClaimed, error) { + iterator, err := s.core.FilterRewardsClaimed(opts, nil, nil, nil) + if err != nil { + logger.Log().WithField("layer", "Service-RetrieveRewardClaimedLimit").Errorf("error get iterator: %v", err.Error()) + return nil, errors.GetFilterErr(err, "core") + } + + var claims []*models.RewardClaimed + + for iterator.Next() { + if iterator.Error() != nil { + logger.Log().WithField("layer", "Service-RetrieveRewardClaimedLimit").Errorf("iterator error: %v", iterator.Error().Error()) + return nil, errors.GetFilterErr(iterator.Error(), "core") + } + + claim, err := s.getRewardClaimed(iterator.Event, iterator.Event.Raw.BlockNumber) + if err != nil { + return nil, err + } + + claims = append(claims, claim) + } + + return claims, nil +} + +// getRewardClaimed is used to get models.RewardClaimed from given event and block number +func (s *Service) getRewardClaimed(event *core.CoreRewardsClaimed, blockN uint64) (*models.RewardClaimed, error) { + block, err := s.rpcClient.HeaderByNumber(context.Background(), big.NewInt(int64(blockN))) + if err != nil { + logger.Log().WithField("layer", "Service-RetrieveRewardClaimedLimit").Errorf( + "get block:%v by number error: %v", blockN, err.Error(), + ) + return nil, errors.GetRPCProviderErr(err, "HeaderByNumber") + } + + return models.GetRewardClaimedFromEvent(event, block.Time), nil +} + +func (s *Service) RetrieveRewardDistributedLimit(limit uint64) ([]*models.RewardDistributed, error) { + iterations, last, err := s.getIterationsForLimitQuery(limit) + if err != nil { + return nil, err + } + + var distributions []*models.RewardDistributed + + logger.Log().WithField("layer", "Service-RetrieveRewardDistributedLimit").Infof( + "fetching RewardDistributed 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-RetrieveRewardDistributedLimit").Infof("-- iteration %v", i) + } + opts := s.getFilterOptsCore(fromBlock, &toBlock) + + res, err := s.retrieveRewardDistributed(opts) + if err != nil { + return nil, err + } + + distributions = append(distributions, res...) + + fromBlock = toBlock + 1 + + if i == iterations-1 { + toBlock = last + } else { + toBlock = fromBlock + limit + } + } + + logger.Log().WithField("layer", "Service-RetrieveRewardDistributedLimit").Infof("task completed successfully") + + return distributions, nil +} + +func (s *Service) retrieveRewardDistributed(opts *bind.FilterOpts) ([]*models.RewardDistributed, error) { + iterator, err := s.core.FilterRewardsDistributed(opts, nil, nil) + if err != nil { + logger.Log().WithField("layer", "Service-RetrieveRewardDistributedLimit").Errorf("error get iterator: %v", err.Error()) + return nil, errors.GetFilterErr(err, "core") + } + + var distributions []*models.RewardDistributed + + for iterator.Next() { + if iterator.Error() != nil { + logger.Log().WithField("layer", "Service-RetrieveRewardDistributedLimit").Errorf("iterator error: %v", iterator.Error().Error()) + return nil, errors.GetFilterErr(iterator.Error(), "core") + } + + distribution, err := s.getRewardDistributed(iterator.Event, iterator.Event.Raw.BlockNumber) + if err != nil { + return nil, err + } + + distributions = append(distributions, distribution) + } + + return distributions, nil +} + +// getRewardDistributed is used to get models.RewardDistributed from given event and block number +func (s *Service) getRewardDistributed(event *core.CoreRewardsDistributed, blockN uint64) (*models.RewardDistributed, error) { + block, err := s.rpcClient.HeaderByNumber(context.Background(), big.NewInt(int64(blockN))) + if err != nil { + logger.Log().WithField("layer", "Service-RetrieveRewardDistributedLimit").Errorf( + "get block:%v by number error: %v", blockN, err.Error(), + ) + return nil, errors.GetRPCProviderErr(err, "HeaderByNumber") + } + + return models.GetRewardDistributedFromEvent(event, block.Time), nil +} diff --git a/services/service.go b/services/service.go index d860937..c8b2f60 100644 --- a/services/service.go +++ b/services/service.go @@ -82,6 +82,14 @@ type IService interface { // limit. For most public RPC providers the value for limit is 20 000 blocks RetrieveCollateralDepositedLimit(limit uint64) ([]*models.CollateralDeposited, error) + // RetrieveRewardClaimedLimit is used to get all `RewardClaimed` events from the Core contract with given block search + // limit. For most public RPC providers the value for limit is 20 000 blocks + RetrieveRewardClaimedLimit(limit uint64) ([]*models.RewardClaimed, error) + + // RetrieveRewardDistributedLimit is used to get all `RewardDistributed` events from the Core contract with given block search + // limit. For most public RPC providers the value for limit is 20 000 blocks + RetrieveRewardDistributedLimit(limit uint64) ([]*models.RewardDistributed, 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)