Skip to content

Commit

Permalink
Report all prices from Jobspec (#14185)
Browse files Browse the repository at this point in the history
* Report all prices from Jobspec (#1275)

Fetch all the token prices in the jobspec for dest tokens

* Missing changeset

---------

Co-authored-by: nogo <[email protected]>
  • Loading branch information
mateusz-sekara and 0xnogo authored Aug 22, 2024
1 parent 8322953 commit b563d77
Show file tree
Hide file tree
Showing 17 changed files with 973 additions and 315 deletions.
5 changes: 5 additions & 0 deletions .changeset/lucky-boats-run.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"chainlink": patch
---

Reporting all the token prices from the job spec for CCIP #updated
5 changes: 5 additions & 0 deletions .mockery.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -529,6 +529,11 @@ packages:
PriceGetter:
config:
mockname: "Mock{{ .InterfaceName }}"
filename: mock.go
AllTokensPriceGetter:
config:
mockname: "Mock{{ .InterfaceName }}"
filename: all_price_getter_mock.go
github.com/smartcontractkit/chainlink/v2/core/services/relay/evm/statuschecker:
interfaces:
CCIPTransactionStatusChecker:
Expand Down
2 changes: 1 addition & 1 deletion core/services/ocr2/plugins/ccip/ccipcommit/initializers.go
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ func NewCommitServices(ctx context.Context, ds sqlutil.DataSource, srcProvider c
commitStoreReader = ccip.NewProviderProxyCommitStoreReader(srcCommitStore, dstCommitStore)
commitLggr := lggr.Named("CCIPCommit").With("sourceChain", sourceChainID, "destChain", destChainID)

var priceGetter pricegetter.PriceGetter
var priceGetter pricegetter.AllTokensPriceGetter
withPipeline := strings.Trim(pluginConfig.TokenPricesUSDPipeline, "\n\t ") != ""
if withPipeline {
priceGetter, err = pricegetter.NewPipelineGetter(pluginConfig.TokenPricesUSDPipeline, pr, jb.ID, jb.ExternalJobID, jb.Name.ValueOrZero(), lggr)
Expand Down
26 changes: 13 additions & 13 deletions core/services/ocr2/plugins/ccip/clo_ccip_integration_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package ccip_test

import (
"context"
"encoding/json"
"math/big"
"testing"
Expand Down Expand Up @@ -32,7 +33,6 @@ func Test_CLOSpecApprovalFlow_dynamicPriceGetter(t *testing.T) {
ccipTH := integrationtesthelpers.SetupCCIPIntegrationTH(t, testhelpers.SourceChainID, testhelpers.SourceChainSelector, testhelpers.DestChainID, testhelpers.DestChainSelector)

//Set up the aggregators here to avoid modifying ccipTH.
srcLinkAddr := ccipTH.Source.LinkToken.Address()
dstLinkAddr := ccipTH.Dest.LinkToken.Address()
srcNativeAddr, err := ccipTH.Source.Router.GetWrappedNative(nil)
require.NoError(t, err)
Expand All @@ -44,13 +44,6 @@ func Test_CLOSpecApprovalFlow_dynamicPriceGetter(t *testing.T) {
require.NoError(t, err)
ccipTH.Source.Chain.Commit()

aggSrcLnkAddr, _, aggSrcLnk, err := mock_v3_aggregator_contract.DeployMockV3AggregatorContract(ccipTH.Source.User, ccipTH.Source.Chain, 18, big.NewInt(3e18))
require.NoError(t, err)
ccipTH.Dest.Chain.Commit()
_, err = aggSrcLnk.UpdateRoundData(ccipTH.Source.User, big.NewInt(50), big.NewInt(8000000), big.NewInt(1000), big.NewInt(1000))
require.NoError(t, err)
ccipTH.Source.Chain.Commit()

aggDstLnkAddr, _, aggDstLnk, err := mock_v3_aggregator_contract.DeployMockV3AggregatorContract(ccipTH.Dest.User, ccipTH.Dest.Chain, 18, big.NewInt(3e18))
require.NoError(t, err)
ccipTH.Dest.Chain.Commit()
Expand All @@ -74,10 +67,6 @@ func Test_CLOSpecApprovalFlow_dynamicPriceGetter(t *testing.T) {

priceGetterConfig := config.DynamicPriceGetterConfig{
AggregatorPrices: map[common.Address]config.AggregatorPriceConfig{
srcLinkAddr: {
ChainID: ccipTH.Source.ChainID,
AggregatorContractAddress: aggSrcLnkAddr,
},
srcNativeAddr: {
ChainID: ccipTH.Source.ChainID,
AggregatorContractAddress: aggSrcNatAddr,
Expand Down Expand Up @@ -125,11 +114,22 @@ func test_CLOSpecApprovalFlow(t *testing.T, ccipTH integrationtesthelpers.CCIPIn

_, err = ccipTH.Source.LinkToken.Approve(ccipTH.Source.User, ccipTH.Source.Router.Address(), new(big.Int).Set(fee))
require.NoError(t, err)
ccipTH.Source.Chain.Commit()
blockHash := ccipTH.Dest.Chain.Commit()
// get the block number
block, err := ccipTH.Dest.Chain.BlockByHash(context.Background(), blockHash)
require.NoError(t, err)
blockNumber := block.Number().Uint64() + 1 // +1 as a block will be mined for the request from EventuallyReportCommitted

ccipTH.SendRequest(t, msg)
ccipTH.AllNodesHaveReqSeqNum(t, currentSeqNum)
ccipTH.EventuallyReportCommitted(t, currentSeqNum)
ccipTH.EventuallyPriceRegistryUpdated(
t,
blockNumber,
ccipTH.Source.ChainSelector,
[]common.Address{ccipTH.Dest.LinkToken.Address(), ccipTH.Dest.WrappedNative.Address()},
ccipTH.Source.WrappedNative.Address(),
)

executionLogs := ccipTH.AllNodesHaveExecutedSeqNums(t, currentSeqNum, currentSeqNum)
assert.Len(t, executionLogs, 1)
Expand Down
11 changes: 0 additions & 11 deletions core/services/ocr2/plugins/ccip/integration_legacy_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,13 +63,6 @@ func TestIntegration_legacy_CCIP(t *testing.T) {
require.NoError(t, err)
ccipTH.Source.Chain.Commit()

aggSrcLnkAddr, _, aggSrcLnk, err := mock_v3_aggregator_contract.DeployMockV3AggregatorContract(ccipTH.Source.User, ccipTH.Source.Chain, 18, big.NewInt(3e18))
require.NoError(t, err)
ccipTH.Dest.Chain.Commit()
_, err = aggSrcLnk.UpdateRoundData(ccipTH.Source.User, big.NewInt(50), big.NewInt(8000000), big.NewInt(1000), big.NewInt(1000))
require.NoError(t, err)
ccipTH.Source.Chain.Commit()

aggDstLnkAddr, _, aggDstLnk, err := mock_v3_aggregator_contract.DeployMockV3AggregatorContract(ccipTH.Dest.User, ccipTH.Dest.Chain, 18, big.NewInt(3e18))
require.NoError(t, err)
ccipTH.Dest.Chain.Commit()
Expand All @@ -79,10 +72,6 @@ func TestIntegration_legacy_CCIP(t *testing.T) {

priceGetterConfig := config.DynamicPriceGetterConfig{
AggregatorPrices: map[common.Address]config.AggregatorPriceConfig{
ccipTH.Source.LinkToken.Address(): {
ChainID: ccipTH.Source.ChainID,
AggregatorContractAddress: aggSrcLnkAddr,
},
ccipTH.Source.WrappedNative.Address(): {
ChainID: ccipTH.Source.ChainID,
AggregatorContractAddress: aggSrcNatAddr,
Expand Down
11 changes: 0 additions & 11 deletions core/services/ocr2/plugins/ccip/integration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -66,13 +66,6 @@ func TestIntegration_CCIP(t *testing.T) {
require.NoError(t, err)
ccipTH.Source.Chain.Commit()

aggSrcLnkAddr, _, aggSrcLnk, err := mock_v3_aggregator_contract.DeployMockV3AggregatorContract(ccipTH.Source.User, ccipTH.Source.Chain, 18, big.NewInt(3e18))
require.NoError(t, err)
ccipTH.Dest.Chain.Commit()
_, err = aggSrcLnk.UpdateRoundData(ccipTH.Source.User, big.NewInt(50), big.NewInt(8000000), big.NewInt(1000), big.NewInt(1000))
require.NoError(t, err)
ccipTH.Source.Chain.Commit()

aggDstLnkAddr, _, aggDstLnk, err := mock_v3_aggregator_contract.DeployMockV3AggregatorContract(ccipTH.Dest.User, ccipTH.Dest.Chain, 18, big.NewInt(3e18))
require.NoError(t, err)
ccipTH.Dest.Chain.Commit()
Expand All @@ -82,10 +75,6 @@ func TestIntegration_CCIP(t *testing.T) {

priceGetterConfig := config.DynamicPriceGetterConfig{
AggregatorPrices: map[common.Address]config.AggregatorPriceConfig{
ccipTH.Source.LinkToken.Address(): {
ChainID: ccipTH.Source.ChainID,
AggregatorContractAddress: aggSrcLnkAddr,
},
ccipTH.Source.WrappedNative.Address(): {
ChainID: ccipTH.Source.ChainID,
AggregatorContractAddress: aggSrcNatAddr,
Expand Down
19 changes: 1 addition & 18 deletions core/services/ocr2/plugins/ccip/internal/ccipcommon/shortcuts.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,24 +32,7 @@ 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) {
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...)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,22 +3,14 @@ package ccipcommon
import (
"fmt"
"math/rand"
"sort"
"strconv"
"testing"
"time"

"github.com/ethereum/go-ethereum/common"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/mock"

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

"github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils"
"github.com/smartcontractkit/chainlink/v2/core/internal/testutils"
"github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ccip/internal/ccipcalc"
ccipdatamocks "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ccip/internal/ccipdata/mocks"
"github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ccip/internal/pricegetter"
)

func TestGetMessageIDsAsHexString(t *testing.T) {
Expand Down Expand Up @@ -70,77 +62,47 @@ func TestFlattenUniqueSlice(t *testing.T) {
}
}

func TestGetFilteredChainTokens(t *testing.T) {
const numTokens = 6
var tokens []cciptypes.Address
for i := 0; i < numTokens; i++ {
tokens = append(tokens, ccipcalc.EvmAddrToGeneric(utils.RandomAddress()))
}

func TestFlattenedAndSortedTokens(t *testing.T) {
testCases := []struct {
name string
feeTokens []cciptypes.Address
destTokens []cciptypes.Address
expectedChainTokens []cciptypes.Address
expectedFilteredTokens []cciptypes.Address
name string
inputSlices [][]cciptypes.Address
expectedOutput []cciptypes.Address
}{
{name: "empty", inputSlices: nil, expectedOutput: []cciptypes.Address{}},
{name: "empty 2", inputSlices: [][]cciptypes.Address{}, expectedOutput: []cciptypes.Address{}},
{
name: "empty",
feeTokens: []cciptypes.Address{},
destTokens: []cciptypes.Address{},
expectedChainTokens: []cciptypes.Address{},
expectedFilteredTokens: []cciptypes.Address{},
name: "single",
inputSlices: [][]cciptypes.Address{{"0x1", "0x2", "0x3"}},
expectedOutput: []cciptypes.Address{"0x1", "0x2", "0x3"},
},
{
name: "unique tokens",
feeTokens: []cciptypes.Address{tokens[0]},
destTokens: []cciptypes.Address{tokens[1], tokens[2], tokens[3]},
expectedChainTokens: []cciptypes.Address{tokens[0], tokens[1], tokens[2], tokens[3]},
expectedFilteredTokens: []cciptypes.Address{tokens[4], tokens[5]},
name: "simple",
inputSlices: [][]cciptypes.Address{{"0x1", "0x2", "0x3"}, {"0x2", "0x3", "0x4"}},
expectedOutput: []cciptypes.Address{"0x1", "0x2", "0x3", "0x4"},
},
{
name: "all tokens",
feeTokens: []cciptypes.Address{tokens[0]},
destTokens: []cciptypes.Address{tokens[1], tokens[2], tokens[3], tokens[4], tokens[5]},
expectedChainTokens: []cciptypes.Address{tokens[0], tokens[1], tokens[2], tokens[3], tokens[4], tokens[5]},
expectedFilteredTokens: []cciptypes.Address{},
},
{
name: "overlapping tokens",
feeTokens: []cciptypes.Address{tokens[0]},
destTokens: []cciptypes.Address{tokens[1], tokens[2], tokens[5], tokens[3], tokens[0], tokens[2], tokens[3], tokens[4], tokens[5], tokens[5]},
expectedChainTokens: []cciptypes.Address{tokens[0], tokens[1], tokens[2], tokens[3], tokens[4], tokens[5]},
expectedFilteredTokens: []cciptypes.Address{},
},
{
name: "unconfigured tokens",
feeTokens: []cciptypes.Address{tokens[0]},
destTokens: []cciptypes.Address{tokens[0], tokens[1], tokens[2], tokens[3], tokens[0], tokens[2], tokens[3], tokens[4], tokens[5], tokens[5]},
expectedChainTokens: []cciptypes.Address{tokens[0], tokens[1], tokens[2], tokens[3], tokens[4]},
expectedFilteredTokens: []cciptypes.Address{tokens[5]},
name: "more complex case",
inputSlices: [][]cciptypes.Address{
{"0x1", "0x3"},
{"0x2", "0x4", "0x3"},
{"0x5", "0x2", "0x7", "0xa"},
},
expectedOutput: []cciptypes.Address{
"0x1",
"0x2",
"0x3",
"0x4",
"0x5",
"0x7",
"0xa",
},
},
}

ctx := testutils.Context(t)
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
priceRegistry := ccipdatamocks.NewPriceRegistryReader(t)
priceRegistry.On("GetFeeTokens", ctx).Return(tc.feeTokens, nil).Once()

priceGet := pricegetter.NewMockPriceGetter(t)
priceGet.On("FilterConfiguredTokens", mock.Anything, mock.Anything).Return(tc.expectedChainTokens, tc.expectedFilteredTokens, nil)

offRamp := ccipdatamocks.NewOffRampReader(t)
offRamp.On("GetTokens", ctx).Return(cciptypes.OffRampTokens{DestinationTokens: tc.destTokens}, nil).Once()

chainTokens, filteredTokens, err := GetFilteredSortedLaneTokens(ctx, offRamp, priceRegistry, priceGet)
assert.NoError(t, err)

sort.Slice(tc.expectedChainTokens, func(i, j int) bool {
return tc.expectedChainTokens[i] < tc.expectedChainTokens[j]
})
assert.Equal(t, tc.expectedChainTokens, chainTokens)
assert.Equal(t, tc.expectedFilteredTokens, filteredTokens)
res := FlattenedAndSortedTokens(tc.inputSlices...)
assert.Equal(t, tc.expectedOutput, res)
})
}
}
Expand Down
Loading

0 comments on commit b563d77

Please sign in to comment.