diff --git a/commit/plugin.go b/commit/plugin.go index 9e2b1935d..983d45c13 100644 --- a/commit/plugin.go +++ b/commit/plugin.go @@ -3,7 +3,6 @@ package commit import ( "context" "fmt" - "sync/atomic" "time" "github.com/smartcontractkit/libocr/commontypes" @@ -30,9 +29,9 @@ import ( "github.com/smartcontractkit/chainlink-ccip/pluginconfig" ) -type attributedMerkleRootObservation = plugincommon.AttributedObservation[merkleroot.Observation] -type attributedTokenPricesObservation = plugincommon.AttributedObservation[tokenprice.Observation] -type attributedChainFeeObservation = plugincommon.AttributedObservation[chainfee.Observation] +type merkleRootObservation = plugincommon.AttributedObservation[merkleroot.Observation] +type tokenPricesObservation = plugincommon.AttributedObservation[tokenprice.Observation] +type chainFeeObservation = plugincommon.AttributedObservation[chainfee.Observation] type Plugin struct { donID plugintypes.DonID @@ -53,7 +52,7 @@ type Plugin struct { discoveryProcessor *discovery.ContractDiscoveryProcessor // state - contractsInitialized atomic.Bool + contractsInitialized bool } func NewPlugin( @@ -171,10 +170,7 @@ func (p *Plugin) Query(ctx context.Context, outCtx ocr3types.OutcomeContext) (ty var err error var q Query - prevOutcome, err := decodeOutcome(outCtx.PreviousOutcome) - if err != nil { - return nil, fmt.Errorf("decode previous outcome: %w", err) - } + prevOutcome := p.decodeOutcome(outCtx.PreviousOutcome) q.MerkleRootQuery, err = p.merkleRootProcessor.Query(ctx, prevOutcome.MerkleRootOutcome) if err != nil { @@ -199,157 +195,165 @@ func (p *Plugin) ObservationQuorum( ) (bool, error) { // Across all chains we require at least 2F+1 observations. return quorumhelper.ObservationCountReachesObservationQuorum( - quorumhelper.QuorumTwoFPlusOne, - p.reportingCfg.N, - p.reportingCfg.F, - aos, - ), nil + quorumhelper.QuorumTwoFPlusOne, p.reportingCfg.N, p.reportingCfg.F, aos), nil } func (p *Plugin) Observation( ctx context.Context, outCtx ocr3types.OutcomeContext, q types.Query, ) (types.Observation, error) { - // If the contracts are not initialized then only submit contracts discovery related observation. - if !p.contractsInitialized.Load() && p.discoveryProcessor != nil { - discoveryObs, err := p.discoveryProcessor.Observation(ctx, dt.Outcome{}, dt.Query{}) - if err != nil { - p.lggr.Errorw("failed to discover contracts", "err", err) - } - - obs := Observation{DiscoveryObs: discoveryObs} - encoded, err := obs.Encode() - if err != nil { - return nil, fmt.Errorf("encode discovery observation: %w, observation: %+v", err, obs) - } - - p.lggr.Infow("contracts not initialized, only making discovery observations", "discoveryObs", discoveryObs) - p.lggr.Debugw("commit plugin making observation", "encodedObservation", encoded, "observation", obs) - - return encoded, nil - } - - prevOutcome, err := decodeOutcome(outCtx.PreviousOutcome) - if err != nil { - return nil, fmt.Errorf("decode previous outcome: %w", err) - } + prevOutcome := p.decodeOutcome(outCtx.PreviousOutcome) + fChain := p.ObserveFChain() decodedQ, err := DecodeCommitPluginQuery(q) if err != nil { return nil, fmt.Errorf("decode query: %w", err) } + var discoveryObs dt.Observation + if p.discoveryProcessor != nil { + discoveryObs, err = p.discoveryProcessor.Observation(ctx, dt.Outcome{}, dt.Query{}) + if err != nil { + p.lggr.Errorw("failed to discover contracts", "err", err) + } + if !p.contractsInitialized { + obs := Observation{DiscoveryObs: discoveryObs} + encoded, err := obs.Encode() + if err != nil { + return nil, fmt.Errorf("failed to encode observation: %w, observation: %+v", err, obs) + } + + p.lggr.Infow("contracts not initialized, only making discovery observations", + "discoveryObs", discoveryObs) + p.lggr.Debugw("Commit plugin making observation", + "encodedObservation", encoded, + "observation", obs) + return encoded, nil + } + } + merkleRootObs, err := p.merkleRootProcessor.Observation(ctx, prevOutcome.MerkleRootOutcome, decodedQ.MerkleRootQuery) if err != nil { - p.lggr.Errorw("get merkle root processor observation", - "err", err, "prevOutcome", prevOutcome.MerkleRootOutcome, "decodedQ", decodedQ.MerkleRootQuery) + p.lggr.Errorw("failed to get merkle observation", "err", err) } - tokenPriceObs, err := p.tokenPriceProcessor.Observation(ctx, prevOutcome.TokenPriceOutcome, decodedQ.TokenPriceQuery) if err != nil { - p.lggr.Errorw("get token price processor observation", "err", err, - "prevOutcome", prevOutcome.TokenPriceOutcome, "decodedQ", decodedQ.TokenPriceQuery) + p.lggr.Errorw("failed to get token prices", "err", err) } - chainFeeObs, err := p.chainFeeProcessor.Observation(ctx, prevOutcome.ChainFeeOutcome, decodedQ.ChainFeeQuery) if err != nil { - p.lggr.Errorw("get gas prices processor observation", - "err", err, "prevOutcome", prevOutcome.ChainFeeOutcome, "decodedQ", decodedQ.ChainFeeQuery) + p.lggr.Errorw("failed to get gas prices", "err", err) } obs := Observation{ MerkleRootObs: merkleRootObs, TokenPriceObs: tokenPriceObs, ChainFeeObs: chainFeeObs, - FChain: p.ObserveFChain(), + DiscoveryObs: discoveryObs, + FChain: fChain, } - encoded, err := obs.Encode() if err != nil { - return nil, fmt.Errorf("encode observation: %w, observation: %+v", err, obs) + return nil, fmt.Errorf("failed to encode observation: %w, observation: %+v", err, obs) } - p.lggr.Debugw("Commit plugin making observation", "encodedObservation", encoded, "observation", obs) + p.lggr.Debugw("Commit plugin making observation", + "encodedObservation", encoded, "observation", obs) return encoded, nil } func (p *Plugin) ObserveFChain() map[cciptypes.ChainSelector]int { fChain, err := p.homeChain.GetFChain() if err != nil { + // TODO: metrics p.lggr.Errorw("call to GetFChain failed", "err", err) return map[cciptypes.ChainSelector]int{} } return fChain } +// Outcome depending on the current state, either: +// - chooses the seq num ranges for the next round +// - builds a report +// - checks for the transmission of a previous report func (p *Plugin) Outcome( ctx context.Context, outCtx ocr3types.OutcomeContext, q types.Query, aos []types.AttributedObservation, ) (ocr3types.Outcome, error) { - p.lggr.Debugw("performing outcome", "outctx", outCtx, "query", q, "attributedObservations", aos) + p.lggr.Debugw("Commit plugin performing outcome", + "outctx", outCtx, + "query", q, + "attributedObservations", aos) - prevOutcome, err := decodeOutcome(outCtx.PreviousOutcome) - if err != nil { - return nil, fmt.Errorf("decode previous outcome: %w", err) - } + prevOutcome := p.decodeOutcome(outCtx.PreviousOutcome) decodedQ, err := DecodeCommitPluginQuery(q) if err != nil { return nil, fmt.Errorf("decode query: %w", err) } - merkleRootObservations := make([]attributedMerkleRootObservation, 0, len(aos)) - tokenPricesObservations := make([]attributedTokenPricesObservation, 0, len(aos)) - chainFeeObservations := make([]attributedChainFeeObservation, 0, len(aos)) - discoveryObservations := make([]plugincommon.AttributedObservation[dt.Observation], 0, len(aos)) + var merkleObservations []merkleRootObservation + var tokensObservations []tokenPricesObservation + var feeObservations []chainFeeObservation + var discoveryObservations []plugincommon.AttributedObservation[dt.Observation] for _, ao := range aos { obs, err := DecodeCommitPluginObservation(ao.Observation) if err != nil { - p.lggr.Warnw("failed to decode observation, observation skipped", "err", err) + p.lggr.Errorw("failed to decode observation", "err", err) continue } - p.lggr.Debugw("Commit plugin outcome decoded observation", "observation", obs) - - merkleRootObservations = append(merkleRootObservations, attributedMerkleRootObservation{ - OracleID: ao.Observer, Observation: obs.MerkleRootObs}) - - tokenPricesObservations = append(tokenPricesObservations, attributedTokenPricesObservation{ - OracleID: ao.Observer, Observation: obs.TokenPriceObs}) - - chainFeeObservations = append(chainFeeObservations, attributedChainFeeObservation{ - OracleID: ao.Observer, Observation: obs.ChainFeeObs}) - - discoveryObservations = append(discoveryObservations, plugincommon.AttributedObservation[dt.Observation]{ - OracleID: ao.Observer, Observation: obs.DiscoveryObs}) + merkleObservations = append(merkleObservations, + merkleRootObservation{ + OracleID: ao.Observer, + Observation: obs.MerkleRootObs, + }, + ) + + tokensObservations = append(tokensObservations, + tokenPricesObservation{ + OracleID: ao.Observer, + Observation: obs.TokenPriceObs, + }, + ) + + feeObservations = append(feeObservations, + chainFeeObservation{ + OracleID: ao.Observer, + Observation: obs.ChainFeeObs, + }, + ) + + discoveryObservations = append(discoveryObservations, + plugincommon.AttributedObservation[dt.Observation]{ + OracleID: ao.Observer, + Observation: obs.DiscoveryObs, + }) } if p.discoveryProcessor != nil { p.lggr.Infow("Processing discovery observations", "discoveryObservations", discoveryObservations) - - // The outcome phase of the discovery processor is binding contracts to the chain reader. This is the reason - // we ignore the outcome of the discovery processor. _, err = p.discoveryProcessor.Outcome(ctx, dt.Outcome{}, dt.Query{}, discoveryObservations) if err != nil { - return nil, fmt.Errorf("discovery processor outcome: %w", err) + return nil, fmt.Errorf("unable to process outcome of discovery processor: %w", err) } - p.contractsInitialized.Store(true) + p.contractsInitialized = true } merkleRootOutcome, err := p.merkleRootProcessor.Outcome( ctx, prevOutcome.MerkleRootOutcome, decodedQ.MerkleRootQuery, - merkleRootObservations, + merkleObservations, ) if err != nil { - p.lggr.Errorw(" get merkle roots outcome", "err", err) + p.lggr.Errorw("failed to get merkle outcome", "err", err) } tokenPriceOutcome, err := p.tokenPriceProcessor.Outcome( ctx, prevOutcome.TokenPriceOutcome, decodedQ.TokenPriceQuery, - tokenPricesObservations, + tokensObservations, ) if err != nil { p.lggr.Warnw("failed to get token prices outcome", "err", err) @@ -359,7 +363,7 @@ func (p *Plugin) Outcome( ctx, prevOutcome.ChainFeeOutcome, decodedQ.ChainFeeQuery, - chainFeeObservations, + feeObservations, ) if err != nil { p.lggr.Warnw("failed to get gas prices outcome", "err", err) @@ -381,6 +385,20 @@ func (p *Plugin) Close() error { ) } +func (p *Plugin) decodeOutcome(outcome ocr3types.Outcome) Outcome { + if len(outcome) == 0 { + return Outcome{} + } + + decodedOutcome, err := DecodeOutcome(outcome) + if err != nil { + p.lggr.Errorw("Failed to decode Outcome", "outcome", outcome, "err", err) + return Outcome{} + } + + return decodedOutcome +} + // Assuming that we have to delegate a specific amount of time to the observation requests and the report requests. // We define some percentages in order to help us calculate the time we have to delegate to each request timer. const ( diff --git a/commit/plugin_e2e_test.go b/commit/plugin_e2e_test.go index 482c0bf11..0b57d4490 100644 --- a/commit/plugin_e2e_test.go +++ b/commit/plugin_e2e_test.go @@ -242,7 +242,7 @@ func TestPlugin_E2E_AllNodesAgree_MerkleRoots(t *testing.T) { res, err := runner.RunRound(params.ctx) assert.NoError(t, err) - decodedOutcome, err := decodeOutcome(res.Outcome) + decodedOutcome, err := DecodeOutcome(res.Outcome) assert.NoError(t, err) assert.Equal(t, normalizeOutcome(tc.expOutcome), normalizeOutcome(decodedOutcome)) @@ -387,7 +387,7 @@ func TestPlugin_E2E_AllNodesAgree_TokenPrices(t *testing.T) { res, err := runner.RunRound(params.ctx) assert.NoError(t, err) - decodedOutcome, err := decodeOutcome(res.Outcome) + decodedOutcome, err := DecodeOutcome(res.Outcome) assert.NoError(t, err) assert.Equal(t, normalizeOutcome(tc.expOutcome), normalizeOutcome(decodedOutcome)) @@ -614,7 +614,7 @@ func TestPlugin_E2E_AllNodesAgree_ChainFee(t *testing.T) { res, err := runner.RunRound(params.ctx) assert.NoError(t, err) - decodedOutcome, err := decodeOutcome(res.Outcome) + decodedOutcome, err := DecodeOutcome(res.Outcome) assert.NoError(t, err) assert.Equal(t, normalizeOutcome(tc.expOutcome), normalizeOutcome(decodedOutcome)) diff --git a/commit/report.go b/commit/report.go index ead8e7278..fc363c70c 100644 --- a/commit/report.go +++ b/commit/report.go @@ -2,6 +2,7 @@ package commit import ( "context" + "encoding/hex" "encoding/json" "fmt" @@ -24,7 +25,7 @@ func (ri ReportInfo) Encode() ([]byte, error) { return json.Marshal(ri) } -// Decode should be used to decode the report info +// decode should be used to decode the report info func (ri *ReportInfo) Decode(encodedReportInfo []byte) error { return json.Unmarshal(encodedReportInfo, ri) } @@ -32,10 +33,11 @@ func (ri *ReportInfo) Decode(encodedReportInfo []byte) error { func (p *Plugin) Reports( ctx context.Context, seqNr uint64, outcomeBytes ocr3types.Outcome, ) ([]ocr3types.ReportPlus[[]byte], error) { - outcome, err := decodeOutcome(outcomeBytes) + outcome, err := DecodeOutcome(outcomeBytes) if err != nil { - p.lggr.Errorw("failed to decode Outcome", "outcome", string(outcomeBytes), "err", err) - return nil, fmt.Errorf("decode outcome: %w", err) + // TODO: metrics + p.lggr.Errorw("failed to decode Outcome", "outcomeBytes", outcomeBytes, "err", err) + return nil, fmt.Errorf("failed to decode Outcome (%s): %w", hex.EncodeToString(outcomeBytes), err) } // Gas prices and token prices do not need to get reported when merkle roots do not exist. @@ -71,7 +73,13 @@ func (p *Plugin) Reports( return nil, fmt.Errorf("encode commit plugin report: %w", err) } - reportInfo, err := ReportInfo{RemoteF: outcome.MerkleRootOutcome.RMNRemoteCfg.F}.Encode() + // Prepare the info data + reportInfo := ReportInfo{ + RemoteF: outcome.MerkleRootOutcome.RMNRemoteCfg.F, + } + + // Serialize reportInfo to []byte + infoBytes, err := reportInfo.Encode() if err != nil { return nil, fmt.Errorf("encode report info: %w", err) } @@ -80,7 +88,7 @@ func (p *Plugin) Reports( { ReportWithInfo: ocr3types.ReportWithInfo[[]byte]{ Report: encodedReport, - Info: reportInfo, + Info: infoBytes, }, }, }, nil diff --git a/commit/types.go b/commit/types.go index c81244f45..55bfa8015 100644 --- a/commit/types.go +++ b/commit/types.go @@ -68,15 +68,8 @@ func (o Outcome) Encode() ([]byte, error) { return encodedOutcome, nil } -func decodeOutcome(b []byte) (Outcome, error) { - if len(b) == 0 { - return Outcome{}, nil - } - +func DecodeOutcome(b []byte) (Outcome, error) { o := Outcome{} - if err := json.Unmarshal(b, &o); err != nil { - return Outcome{}, fmt.Errorf("decode outcome: %w", err) - } - - return o, nil + err := json.Unmarshal(b, &o) + return o, err } diff --git a/commit/validate_observation.go b/commit/validate_observation.go index 28bfa6c73..a570f0259 100644 --- a/commit/validate_observation.go +++ b/commit/validate_observation.go @@ -27,16 +27,12 @@ func (p *Plugin) ValidateObservation( return fmt.Errorf("failed to decode commit plugin observation: %w", err) } - prevOutcome, err := decodeOutcome(outCtx.PreviousOutcome) - if err != nil { - return fmt.Errorf("decode previous outcome: %w", err) - } - + prevOutcome := p.decodeOutcome(outCtx.PreviousOutcome) if err := validateFChain(obs.FChain); err != nil { return fmt.Errorf("failed to validate FChain: %w", err) } - merkleObs := attributedMerkleRootObservation{ + merkleObs := merkleRootObservation{ OracleID: ao.Observer, Observation: obs.MerkleRootObs, } @@ -46,7 +42,7 @@ func (p *Plugin) ValidateObservation( return fmt.Errorf("validate merkle roots observation: %w", err) } - tokenObs := attributedTokenPricesObservation{ + tokenObs := tokenPricesObservation{ OracleID: ao.Observer, Observation: obs.TokenPriceObs, } @@ -55,7 +51,7 @@ func (p *Plugin) ValidateObservation( return fmt.Errorf("validate token prices observation: %w", err) } - gasObs := attributedChainFeeObservation{ + gasObs := chainFeeObservation{ OracleID: ao.Observer, Observation: obs.ChainFeeObs, } diff --git a/execute/exectypes/costly_messages.go b/execute/exectypes/costly_messages.go index 699717742..498b8223d 100644 --- a/execute/exectypes/costly_messages.go +++ b/execute/exectypes/costly_messages.go @@ -10,8 +10,6 @@ import ( "github.com/smartcontractkit/chainlink-ccip/execute/internal/gas" - "github.com/smartcontractkit/chainlink-common/pkg/types" - "github.com/smartcontractkit/chainlink-ccip/internal/plugintypes" readerpkg "github.com/smartcontractkit/chainlink-ccip/pkg/reader" cciptypes "github.com/smartcontractkit/chainlink-ccip/pkg/types/ccipocr3" @@ -299,10 +297,7 @@ func (c *CCIPMessageFeeUSD18Calculator) MessageFeeUSD18( messageFees := make(map[cciptypes.Bytes32]plugintypes.USD18) for _, msg := range messages { - feeUSD18 := new(big.Int).Div( - new(big.Int).Mul(linkPriceUSD.Int, msg.FeeValueJuels.Int), - new(big.Int).Exp(big.NewInt(10), big.NewInt(18), nil), - ) + feeUSD18 := new(big.Int).Mul(linkPriceUSD.Int, msg.FeeValueJuels.Int) timestamp, ok := messageTimeStamps[msg.Header.MessageID] if !ok { // If a timestamp is missing we can't do fee boosting, but we still record the fee. In the worst case, the @@ -339,16 +334,12 @@ type CCIPMessageExecCostUSD18Calculator struct { estimateProvider gas.EstimateProvider } -// getFeesUSD18 converts the fee components (ExecutionFee and DataAvailabilityFee) from native token units -// to USD with 18 decimals (USD18). +// MessageExecCostUSD18 returns a map from message ID to the message's estimated execution cost in USD18s. func (c *CCIPMessageExecCostUSD18Calculator) MessageExecCostUSD18( ctx context.Context, messages []cciptypes.Message, ) (map[cciptypes.Bytes32]plugintypes.USD18, error) { messageExecCosts := make(map[cciptypes.Bytes32]plugintypes.USD18) - - // Retrieve the fee components from the destination chain. - // feeComponents.ExecutionFee and feeComponents.DataAvailabilityFee are in native token units. feeComponents, err := c.ccipReader.GetDestChainFeeComponents(ctx) if err != nil { return nil, fmt.Errorf("unable to get fee components: %w", err) @@ -359,28 +350,14 @@ func (c *CCIPMessageExecCostUSD18Calculator) MessageExecCostUSD18( if feeComponents.DataAvailabilityFee == nil { return nil, fmt.Errorf("missing data availability fee") } - if len(messages) == 0 { - return messageExecCosts, nil - } - - // Calculate execution fee in USD18 by multiplying the execution fee (in native tokens) by the native token price. - // feeComponents.ExecutionFee is in native tokens, nativeTokenPrice is in USD18, so the result is scaled by 1e18. - executionFee, daFee, err := c.getFeesUSD18(ctx, feeComponents, messages[0].Header.DestChainSelector) - if err != nil { - return nil, fmt.Errorf("unable to convert fee components to USD18: %w", err) - } - - // Calculate da fee in USD18 by multiplying the data availability fee (in native tokens) by the native token price. - // feeComponents.DataAvailabilityFee is in native tokens, nativeTokenPrice is in USD18, - // so the result is scaled by 1e18. daConfig, err := c.ccipReader.GetMedianDataAvailabilityGasConfig(ctx) if err != nil { return nil, fmt.Errorf("unable to get data availability gas config: %w", err) } for _, msg := range messages { - executionCostUSD18 := c.computeExecutionCostUSD18(executionFee, msg) - dataAvailabilityCostUSD18 := computeDataAvailabilityCostUSD18(daFee, daConfig, msg) + executionCostUSD18 := c.computeExecutionCostUSD18(feeComponents.ExecutionFee, msg) + dataAvailabilityCostUSD18 := computeDataAvailabilityCostUSD18(feeComponents.DataAvailabilityFee, daConfig, msg) totalCostUSD18 := new(big.Int).Add(executionCostUSD18, dataAvailabilityCostUSD18) messageExecCosts[msg.Header.MessageID] = totalCostUSD18 } @@ -388,35 +365,6 @@ func (c *CCIPMessageExecCostUSD18Calculator) MessageExecCostUSD18( return messageExecCosts, nil } -func (c *CCIPMessageExecCostUSD18Calculator) getFeesUSD18( - ctx context.Context, - feeComponents types.ChainFeeComponents, - destChainSelector cciptypes.ChainSelector, -) (plugintypes.USD18, plugintypes.USD18, error) { - nativeTokenPrices := c.ccipReader.GetWrappedNativeTokenPriceUSD( - ctx, - []cciptypes.ChainSelector{destChainSelector}) - if nativeTokenPrices == nil { - return nil, nil, fmt.Errorf("unable to get native token prices") - } - nativeTokenPrice, ok := nativeTokenPrices[destChainSelector] - if !ok { - return nil, nil, fmt.Errorf("missing native token price for chain %s", destChainSelector) - } - - if (feeComponents.ExecutionFee).Cmp(big.NewInt(0)) == 0 { - return big.NewInt(0), big.NewInt(0), nil - } - executionFee := new(big.Int).Div(nativeTokenPrice.Int, feeComponents.ExecutionFee) - - if (feeComponents.DataAvailabilityFee).Cmp(big.NewInt(0)) == 0 { - return executionFee, big.NewInt(0), nil - } - dataAvailabilityFee := new(big.Int).Div(nativeTokenPrice.Int, feeComponents.DataAvailabilityFee) - - return executionFee, dataAvailabilityFee, nil -} - // computeExecutionCostUSD18 computes the execution cost of a message in USD18s. // The cost is: // messageGas (gas) * executionFee (USD18/gas) = USD18 @@ -440,9 +388,7 @@ func computeDataAvailabilityCostUSD18( } messageGas := calculateMessageMaxDAGas(message, daConfig) - cost := big.NewInt(0).Mul(messageGas, dataAvailabilityFee) - - return cost + return big.NewInt(0).Mul(messageGas, dataAvailabilityFee) } // calculateMessageMaxDAGas calculates the total DA gas needed for a CCIP message diff --git a/execute/exectypes/costly_messages_test.go b/execute/exectypes/costly_messages_test.go index 1ab6c2580..86bd9cd14 100644 --- a/execute/exectypes/costly_messages_test.go +++ b/execute/exectypes/costly_messages_test.go @@ -291,10 +291,6 @@ func TestCCIPMessageFeeE18USDCalculator_MessageFeeE18USD(t *testing.T) { } func TestCCIPMessageExecCostUSD18Calculator_MessageExecCostUSD18(t *testing.T) { - destChainSelector := ccipocr3.ChainSelector(1) - nativeTokenPrice := ccipocr3.BigInt{ - Int: new(big.Int).Mul(big.NewInt(9), new(big.Int).Exp(big.NewInt(10), big.NewInt(18), nil))} - tests := []struct { name string messages []ccipocr3.Message @@ -310,17 +306,17 @@ func TestCCIPMessageExecCostUSD18Calculator_MessageExecCostUSD18(t *testing.T) { name: "happy path, no DA cost", messages: []ccipocr3.Message{ { - Header: ccipocr3.RampMessageHeader{MessageID: b1, DestChainSelector: destChainSelector}, + Header: ccipocr3.RampMessageHeader{MessageID: b1}, }, { - Header: ccipocr3.RampMessageHeader{MessageID: b2, DestChainSelector: destChainSelector}, + Header: ccipocr3.RampMessageHeader{MessageID: b2}, }, { - Header: ccipocr3.RampMessageHeader{MessageID: b3, DestChainSelector: destChainSelector}, + Header: ccipocr3.RampMessageHeader{MessageID: b3}, }, }, messageGases: []uint64{100, 200, 300}, - executionFee: new(big.Int).Mul(big.NewInt(20), new(big.Int).Exp(big.NewInt(10), big.NewInt(9), nil)), + executionFee: big.NewInt(100), dataAvailabilityFee: big.NewInt(0), feeComponentsError: nil, daGasConfig: ccipocr3.DataAvailabilityGasConfig{ @@ -329,9 +325,9 @@ func TestCCIPMessageExecCostUSD18Calculator_MessageExecCostUSD18(t *testing.T) { DestDataAvailabilityMultiplierBps: 1, }, want: map[ccipocr3.Bytes32]plugintypes.USD18{ - b1: plugintypes.NewUSD18(45000000000), // 5_000_000_000 * 9 (price conversion) - b2: plugintypes.NewUSD18(90000000000), // 10_000_000_000 * 9 - b3: plugintypes.NewUSD18(135000000000), // 15_000_000_000 * 9 + b1: plugintypes.NewUSD18(10000), + b2: plugintypes.NewUSD18(20000), + b3: plugintypes.NewUSD18(30000), }, wantErr: false, }, @@ -339,18 +335,18 @@ func TestCCIPMessageExecCostUSD18Calculator_MessageExecCostUSD18(t *testing.T) { name: "happy path, with DA cost", messages: []ccipocr3.Message{ { - Header: ccipocr3.RampMessageHeader{MessageID: b1, DestChainSelector: destChainSelector}, + Header: ccipocr3.RampMessageHeader{MessageID: b1}, }, { - Header: ccipocr3.RampMessageHeader{MessageID: b2, DestChainSelector: destChainSelector}, + Header: ccipocr3.RampMessageHeader{MessageID: b2}, }, { - Header: ccipocr3.RampMessageHeader{MessageID: b3, DestChainSelector: destChainSelector}, + Header: ccipocr3.RampMessageHeader{MessageID: b3}, }, }, messageGases: []uint64{100, 200, 300}, - executionFee: new(big.Int).Mul(big.NewInt(20), new(big.Int).Exp(big.NewInt(10), big.NewInt(9), nil)), - dataAvailabilityFee: new(big.Int).Mul(big.NewInt(100), new(big.Int).Exp(big.NewInt(10), big.NewInt(9), nil)), + executionFee: big.NewInt(100), + dataAvailabilityFee: big.NewInt(400), feeComponentsError: nil, daGasConfig: ccipocr3.DataAvailabilityGasConfig{ DestDataAvailabilityOverheadGas: 1200, @@ -358,9 +354,9 @@ func TestCCIPMessageExecCostUSD18Calculator_MessageExecCostUSD18(t *testing.T) { DestDataAvailabilityMultiplierBps: 200, }, want: map[ccipocr3.Bytes32]plugintypes.USD18{ - b1: plugintypes.NewUSD18(55170000000), // 4.5e10 (exec) + 1.017e10 (da) - b2: plugintypes.NewUSD18(100170000000), // 9e10 (exec) + 1.017e10 (da) - b3: plugintypes.NewUSD18(145170000000), // 135e10 (exec) + 1.017e10 (da) + b1: plugintypes.NewUSD18(55200), // 10_000 (exec) + 45_200 (da) + b2: plugintypes.NewUSD18(65200), // 20_000 (exec) + 45_200 (da) + b3: plugintypes.NewUSD18(75200), // 30_000 (exec) + 45_200 (da) }, wantErr: false, }, @@ -368,7 +364,7 @@ func TestCCIPMessageExecCostUSD18Calculator_MessageExecCostUSD18(t *testing.T) { name: "message with token amounts affects DA gas calculation", messages: []ccipocr3.Message{ { - Header: ccipocr3.RampMessageHeader{MessageID: b1, DestChainSelector: destChainSelector}, + Header: ccipocr3.RampMessageHeader{MessageID: b1}, TokenAmounts: []ccipocr3.RampTokenAmount{ { SourcePoolAddress: []byte("source_pool"), @@ -385,8 +381,8 @@ func TestCCIPMessageExecCostUSD18Calculator_MessageExecCostUSD18(t *testing.T) { }, }, messageGases: []uint64{100}, - executionFee: new(big.Int).Mul(big.NewInt(20), new(big.Int).Exp(big.NewInt(10), big.NewInt(9), nil)), - dataAvailabilityFee: new(big.Int).Mul(big.NewInt(100), new(big.Int).Exp(big.NewInt(10), big.NewInt(9), nil)), + executionFee: big.NewInt(100), + dataAvailabilityFee: big.NewInt(400), feeComponentsError: nil, daGasConfig: ccipocr3.DataAvailabilityGasConfig{ DestDataAvailabilityOverheadGas: 1000, @@ -394,7 +390,7 @@ func TestCCIPMessageExecCostUSD18Calculator_MessageExecCostUSD18(t *testing.T) { DestDataAvailabilityMultiplierBps: 200, }, want: map[ccipocr3.Bytes32]plugintypes.USD18{ - b1: plugintypes.NewUSD18(60570000000), // 4.5e10 (exec) + 1.557e10 (da) + b1: plugintypes.NewUSD18(79200), // 10_000 (exec) + 69_200 (da) }, wantErr: false, }, @@ -402,13 +398,13 @@ func TestCCIPMessageExecCostUSD18Calculator_MessageExecCostUSD18(t *testing.T) { name: "zero DA multiplier results in only overhead gas", messages: []ccipocr3.Message{ { - Header: ccipocr3.RampMessageHeader{MessageID: b1, DestChainSelector: destChainSelector}, + Header: ccipocr3.RampMessageHeader{MessageID: b1}, Data: []byte("some_data"), }, }, messageGases: []uint64{100}, - executionFee: new(big.Int).Mul(big.NewInt(20), new(big.Int).Exp(big.NewInt(10), big.NewInt(9), nil)), - dataAvailabilityFee: new(big.Int).Mul(big.NewInt(100), new(big.Int).Exp(big.NewInt(10), big.NewInt(9), nil)), + executionFee: big.NewInt(100), + dataAvailabilityFee: big.NewInt(400), feeComponentsError: nil, daGasConfig: ccipocr3.DataAvailabilityGasConfig{ DestDataAvailabilityOverheadGas: 1000, @@ -416,7 +412,7 @@ func TestCCIPMessageExecCostUSD18Calculator_MessageExecCostUSD18(t *testing.T) { DestDataAvailabilityMultiplierBps: 0, // Zero multiplier }, want: map[ccipocr3.Bytes32]plugintypes.USD18{ - b1: plugintypes.NewUSD18(45000000000), // Only exec cost, DA cost is 0 + b1: plugintypes.NewUSD18(10000), // Only exec cost, DA cost is 0 }, wantErr: false, }, @@ -424,7 +420,7 @@ func TestCCIPMessageExecCostUSD18Calculator_MessageExecCostUSD18(t *testing.T) { name: "large message with multiple tokens", messages: []ccipocr3.Message{ { - Header: ccipocr3.RampMessageHeader{MessageID: b1, DestChainSelector: destChainSelector}, + Header: ccipocr3.RampMessageHeader{MessageID: b1}, TokenAmounts: []ccipocr3.RampTokenAmount{ { SourcePoolAddress: make([]byte, 100), // Large token data @@ -448,8 +444,8 @@ func TestCCIPMessageExecCostUSD18Calculator_MessageExecCostUSD18(t *testing.T) { }, }, messageGases: []uint64{100}, - executionFee: new(big.Int).Mul(big.NewInt(20), new(big.Int).Exp(big.NewInt(10), big.NewInt(9), nil)), - dataAvailabilityFee: new(big.Int).Mul(big.NewInt(100), new(big.Int).Exp(big.NewInt(10), big.NewInt(9), nil)), + executionFee: big.NewInt(100), + dataAvailabilityFee: big.NewInt(400), feeComponentsError: nil, daGasConfig: ccipocr3.DataAvailabilityGasConfig{ DestDataAvailabilityOverheadGas: 1000, @@ -457,7 +453,7 @@ func TestCCIPMessageExecCostUSD18Calculator_MessageExecCostUSD18(t *testing.T) { DestDataAvailabilityMultiplierBps: 200, }, want: map[ccipocr3.Bytes32]plugintypes.USD18{ - b1: plugintypes.NewUSD18(92160000000), // 4.5e10 (exec) + 4.716e10 (da) + b1: plugintypes.NewUSD18(219600), // 10_000 (exec) + 218_600 (da) }, wantErr: false, }, @@ -490,12 +486,12 @@ func TestCCIPMessageExecCostUSD18Calculator_MessageExecCostUSD18(t *testing.T) { name: "minimal message - only constant parts", messages: []ccipocr3.Message{ { - Header: ccipocr3.RampMessageHeader{MessageID: b1, DestChainSelector: destChainSelector}, + Header: ccipocr3.RampMessageHeader{MessageID: b1}, }, }, messageGases: []uint64{100}, - executionFee: new(big.Int).Mul(big.NewInt(20), new(big.Int).Exp(big.NewInt(10), big.NewInt(9), nil)), - dataAvailabilityFee: new(big.Int).Mul(big.NewInt(100), new(big.Int).Exp(big.NewInt(10), big.NewInt(9), nil)), + executionFee: big.NewInt(100), + dataAvailabilityFee: big.NewInt(400), feeComponentsError: nil, daGasConfig: ccipocr3.DataAvailabilityGasConfig{ DestDataAvailabilityOverheadGas: 1000, @@ -503,7 +499,7 @@ func TestCCIPMessageExecCostUSD18Calculator_MessageExecCostUSD18(t *testing.T) { DestDataAvailabilityMultiplierBps: 200, }, want: map[ccipocr3.Bytes32]plugintypes.USD18{ - b1: plugintypes.NewUSD18(54810000000), // 4.5e10 (exec) + 0.981e10 (da) + b1: plugintypes.NewUSD18(53600), // 10_000 (exec) + 43_600 (da) }, wantErr: false, }, @@ -520,14 +516,6 @@ func TestCCIPMessageExecCostUSD18Calculator_MessageExecCostUSD18(t *testing.T) { DataAvailabilityFee: tt.dataAvailabilityFee, } mockReader.EXPECT().GetDestChainFeeComponents(ctx).Return(feeComponents, tt.feeComponentsError) - mockReader.EXPECT().GetWrappedNativeTokenPriceUSD( - ctx, - []ccipocr3.ChainSelector{destChainSelector}, - ).Return( - map[ccipocr3.ChainSelector]ccipocr3.BigInt{ - destChainSelector: nativeTokenPrice, - }, - ).Maybe() if !tt.wantErr { mockReader.EXPECT().GetMedianDataAvailabilityGasConfig(ctx).Return(tt.daGasConfig, nil) } diff --git a/execute/plugin_e2e_test.go b/execute/plugin_e2e_test.go index d8b9ba61c..44fe9db4f 100644 --- a/execute/plugin_e2e_test.go +++ b/execute/plugin_e2e_test.go @@ -76,7 +76,6 @@ func Test_ExcludingCostlyMessages(t *testing.T) { tm := timeMachine{now: messageTimestamp} intTest := SetupSimpleTest(t, srcSelector, dstSelector) - intTest.WithMessages(messages, 1000, messageTimestamp) intTest.WithCustomFeeBoosting(1.0, tm.Now, map[cciptypes.Bytes32]plugintypes.USD18{ messages[0].Header.MessageID: plugintypes.NewUSD18(40000), diff --git a/execute/test_utils.go b/execute/test_utils.go index c3f35bc88..e1530449e 100644 --- a/execute/test_utils.go +++ b/execute/test_utils.go @@ -4,7 +4,6 @@ import ( "context" crand "crypto/rand" "encoding/binary" - "math/big" "net/http" "net/http/httptest" "strings" @@ -387,8 +386,7 @@ type msgOption func(*cciptypes.Message) func withFeeValueJuels(fee int64) msgOption { return func(m *cciptypes.Message) { - juels := new(big.Int).Mul(big.NewInt(fee), new(big.Int).Exp(big.NewInt(10), big.NewInt(18), nil)) - m.FeeValueJuels = cciptypes.NewBigInt(juels) + m.FeeValueJuels = cciptypes.NewBigIntFromInt64(fee) } } diff --git a/internal/plugintypes/common.go b/internal/plugintypes/common.go index 04a9f3821..b44cfe669 100644 --- a/internal/plugintypes/common.go +++ b/internal/plugintypes/common.go @@ -36,7 +36,3 @@ type USD18 = *big.Int func NewUSD18(value int64) USD18 { return big.NewInt(value) } - -func NewUSD18FromUSD(value int64) USD18 { - return new(big.Int).Mul(big.NewInt(value), new(big.Int).Exp(big.NewInt(10), big.NewInt(18), nil)) -}