Skip to content

Commit

Permalink
Add tokenDestGasOverhead to CalculateMessageMaxGas
Browse files Browse the repository at this point in the history
Also adds a TODO to update the iface to return an error
  • Loading branch information
rstout committed Oct 25, 2024
1 parent ac6c949 commit 0353073
Show file tree
Hide file tree
Showing 3 changed files with 123 additions and 43 deletions.
20 changes: 17 additions & 3 deletions core/capabilities/ccip/ccipevm/gas_helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,18 +51,31 @@ func bytesForMsgTokens(numTokens int) int {
}

// CalculateMessageMaxGas computes the maximum gas overhead for a message.
// TODO: update interface to return error
func (gp EstimateProvider) CalculateMessageMaxGas(msg cciptypes.Message) uint64 {
numTokens := len(msg.TokenAmounts)
var data []byte = msg.Data
dataLength := len(data)

// TODO: update interface to return error?
// Although this decoding should never fail.
messageGasLimit, err := decodeExtraArgsV1V2(msg.ExtraArgs)
if err != nil {
panic(err)
}

var totalTokenDestGasOverhead uint64
for _, rampTokenAmount := range msg.TokenAmounts {
// If msg is an EVM message, its DestExecData field is an ABI encoded uint32, defined in FeeQuoter.sol here:
// https://github.com/smartcontractkit/ccip/blob/dee09c782c17de1e37e3a1cf625d430330532c6d/contracts/src/v0.8/ccip/FeeQuoter.sol#L958
//nolint:lll
// This is the gas overhead for the token transfer.
tokenDestGasOverhead, err := decodeTokenDestGasOverhead(rampTokenAmount.DestExecData)
if err != nil {
// TODO: metrics
panic(err)
}
totalTokenDestGasOverhead += uint64(tokenDestGasOverhead)
}

messageBytes := ConstantMessagePartBytes +
bytesForMsgTokens(numTokens) +
dataLength
Expand All @@ -85,5 +98,6 @@ func (gp EstimateProvider) CalculateMessageMaxGas(msg cciptypes.Message) uint64
SupportsInterfaceCheck +
adminRegistryOverhead +
rateLimiterOverhead +
PerTokenOverheadGas*uint64(numTokens)
PerTokenOverheadGas*uint64(numTokens) +
totalTokenDestGasOverhead
}
113 changes: 73 additions & 40 deletions core/capabilities/ccip/ccipevm/gas_helpers_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,10 @@ import (

func Test_calculateMessageMaxGas(t *testing.T) {
type args struct {
dataLen int
numTokens int
extraArgs []byte
dataLen int
numTokens int
extraArgs []byte
tokenGasOverhead uint32
}
tests := []struct {
name string
Expand All @@ -23,41 +24,56 @@ func Test_calculateMessageMaxGas(t *testing.T) {
}{
{
name: "base",
args: args{dataLen: 5, numTokens: 2, extraArgs: makeExtraArgsV1(200_000)},
want: 1_022_264,
args: args{dataLen: 5, numTokens: 2, extraArgs: makeExtraArgsV1(200_000), tokenGasOverhead: 10},
want: 1_022_284,
},
{
name: "large",
args: args{dataLen: 1000, numTokens: 1000, extraArgs: makeExtraArgsV1(200_000)},
want: 346_677_520,
args: args{dataLen: 1000, numTokens: 1000, extraArgs: makeExtraArgsV1(200_000), tokenGasOverhead: 1},
want: 346_678_520,
},
{
name: "overheadGas test 1",
args: args{dataLen: 0, numTokens: 0, extraArgs: makeExtraArgsV1(200_000)},
args: args{dataLen: 0, numTokens: 0, extraArgs: makeExtraArgsV1(200_000), tokenGasOverhead: 100},
want: 319_920,
},
{
name: "overheadGas test 2",
args: args{dataLen: len([]byte{0x0, 0x0, 0x0, 0x0, 0x0, 0x0}), numTokens: 1, extraArgs: makeExtraArgsV1(200_000)},
want: 675_948,
args: args{
dataLen: len([]byte{0x0, 0x0, 0x0, 0x0, 0x0, 0x0}),
numTokens: 1,
extraArgs: makeExtraArgsV1(200_000),
tokenGasOverhead: 2,
},
want: 675_950,
},
{
name: "allowOOO set to true makes no difference to final gas estimate",
args: args{dataLen: 5, numTokens: 2, extraArgs: makeExtraArgsV2(200_000, true)},
want: 1_022_264,
args: args{
dataLen: 5,
numTokens: 2,
extraArgs: makeExtraArgsV2(200_000, true),
tokenGasOverhead: 100,
},
want: 1_022_464,
},
{
name: "allowOOO set to false makes no difference to final gas estimate",
args: args{dataLen: 5, numTokens: 2, extraArgs: makeExtraArgsV2(200_000, false)},
want: 1_022_264,
args: args{
dataLen: 5,
numTokens: 2,
extraArgs: makeExtraArgsV2(200_000, false),
tokenGasOverhead: 100,
},
want: 1_022_464,
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
msg := ccipocr3.Message{
Data: make([]byte, tt.args.dataLen),
TokenAmounts: make([]ccipocr3.RampTokenAmount, tt.args.numTokens),
TokenAmounts: getTokenAmounts(t, tt.args.numTokens, tt.args.tokenGasOverhead),
ExtraArgs: tt.args.extraArgs,
}
ep := EstimateProvider{}
Expand All @@ -72,44 +88,48 @@ func Test_calculateMessageMaxGas(t *testing.T) {
// are combined to one function.
func TestCalculateMaxGas(t *testing.T) {
tests := []struct {
name string
numRequests int
dataLength int
numberOfTokens int
extraArgs []byte
want uint64
name string
numRequests int
dataLength int
numberOfTokens int
extraArgs []byte
tokenGasOverhead uint32
want uint64
}{
{
name: "maxGasOverheadGas 1",
numRequests: 6,
dataLength: 0,
numberOfTokens: 0,
extraArgs: makeExtraArgsV1(200_000),
want: 322_992,
name: "maxGasOverheadGas 1",
numRequests: 6,
dataLength: 0,
numberOfTokens: 0,
extraArgs: makeExtraArgsV1(200_000),
tokenGasOverhead: 10,
want: 322_992,
},
{
name: "maxGasOverheadGas 2",
numRequests: 3,
dataLength: len([]byte{0x0, 0x0, 0x0, 0x0, 0x0, 0x0}),
numberOfTokens: 1,
extraArgs: makeExtraArgsV1(200_000),
want: 678_508,
name: "maxGasOverheadGas 2",
numRequests: 3,
dataLength: len([]byte{0x0, 0x0, 0x0, 0x0, 0x0, 0x0}),
numberOfTokens: 1,
extraArgs: makeExtraArgsV1(200_000),
tokenGasOverhead: 10,
want: 678_518,
},
{
name: "v2 extra args",
numRequests: 3,
dataLength: len([]byte{0x0, 0x0, 0x0, 0x0, 0x0, 0x0}),
numberOfTokens: 1,
extraArgs: makeExtraArgsV2(200_000, true),
want: 678_508,
name: "v2 extra args",
numRequests: 3,
dataLength: len([]byte{0x0, 0x0, 0x0, 0x0, 0x0, 0x0}),
numberOfTokens: 1,
extraArgs: makeExtraArgsV2(200_000, true),
tokenGasOverhead: 10,
want: 678_518,
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
msg := ccipocr3.Message{
Data: make([]byte, tt.dataLength),
TokenAmounts: make([]ccipocr3.RampTokenAmount, tt.numberOfTokens),
TokenAmounts: getTokenAmounts(t, tt.numberOfTokens, tt.tokenGasOverhead),
ExtraArgs: tt.extraArgs,
}
ep := EstimateProvider{}
Expand Down Expand Up @@ -155,3 +175,16 @@ func makeExtraArgsV2(gasLimit uint64, allowOOO bool) []byte {
extraArgs = append(extraArgs, allowOOOBytes...)
return extraArgs
}

func getTokenAmounts(t *testing.T, numTokens int, tokenGasOverhead uint32) []ccipocr3.RampTokenAmount {
tokenDestGasOverhead, err := TokenDestGasOverheadABI.Pack(tokenGasOverhead)
assert.NoError(t, err)

tokenAmounts := make([]ccipocr3.RampTokenAmount, numTokens)
for i := 0; i < numTokens; i++ {
tokenAmounts[i] = ccipocr3.RampTokenAmount{
DestExecData: tokenDestGasOverhead,
}
}
return tokenAmounts
}
33 changes: 33 additions & 0 deletions core/capabilities/ccip/ccipevm/helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,15 @@ import (
"github.com/ethereum/go-ethereum/accounts/abi"
)

var (
abiUint32 = ABITypeOrPanic("uint32")
TokenDestGasOverheadABI = abi.Arguments{
{
Type: abiUint32,
},
}
)

func decodeExtraArgsV1V2(extraArgs []byte) (gasLimit *big.Int, err error) {
if len(extraArgs) < 4 {
return nil, fmt.Errorf("extra args too short: %d, should be at least 4 (i.e the extraArgs tag)", len(extraArgs))
Expand Down Expand Up @@ -43,3 +52,27 @@ func abiEncodeMethodInputs(abiDef abi.ABI, inputs ...interface{}) ([]byte, error
}
return packed[4:], nil // remove the method selector
}

func ABITypeOrPanic(t string) abi.Type {
abiType, err := abi.NewType(t, "", nil)
if err != nil {
panic(err)
}
return abiType
}

// Decodes the given bytes into a uint32, based on the encoding of destGasAmount in FeeQuoter.sol here:
// https://github.com/smartcontractkit/ccip/blob/dee09c782c17de1e37e3a1cf625d430330532c6d/contracts/src/v0.8/ccip/FeeQuoter.sol#L958
//
//nolint:lll
func decodeTokenDestGasOverhead(destExecData []byte) (uint32, error) {
ifaces, err := TokenDestGasOverheadABI.UnpackValues(destExecData)
if err != nil {
return 0, fmt.Errorf("abi decode TokenDestGasOverheadABI: %w", err)
}
_, ok := ifaces[0].(uint32)
if !ok {
return 0, fmt.Errorf("expected uint32, got %T", ifaces[0])
}
return ifaces[0].(uint32), nil
}

0 comments on commit 0353073

Please sign in to comment.