Skip to content

Commit

Permalink
get tokens from jobspec
Browse files Browse the repository at this point in the history
  • Loading branch information
0xnogo committed Jul 11, 2024
1 parent 09c25c0 commit 2c27db7
Show file tree
Hide file tree
Showing 3 changed files with 44 additions and 102 deletions.
95 changes: 30 additions & 65 deletions core/services/ocr2/plugins/ccip/internal/ccipcommon/shortcuts.go
Original file line number Diff line number Diff line change
@@ -1,22 +1,16 @@
package ccipcommon

import (
"context"
"encoding/binary"
"encoding/hex"
"fmt"
"sort"
"strings"
"time"

"github.com/avast/retry-go/v4"

"golang.org/x/sync/errgroup"

cciptypes "github.com/smartcontractkit/chainlink-common/pkg/types/ccip"

"github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller"
"github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ccip/internal/ccipdata"
)

func GetMessageIDsAsHexString(messages []cciptypes.EVM2EVMMessage) []string {
Expand All @@ -32,67 +26,38 @@ type BackfillArgs struct {
SourceStartBlock, DestStartBlock uint64
}

// GetFilteredSortedLaneTokens returns union of tokens supported on this lane, including fee tokens from the provided price registry
// and the bridgeable tokens from offRamp. Bridgeable tokens are only included if they are configured on the pricegetter
// Fee tokens are not filtered as they must always be priced
func GetFilteredSortedLaneTokens(ctx context.Context, offRamp ccipdata.OffRampReader, priceRegistry cciptypes.PriceRegistryReader, priceGetter cciptypes.PriceGetter) (laneTokens []cciptypes.Address, excludedTokens []cciptypes.Address, err error) {
destFeeTokens, destBridgeableTokens, err := GetDestinationTokens(ctx, offRamp, priceRegistry)
if err != nil {
return nil, nil, fmt.Errorf("get tokens with batch limit: %w", err)
}

destTokensWithPrice, destTokensWithoutPrice, err := priceGetter.FilterConfiguredTokens(ctx, destBridgeableTokens)
if err != nil {
return nil, nil, fmt.Errorf("filter for priced tokens: %w", err)
}

return flattenedAndSortedTokens(destFeeTokens, destTokensWithPrice), destTokensWithoutPrice, nil
}

func flattenedAndSortedTokens(slices ...[]cciptypes.Address) (tokens []cciptypes.Address) {
// fee token can overlap with bridgeable tokens, we need to dedup them to arrive at lane token set
tokens = FlattenUniqueSlice(slices...)

// return the tokens in deterministic order to aid with testing and debugging
sort.Slice(tokens, func(i, j int) bool {
return tokens[i] < tokens[j]
})

return tokens
}

// GetDestinationTokens returns the destination chain fee tokens from the provided price registry
// and the bridgeable tokens from the offramp.
func GetDestinationTokens(ctx context.Context, offRamp ccipdata.OffRampReader, priceRegistry cciptypes.PriceRegistryReader) (fee, bridged []cciptypes.Address, err error) {
eg := new(errgroup.Group)

var destFeeTokens []cciptypes.Address
var destBridgeableTokens []cciptypes.Address

eg.Go(func() error {
tokens, err := priceRegistry.GetFeeTokens(ctx)
if err != nil {
return fmt.Errorf("get dest fee tokens: %w", err)
}
destFeeTokens = tokens
return nil
})

eg.Go(func() error {
tokens, err := offRamp.GetTokens(ctx)
if err != nil {
return fmt.Errorf("get dest bridgeable tokens: %w", err)
}
destBridgeableTokens = tokens.DestinationTokens
return nil
})

if err := eg.Wait(); err != nil {
return nil, nil, err
}

return destFeeTokens, destBridgeableTokens, nil
}
// func GetDestinationTokens(ctx context.Context, offRamp ccipdata.OffRampReader, priceRegistry cciptypes.PriceRegistryReader) (fee, bridged []cciptypes.Address, err error) {
// eg := new(errgroup.Group)

// var destFeeTokens []cciptypes.Address
// var destBridgeableTokens []cciptypes.Address

// eg.Go(func() error {
// tokens, err := priceRegistry.GetFeeTokens(ctx)
// if err != nil {
// return fmt.Errorf("get dest fee tokens: %w", err)
// }
// destFeeTokens = tokens
// return nil
// })

// eg.Go(func() error {
// tokens, err := offRamp.GetTokens(ctx)
// if err != nil {
// return fmt.Errorf("get dest bridgeable tokens: %w", err)
// }
// destBridgeableTokens = tokens.DestinationTokens
// return nil
// })

// if err := eg.Wait(); err != nil {
// return nil, nil, err
// }

// return destFeeTokens, destBridgeableTokens, nil
// }

// FlattenUniqueSlice returns a flattened slice that contains unique elements by preserving their order.
func FlattenUniqueSlice[T comparable](slices ...[]T) []T {
Expand Down
35 changes: 12 additions & 23 deletions core/services/ocr2/plugins/ccip/internal/ccipdb/price_service.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ import (
"github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets"
cciporm "github.com/smartcontractkit/chainlink/v2/core/services/ccip"
"github.com/smartcontractkit/chainlink/v2/core/services/job"
"github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ccip/internal/ccipcommon"
"github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ccip/internal/ccipdata"
"github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ccip/internal/pricegetter"
"github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ccip/prices"
Expand Down Expand Up @@ -278,54 +277,44 @@ func (p *priceService) observePriceUpdates(
if p.gasPriceEstimator == nil || p.destPriceRegistryReader == nil {
return nil, nil, fmt.Errorf("gasPriceEstimator and/or destPriceRegistry is not set yet")
}

sortedLaneTokens, filteredLaneTokens, err := ccipcommon.GetFilteredSortedLaneTokens(ctx, p.offRampReader, p.destPriceRegistryReader, p.priceGetter)

lggr.Debugw("Filtered bridgeable tokens with no configured price getter", "filteredLaneTokens", filteredLaneTokens)

if err != nil {
return nil, nil, fmt.Errorf("get destination tokens: %w", err)
}

return p.generatePriceUpdates(ctx, lggr, sortedLaneTokens)
return p.generatePriceUpdates(ctx, lggr)
}

// All prices are USD ($1=1e18) denominated. All prices must be not nil.
// Return token prices should contain the exact same tokens as in tokenDecimals.
func (p *priceService) generatePriceUpdates(
ctx context.Context,
lggr logger.Logger,
sortedLaneTokens []cciptypes.Address,
// sortedLaneTokens []cciptypes.Address,
) (sourceGasPriceUSD *big.Int, tokenPricesUSD map[cciptypes.Address]*big.Int, err error) {
// Include wrapped native in our token query as way to identify the source native USD price.
// notice USD is in 1e18 scale, i.e. $1 = 1e18
queryTokens := ccipcommon.FlattenUniqueSlice([]cciptypes.Address{p.sourceNative}, sortedLaneTokens)

rawTokenPricesUSD, err := p.priceGetter.TokenPricesUSD(ctx, queryTokens)
rawTokenPricesUSD, err := p.priceGetter.TokenPricesUSD(ctx, make([]cciptypes.Address, 0))

// Get all the tokens fetched from the PriceGetter
tokensFetched := make([]cciptypes.Address, 0, len(rawTokenPricesUSD))
for token := range rawTokenPricesUSD {
tokensFetched = append(tokensFetched, token)
}

if err != nil {
return nil, nil, err
}
lggr.Infow("Raw token prices", "rawTokenPrices", rawTokenPricesUSD)

// make sure that we got prices for all the tokens of our query
for _, token := range queryTokens {
if rawTokenPricesUSD[token] == nil {
return nil, nil, fmt.Errorf("missing token price: %+v", token)
}
}

sourceNativePriceUSD, exists := rawTokenPricesUSD[p.sourceNative]
if !exists {
return nil, nil, fmt.Errorf("missing source native (%s) price", p.sourceNative)
}

destTokensDecimals, err := p.destPriceRegistryReader.GetTokensDecimals(ctx, sortedLaneTokens)
destTokensDecimals, err := p.destPriceRegistryReader.GetTokensDecimals(ctx, tokensFetched)
if err != nil {
return nil, nil, fmt.Errorf("get tokens decimals: %w", err)
}

tokenPricesUSD = make(map[cciptypes.Address]*big.Int, len(rawTokenPricesUSD))
for i, token := range sortedLaneTokens {
for i, token := range tokensFetched {
tokenPricesUSD[token] = calculateUsdPer1e18TokenAmount(rawTokenPricesUSD[token], destTokensDecimals[i])
}

Expand Down
16 changes: 2 additions & 14 deletions core/services/ocr2/plugins/ccip/internal/pricegetter/pipeline.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import (
"strings"
"time"

mapset "github.com/deckarep/golang-set/v2"
"github.com/google/uuid"
"github.com/pkg/errors"

Expand Down Expand Up @@ -60,7 +59,7 @@ func (d *PipelineGetter) FilterConfiguredTokens(ctx context.Context, tokens []cc
return configured, unconfigured, nil
}

func (d *PipelineGetter) TokenPricesUSD(ctx context.Context, tokens []cciptypes.Address) (map[cciptypes.Address]*big.Int, error) {
func (d *PipelineGetter) TokenPricesUSD(ctx context.Context) (map[cciptypes.Address]*big.Int, error) {
_, trrs, err := d.runner.ExecuteRun(ctx, pipeline.Spec{
ID: d.jobID,
DotDagSource: d.source,
Expand All @@ -84,7 +83,6 @@ func (d *PipelineGetter) TokenPricesUSD(ctx context.Context, tokens []cciptypes.
return nil, errors.Errorf("expected map output of price pipeline, got %T", finalResult.Values[0])
}

providedTokensSet := mapset.NewSet(tokens...)
tokenPrices := make(map[cciptypes.Address]*big.Int)
for tokenAddressStr, rawPrice := range prices {
tokenAddressStr := ccipcalc.HexToAddress(tokenAddressStr)
Expand All @@ -93,17 +91,7 @@ func (d *PipelineGetter) TokenPricesUSD(ctx context.Context, tokens []cciptypes.
return nil, err
}

if providedTokensSet.Contains(tokenAddressStr) {
tokenPrices[tokenAddressStr] = castedPrice
}
}

// The mapping of token address to source of token price has to live offchain.
// Best we can do is sanity check that the token price spec covers all our desired execution token prices.
for _, token := range tokens {
if _, ok = tokenPrices[token]; !ok {
return nil, errors.Errorf("missing token %s from tokensForFeeCoin spec, got %v", token, prices)
}
tokenPrices[tokenAddressStr] = castedPrice
}

return tokenPrices, nil
Expand Down

0 comments on commit 2c27db7

Please sign in to comment.