Skip to content

Commit

Permalink
comments, some more tests
Browse files Browse the repository at this point in the history
  • Loading branch information
ogtownsend committed Jan 21, 2025
1 parent d790899 commit b3b3a86
Show file tree
Hide file tree
Showing 4 changed files with 104 additions and 28 deletions.
28 changes: 17 additions & 11 deletions core/services/ocr2/plugins/ccip/internal/ccipcalc/calc.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,9 @@ import (
)

const (
// DESTINATION_ETH_PPB_GATE is the deviation threshold when writing to ethereum's PriceRegistry
EthereumThresholdGatePPB = 4e9
// CurveBasedDeviationPPB is the deviation threshold when writing to ethereum's PriceRegistry and is the trigger for
// using curve-based deviation logic.
CurveBasedDeviationPPB = 4e9
)

// ContiguousReqs checks if seqNrs contains all numbers from min to max.
Expand Down Expand Up @@ -70,13 +71,13 @@ func Deviates(x1, x2 *big.Int, ppb int64) bool {
return diff.CmpAbs(big.NewInt(ppb)) > 0 // abs(diff) > ppb
}

// DeviatesOnGasCurve calculates a deviation threshold on the fly using xNew. For now it's only used for gas price
// DeviatesOnCurve calculates a deviation threshold on the fly using xNew. For now it's only used for gas price
// deviation calculation. It's important to make sure the order of xNew and xOld is correct when passed into this
// function to get an accurate deviation threshold.
func DeviatesOnGasCurve(xNew, xOld, noDeviationLowerBound *big.Int, ppb int64) bool {
func DeviatesOnCurve(xNew, xOld, noDeviationLowerBound *big.Int, ppb int64) bool {
// This is a temporary gating mechanism that ensures we only apply the gas curve deviation logic to eth-bound price
// updates. If ppb from config is not equal to 4000000000, do not apply the gas curve.
if ppb != EthereumThresholdGatePPB {
if ppb != CurveBasedDeviationPPB {
return Deviates(xOld, xNew, ppb)
}

Expand All @@ -88,19 +89,24 @@ func DeviatesOnGasCurve(xNew, xOld, noDeviationLowerBound *big.Int, ppb int64) b
xNewFloat := new(big.Float).SetInt(xNew)
xNewFloat64, _ := xNewFloat.Float64()

// Calculate the deviation threshold percentage with xNew using the formula: y = (10e11) / (xNew^0.665)
// We use xNew to generate the threshold so that when going from cheap --> expensive, xNew generates a smaller
// deviation threshold so we are more likely to update the gas price on chain. When going from expensive --> cheap,
// xNew generates a larger deviation threshold since it's not as urgent to update the gas price on chain.
curveThresholdPPB := calculateCurveThresholdPPB(xNewFloat64)
return Deviates(xNew, xOld, curveThresholdPPB)
}

// calculateCurveThresholdPPB calculates the deviation threshold percentage with xNew using the formula:
// y = (10e11) / (xNew^0.665).
func calculateCurveThresholdPPB(x float64) int64 {
const constantFactor = 10e11
const exponent = 0.665
xNewPower := math.Pow(xNewFloat64, exponent)
threshold := constantFactor / xNewPower
xPower := math.Pow(x, exponent)
threshold := constantFactor / xPower

// Convert percentage to PPB
// Convert curve output percentage to PPB
thresholdPPB := int64(threshold * 1e7)

return Deviates(xNew, xOld, thresholdPPB)
return thresholdPPB
}

func MergeEpochAndRound(epoch uint32, round uint8) uint64 {
Expand Down
90 changes: 79 additions & 11 deletions core/services/ocr2/plugins/ccip/internal/ccipcalc/calc_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package ccipcalc

import (
"fmt"
"math"
"math/big"
"testing"
Expand Down Expand Up @@ -233,50 +234,50 @@ func TestDeviatesOnGasCurve(t *testing.T) {
}{
{
name: "base case deviates from increase",
args: args{xNew: big.NewInt(4e14), xOld: big.NewInt(1e13), noDev: big.NewInt(3e13), ppb: EthereumThresholdGatePPB},
args: args{xNew: big.NewInt(4e14), xOld: big.NewInt(1e13), noDev: big.NewInt(3e13), ppb: CurveBasedDeviationPPB},
want: true,
},
{
name: "base case deviates from decrease",
args: args{xNew: big.NewInt(1e13), xOld: big.NewInt(4e15), noDev: big.NewInt(1), ppb: EthereumThresholdGatePPB},
args: args{xNew: big.NewInt(1e13), xOld: big.NewInt(4e15), noDev: big.NewInt(1), ppb: CurveBasedDeviationPPB},
want: true,
},
{
name: "does not deviate when equal",
args: args{xNew: big.NewInt(3e14), xOld: big.NewInt(3e14), noDev: big.NewInt(3e13), ppb: EthereumThresholdGatePPB},
args: args{xNew: big.NewInt(3e14), xOld: big.NewInt(3e14), noDev: big.NewInt(3e13), ppb: CurveBasedDeviationPPB},
want: false,
},
{
name: "does not deviate with small difference when xNew is bigger",
args: args{xNew: big.NewInt(3e14 + 1), xOld: big.NewInt(3e14), noDev: big.NewInt(3e13), ppb: EthereumThresholdGatePPB},
args: args{xNew: big.NewInt(3e14 + 1), xOld: big.NewInt(3e14), noDev: big.NewInt(3e13), ppb: CurveBasedDeviationPPB},
want: false,
},
{
name: "does not deviate with small difference when xOld is bigger",
args: args{xNew: big.NewInt(3e14), xOld: big.NewInt(3e14 + 1), noDev: big.NewInt(3e13), ppb: EthereumThresholdGatePPB},
args: args{xNew: big.NewInt(3e14), xOld: big.NewInt(3e14 + 1), noDev: big.NewInt(3e13), ppb: CurveBasedDeviationPPB},
want: false,
},
{
name: "deviates when ppb is not equal to EthereumThresholdGatePPB",
name: "deviates when ppb is not equal to CurveBasedDeviationPPB",
args: args{xNew: big.NewInt(1e9), xOld: big.NewInt(2e9), noDev: big.NewInt(1), ppb: 1},
want: true,
},
{
name: "does not deviate when xNew is below noDeviationLowerBound",
args: args{xNew: big.NewInt(2e13), xOld: big.NewInt(1e13), noDev: big.NewInt(3e13), ppb: EthereumThresholdGatePPB},
args: args{xNew: big.NewInt(2e13), xOld: big.NewInt(1e13), noDev: big.NewInt(3e13), ppb: CurveBasedDeviationPPB},
want: false,
},
// thresholdPPB = (10e11) / (xNew^0.665) * 1e7
// diff = (xNew - xOld) / min(xNew, xOld) * 1e9
// Deviates = abs(diff) > thresholdPPB
{
name: "xNew is just below deviation threshold and does deviate",
args: args{xNew: big.NewInt(3e13), xOld: big.NewInt(2.519478222838e12), noDev: big.NewInt(1), ppb: EthereumThresholdGatePPB},
args: args{xNew: big.NewInt(3e13), xOld: big.NewInt(2.519478222838e12), noDev: big.NewInt(1), ppb: CurveBasedDeviationPPB},
want: false,
},
{
name: "xNew is just above deviation threshold and does deviate",
args: args{xNew: big.NewInt(3e13), xOld: big.NewInt(2.519478222838e12 - 30), noDev: big.NewInt(1), ppb: EthereumThresholdGatePPB},
args: args{xNew: big.NewInt(3e13), xOld: big.NewInt(2.519478222838e12 - 30), noDev: big.NewInt(1), ppb: CurveBasedDeviationPPB},
want: true,
},
}
Expand All @@ -285,8 +286,75 @@ func TestDeviatesOnGasCurve(t *testing.T) {
assert.Equalf(
t,
tt.want,
DeviatesOnGasCurve(tt.args.xNew, tt.args.xOld, tt.args.noDev, tt.args.ppb),
"DeviatesOnGasCurve(%v, %v, %v, %v)", tt.args.xNew, tt.args.xOld, tt.args.noDev, tt.args.ppb)
DeviatesOnCurve(tt.args.xNew, tt.args.xOld, tt.args.noDev, tt.args.ppb),
"DeviatesOnCurve(%v, %v, %v, %v)", tt.args.xNew, tt.args.xOld, tt.args.noDev, tt.args.ppb)
})
}
}

func TestCalculateCurveThresholdPPB(t *testing.T) {
tests := []struct {
x float64
ppbLowerBound int64
ppbUpperBound int64
}{
{
x: 500_000,
ppbLowerBound: 1_000_000_000_000_000, // 100,000,000%
ppbUpperBound: 3_000_000_000_000_000, // 300,000,000%
},
{
x: 50_000_000,
ppbLowerBound: 70_000_000_000_000, // 7,000,000%
ppbUpperBound: 90_000_000_000_000, // 9,000,000%
},
{
x: 350_000_000,
ppbLowerBound: 20_000_000_000_000, // 2,000,000%
ppbUpperBound: 30_000_000_000_000, // 3,000,000%
},
{
x: 200_000_000_000,
ppbLowerBound: 300_000_000_000, // 30,000%
ppbUpperBound: 400_000_000_000, // 40,000%
},
{
x: 6_000_000_000_000,
ppbLowerBound: 30_000_000_000, // 3,000%
ppbUpperBound: 40000000000, // 4,000%
},
{
x: 50_000_000_000_000,
ppbLowerBound: 7_000_000_000, // 700%
ppbUpperBound: 8_000_000_000, // 800%
},
{
x: 225_000_000_000_000,
ppbLowerBound: 2_000_000_000, // 200%
ppbUpperBound: 3_000_000_000, // 300%
},
{
x: 5_000_000_000_000_000,
ppbLowerBound: 300_000_000, // 30%
ppbUpperBound: 400_000_000, // 40%
},
{
x: 70_000_000_000_000_000,
ppbLowerBound: 60_000_000, // 6%
ppbUpperBound: 70_000_000, // 7%
},
{
x: 500_000_000_000_000_000,
ppbLowerBound: 10_000_000, // 1%
ppbUpperBound: 20_000_000, // 2%
},
}
for _, tt := range tests {
// convert tt.x to string
t.Run(fmt.Sprintf("%f", tt.x), func(t *testing.T) {
thresholdPPB := calculateCurveThresholdPPB(tt.x)
assert.GreaterOrEqual(t, thresholdPPB, tt.ppbLowerBound)
assert.LessOrEqual(t, thresholdPPB, tt.ppbUpperBound)
})
}
}
6 changes: 1 addition & 5 deletions core/services/ocr2/plugins/ccip/prices/da_price_estimator.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,6 @@ import (
)

const (
// ExecNoDeviationThresholdUSD is the lower bound no deviation threshold for exec gas. If the exec gas price is
// less than this value, we should never trigger a deviation. This is set to 10 gwei in USD terms.
ExecNoDeviationThresholdUSD = 10e9

// DANoDeviationThresholdUSD is the lower bound no deviation threshold for DA gas. If the DA gas price is less
// than this value, we should never trigger a deviation. This is set to 20 gwei in USD terms.
DANoDeviationThresholdUSD = 20e9
Expand Down Expand Up @@ -145,7 +141,7 @@ func (g DAGasPriceEstimator) Deviates(ctx context.Context, p1, p2 *big.Int) (boo
return execDeviates, nil
}

return ccipcalc.DeviatesOnGasCurve(p1DAGasPrice, p2DAGasPrice, big.NewInt(DANoDeviationThresholdUSD), g.daDeviationPPB), nil
return ccipcalc.DeviatesOnCurve(p1DAGasPrice, p2DAGasPrice, big.NewInt(DANoDeviationThresholdUSD), g.daDeviationPPB), nil
}

func (g DAGasPriceEstimator) EstimateMsgCostUSD(ctx context.Context, p *big.Int, wrappedNativePrice *big.Int, msg cciptypes.EVM2EVMOnRampCCIPSendRequestedWithMeta) (*big.Int, error) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,12 @@ import (
"github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ccip/internal/ccipcalc"
)

const (
// ExecNoDeviationThresholdUSD is the lower bound no deviation threshold for exec gas. If the exec gas price is
// less than this value, we should never trigger a deviation. This is set to 10 gwei in USD terms.
ExecNoDeviationThresholdUSD = 10e9
)

type ExecGasPriceEstimator struct {
estimator gas.EvmFeeEstimator
maxGasPrice *big.Int
Expand Down Expand Up @@ -52,7 +58,7 @@ func (g ExecGasPriceEstimator) Median(ctx context.Context, gasPrices []*big.Int)
}

func (g ExecGasPriceEstimator) Deviates(ctx context.Context, p1 *big.Int, p2 *big.Int) (bool, error) {
return ccipcalc.DeviatesOnGasCurve(p1, p2, big.NewInt(ExecNoDeviationThresholdUSD), g.deviationPPB), nil
return ccipcalc.DeviatesOnCurve(p1, p2, big.NewInt(ExecNoDeviationThresholdUSD), g.deviationPPB), nil
}

func (g ExecGasPriceEstimator) EstimateMsgCostUSD(ctx context.Context, p *big.Int, wrappedNativePrice *big.Int, msg cciptypes.EVM2EVMOnRampCCIPSendRequestedWithMeta) (*big.Int, error) {
Expand Down

0 comments on commit b3b3a86

Please sign in to comment.