Skip to content

Commit

Permalink
[BCF-3392] - ContractReaderByIDs Wrapper (#797)
Browse files Browse the repository at this point in the history
* WIP

* Update ContractReaderByIDs interface method names

* Unexpose types.ContractReader in contractReaderByIDs

* Add multiple contract address support to fakeContractReader for tests

* Add GetLatestValue unit test for contractReaderByIDs

* Add GetLatestValue unit test for QueryKey

* Add BatchGetLatestValues unit test for CR by custom IdDs wrapper

* Rm ContractReaderByIDs interface and export the struct

* Change ContractReaderByIDs wrapper Unbind handling

* Improve ContractReaderByIDs wrapper err handling

* Remove mockery usage from ContractReaderByIDs tests

* lint
  • Loading branch information
ilija42 authored Sep 30, 2024
1 parent ef04dd4 commit f10ba2b
Show file tree
Hide file tree
Showing 3 changed files with 765 additions and 38 deletions.
115 changes: 115 additions & 0 deletions pkg/chainreader/contract_reader.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
package chainreader

import (
"context"
"errors"
"fmt"
"sync"

"github.com/smartcontractkit/chainlink-common/pkg/types"
"github.com/smartcontractkit/chainlink-common/pkg/types/query"
"github.com/smartcontractkit/chainlink-common/pkg/types/query/primitives"
)

var (
ErrNoBindings = errors.New("no bindings found")
)

// WrapContractReaderByIDs returns types.ContractReader behind ContractReaderByIDs interface.
func WrapContractReaderByIDs(contractReader types.ContractReader) ContractReaderByIDs {
return ContractReaderByIDs{
cr: contractReader,
}
}

// ContractReaderByIDs wraps types.ContractReader to allow the caller to set custom contractIDs with Bind.
type ContractReaderByIDs struct {
bindings sync.Map
cr types.ContractReader
}

// Bind accepts a map of bound contracts where map keys are custom contractIDs which will be used by the wrapper to reference contracts.
func (crByIds *ContractReaderByIDs) Bind(ctx context.Context, bindings map[string]types.BoundContract) error {
var toBind []types.BoundContract
for customContractID, boundContract := range bindings {
crByIds.bindings.Store(customContractID, boundContract)
toBind = append(toBind, boundContract)
}
return crByIds.cr.Bind(ctx, toBind)
}

func (crByIds *ContractReaderByIDs) Unbind(ctx context.Context, bindings map[string]types.BoundContract) error {
var toUnbind []types.BoundContract
for _, boundContract := range bindings {
toUnbind = append(toUnbind, boundContract)
}

err := crByIds.cr.Unbind(ctx, toUnbind)
if err == nil {
for customContractID := range bindings {
crByIds.bindings.Delete(customContractID)
}
}

return err
}

func (crByIds *ContractReaderByIDs) GetLatestValue(ctx context.Context, contractID, readName string, confidenceLevel primitives.ConfidenceLevel, params, returnVal any) error {
boundContract, err := crByIds.getBoundContract(contractID)
if err != nil {
return err
}

return crByIds.cr.GetLatestValue(ctx, boundContract.ReadIdentifier(readName), confidenceLevel, params, returnVal)
}

func (crByIds *ContractReaderByIDs) QueryKey(ctx context.Context, contractID string, filter query.KeyFilter, limitAndSort query.LimitAndSort, sequenceDataType any) ([]types.Sequence, error) {
boundContract, err := crByIds.getBoundContract(contractID)
if err != nil {
return nil, err
}

return crByIds.cr.QueryKey(ctx, boundContract, filter, limitAndSort, sequenceDataType)
}

type BatchGetLatestValuesRequestByCustomID map[string]types.ContractBatch
type BatchGetLatestValuesResultByCustomID map[string]types.ContractBatchResults

func (crByIds *ContractReaderByIDs) BatchGetLatestValues(ctx context.Context, request BatchGetLatestValuesRequestByCustomID) (BatchGetLatestValuesResultByCustomID, error) {
bcToID := make(map[string]string)
req := make(types.BatchGetLatestValuesRequest)
for contractID, batch := range request {
boundContract, err := crByIds.getBoundContract(contractID)
if err != nil {
return nil, err
}

req[boundContract] = batch
bcToID[boundContract.String()] = contractID
}

res, err := crByIds.cr.BatchGetLatestValues(ctx, req)
if err != nil {
return nil, err
}

wrappedRes := make(BatchGetLatestValuesResultByCustomID)
for bc, batchResp := range res {
wrappedRes[bcToID[bc.String()]] = batchResp
}

return wrappedRes, nil
}

func (crByIds *ContractReaderByIDs) getBoundContract(contractID string) (types.BoundContract, error) {
binding, ok := crByIds.bindings.Load(contractID)
if !ok {
return types.BoundContract{}, fmt.Errorf("%w for contractID: %s", ErrNoBindings, contractID)
}

boundContract, ok := binding.(types.BoundContract)
if !ok {
return types.BoundContract{}, fmt.Errorf("binding found for contractID %s, but is malformed", contractID)
}
return boundContract, nil
}
Loading

0 comments on commit f10ba2b

Please sign in to comment.