Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
946f753
address gobinding inconsistency
huangzhen1997 Feb 2, 2026
a5d1bb7
update ccip send executor
huangzhen1997 Feb 2, 2026
a0c563c
address comments
huangzhen1997 Feb 2, 2026
06bb728
Merge branch 'main' into jh/fix-bindings-incompatbilities
huangzhen1997 Feb 3, 2026
2e08c67
use uint32
huangzhen1997 Feb 3, 2026
ccfd578
Merge branch 'main' into jh/fix-bindings-incompatbilities
huangzhen1997 Feb 3, 2026
9288fb2
fix ts test
huangzhen1997 Feb 3, 2026
acb13df
fix uint32
huangzhen1997 Feb 3, 2026
01f5aaf
rm unneeded entry
huangzhen1997 Feb 3, 2026
692c1da
fix mod
huangzhen1997 Feb 3, 2026
0915be7
rm outdated comment
huangzhen1997 Feb 3, 2026
46e4e6e
refactor
huangzhen1997 Feb 3, 2026
76668ff
update type
huangzhen1997 Feb 3, 2026
3949377
update
huangzhen1997 Feb 3, 2026
ef49fb1
fix
huangzhen1997 Feb 3, 2026
0eb22b3
minor
huangzhen1997 Feb 4, 2026
cddbd91
Merge branch 'main' into jh/fix-bindings-incompatbilities
huangzhen1997 Feb 4, 2026
8f22724
Merge branch 'main' into jh/fix-bindings-incompatbilities
huangzhen1997 Feb 4, 2026
8bd05ea
mod tidy
huangzhen1997 Feb 4, 2026
cb75692
mod tidy
huangzhen1997 Feb 4, 2026
35403ee
Merge branch 'main' into jh/fix-bindings-incompatbilities
huangzhen1997 Feb 4, 2026
51d4d96
fix make
huangzhen1997 Feb 4, 2026
ebd64c8
update jetton_test.go
huangzhen1997 Feb 4, 2026
5e5b92a
Merge branch 'main' into jh/fix-bindings-incompatbilities
huangzhen1997 Feb 4, 2026
2724afa
fix make
huangzhen1997 Feb 4, 2026
9b0739c
Merge branch 'main' into jh/fix-bindings-incompatbilities
huangzhen1997 Feb 4, 2026
6099e1f
update comment
huangzhen1997 Feb 4, 2026
018738d
Merge branch 'main' into jh/fix-bindings-incompatbilities
huangzhen1997 Feb 9, 2026
c2ef0ca
Merge branch 'main' into jh/fix-bindings-incompatbilities
huangzhen1997 Feb 10, 2026
6c78684
Merge branch 'main' into jh/fix-bindings-incompatbilities
huangzhen1997 Feb 10, 2026
c2e5de8
Merge branch 'main' into jh/fix-bindings-incompatbilities
huangzhen1997 Feb 11, 2026
00c9629
fix test
huangzhen1997 Feb 11, 2026
59aa374
Merge branch 'main' into jh/fix-bindings-incompatbilities
huangzhen1997 Feb 11, 2026
e6eb97f
revert
huangzhen1997 Feb 11, 2026
2b3f681
Merge branch 'jh/fix-bindings-incompatbilities' of github.com:smartco…
huangzhen1997 Feb 11, 2026
eb90f3d
update
huangzhen1997 Feb 11, 2026
68ca520
add comment
huangzhen1997 Feb 11, 2026
deb3a42
fix
huangzhen1997 Feb 11, 2026
f338ac7
fix
huangzhen1997 Feb 11, 2026
03a7970
fix mcms types
huangzhen1997 Feb 11, 2026
bdf8980
Merge branch 'main' into jh/fix-bindings-incompatbilities
huangzhen1997 Feb 11, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 7 additions & 24 deletions pkg/ccip/bindings/ccipsendexecutor/ccipsendexecutor.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,14 @@ import (
"github.com/xssnick/tonutils-go/tlb"
"github.com/xssnick/tonutils-go/tvm/cell"

"github.com/smartcontractkit/chainlink-ton/pkg/ccip/bindings/feequoter"
"github.com/smartcontractkit/chainlink-ton/pkg/ccip/bindings/onramp"
"github.com/smartcontractkit/chainlink-ton/pkg/ccip/bindings/router"
"github.com/smartcontractkit/chainlink-ton/pkg/ton/tvm"
)

// CCIPSend Executor opcodes
const (
OpcodeCCIPSendExecutorExecute = 0xAF3C62B3 // crc32('CCIPSendExecutor_Execute')
OpcodeFeeQuoterMessageValidated = 0x1fa60374 // crc32('FeeQuoter_MessageValidated')
OpcodeFeeQuoterMessageValidationFailed = 0xbcf0ab0f // crc32('FeeQuoter_MessageValidationFailed')
OpcodeCCIPSendExecutorExecute = 0xAF3C62B3 // crc32('CCIPSendExecutor_Execute')
)

//go:generate go run golang.org/x/tools/cmd/stringer@v0.38.0 -type=ExitCode
Expand Down Expand Up @@ -48,28 +46,13 @@ type Execute struct {
Config *cell.Cell `tlb:"^"`
}

// FeeQuoter_MessageValidated message structure
type MessageValidated struct {
_ tlb.Magic `tlb:"#cbc4af76" json:"-"` //nolint:revive // Ignore opcode tag
Fee *tlb.Coins `tlb:"."`
Msg *router.CCIPSend `tlb:"^"`
Metadata *cell.Cell `tlb:"^"`
}

// FeeQuoter_MessageValidationFailed message structure
type MessageValidationFailed struct {
_ tlb.Magic `tlb:"#0f756150" json:"-"` //nolint:revive // Ignore opcode tag
Error *big.Int `tlb:"## 256"`
Msg *router.CCIPSend `tlb:"^"`
Context *cell.Cell `tlb:"^"`
}
// MessageValidated and MessageValidationFailed are reused from the feequoter package
// to ensure schema consistency with the on-chain FeeQuoter contract responses.

var TLBs = tvm.MustNewTLBMap([]any{
Execute{},
MessageValidated{},
MessageValidationFailed{},
// Note: We don't handle JettonTransferNotification or FeeQuoter_MessageValidated here
// because they are already handled by their respective decoders (jetton wallet and fee quoter)
feequoter.MessageValidated{},
feequoter.MessageValidationFailed{},
}).MustWithStorageType(InitialData{})

// Metadata structure
Expand Down Expand Up @@ -104,6 +87,6 @@ type StateOnGoingFeeValidation struct {

// TokenAmount structure (reused from router package concept)
Copy link

Copilot AI Feb 2, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The struct name 'TokenAmount' is confusing because it appears in multiple packages (router, ccipsendexecutor) with the same name but potentially different purposes. Consider adding a package-specific prefix (e.g., ExecutorTokenAmount) or documenting why this duplicates the router.TokenAmount type instead of reusing it.

Suggested change
// TokenAmount structure (reused from router package concept)
// TokenAmount represents an amount of a given token for the CCIP Send Executor.
// It intentionally mirrors the schema and naming of router.TokenAmount instead of
// reusing that type directly, to keep this bindings package self-contained and to
// match the on-chain / TLB schema for executor messages.

Copilot uses AI. Check for mistakes.
type TokenAmount struct {
Amount *big.Int `tlb:"## 256"`
Amount *tlb.Coins `tlb:"."`
Token *address.Address `tlb:"addr"`
}
69 changes: 45 additions & 24 deletions pkg/ccip/bindings/feequoter/fee_quoter.go
Original file line number Diff line number Diff line change
Expand Up @@ -84,19 +84,23 @@ const (
)

type Storage struct {
ID uint32 `tlb:"## 32"`
Ownable ownable2step.Storage `tlb:"."`
AllowedPriceUpdaters *cell.Dictionary `tlb:"dict 267"`
MaxFeeJuelsPerMsg *big.Int `tlb:"## 96"`
LinkToken *address.Address `tlb:"addr"`
TokenPriceStalenessThreshold uint64 `tlb:"## 64"`
UsdPerToken *cell.Dictionary `tlb:"dict 267"`
PremiumMultiplierWeiPerEth *cell.Dictionary `tlb:"dict 267"`
DestChainConfigs *cell.Dictionary `tlb:"dict 64"`
}

ID uint32 `tlb:"## 32"`
Ownable ownable2step.Storage `tlb:"."`
AllowedPriceUpdaters *cell.Dictionary `tlb:"dict 267"`
MaxFeeJuelsPerMsg *big.Int `tlb:"## 96"`
LinkToken *address.Address `tlb:"addr"`
// TODO: Consider changing to uint32 for EVM compatibility. Currently uint64 on-chain.
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@vicentevieytes why are we using uint64 on-chain for TokenPriceStalenessThreshold ?

TokenPriceStalenessThreshold uint64 `tlb:"## 64"`
UsdPerToken *cell.Dictionary `tlb:"dict 267"`
PremiumMultiplierWeiPerEth *cell.Dictionary `tlb:"dict 267"`
DestChainConfigs *cell.Dictionary `tlb:"dict 64"`
}

// DestChainConfigs represents the full on-chain DestChainConfig struct from the FeeQuoter contract.
// See contracts/contracts/ccip/fee_quoter/types.tolk for the on-chain definition.
// Note the naming: this Go type uses plural "Configs" to distinguish from DestChainConfig above.
type DestChainConfigs struct {
Config DestChainConfig `tlb:"."` // inline struct
Config DestChainConfig `tlb:"."` // inline struct (FeeQuoterDestChainConfig on-chain)
USDPerUnitGasRef *cell.Cell `tlb:"^"` // ^Cell<GasPrice>
TokenTransferFeeConfigs *cell.Dictionary `tlb:"dict 267"` // map<address, TokenTransferFeeConfig>
}
Expand All @@ -122,6 +126,12 @@ func (u *USDPerUnitGas) GetterMethodName() string {
return destinationChainGasPriceGetter
}

// DestChainConfig represents the FeeQuoterDestChainConfig fields from the on-chain FeeQuoter contract.
//
// NOTE: This Go type is named "DestChainConfig" but corresponds to the on-chain "FeeQuoterDestChainConfig"
// struct (see contracts/contracts/ccip/fee_quoter/types.tolk). The on-chain "DestChainConfig" is a larger
// struct that wraps FeeQuoterDestChainConfig along with usdPerUnitGas and tokenTransferFeeConfigs fields.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the comment - imo this is super confusing and the naming is not best we can do.

// The full on-chain struct is represented by DestChainConfigs (plural) in this package.
type DestChainConfig struct {
IsEnabled bool `tlb:"bool"`
MaxNumberOfTokensPerMsg uint16 `tlb:"## 16"`
Expand Down Expand Up @@ -205,30 +215,36 @@ type FeeToken struct {
// Methods

// Generic wrapper for fee quoter messages with context
// NOTE: Context is T=RemainingBitsAndRefs on-chain, meaning the remaining bits/refs
// are written inline with no presence bit and no ref cell.
type GetValidatedFee struct {
_ tlb.Magic `tlb:"#7496FF56" json:"-"` //nolint:revive // Ignore opcode tag
Msg *cell.Cell `tlb:"^"` // Cell containing the CCIPSend message
Context *cell.Cell `tlb:"maybe ^"` // Cell containing context
Context *cell.Cell `tlb:"."` // Remaining bits/refs written inline
}

// --- Response from GetValidatedFee ---
// NOTE: Context is T=RemainingBitsAndRefs on-chain, meaning the remaining bits/refs
// are written inline with no presence bit and no ref cell.
type MessageValidated struct {
_ tlb.Magic `tlb:"#1fa60374" json:"-"` //nolint:revive // Ignore opcode tag
Fee Fee `tlb:"."`
Msg *cell.Cell `tlb:"^"` // Original message
Context *cell.Cell `tlb:"maybe ^"` // Original context
Msg *cell.Cell `tlb:"^"` // Original message
Context *cell.Cell `tlb:"."` // Remaining bits/refs written inline
}

type Fee struct {
FeeTokenAmount *tlb.Coins `tlb:"."` // fee value in fee token
FeeValueJuels *big.Int `tlb:"## 96"` // fee value in juels
}

// NOTE: Context is T=RemainingBitsAndRefs on-chain, meaning the remaining bits/refs
// are written inline with no presence bit and no ref cell.
type MessageValidationFailed struct {
_ tlb.Magic `tlb:"#bcf0ab0f" json:"-"` //nolint:revive // Ignore opcode tag
ErrorCode *big.Int `tlb:"## 256"`
Msg *cell.Cell `tlb:"^"` // Original message,
Context *cell.Cell `tlb:"maybe ^"` // Original context
Msg *cell.Cell `tlb:"^"` // Original message
Context *cell.Cell `tlb:"."` // Remaining bits/refs written inline
}

type AddPriceUpdater struct {
Expand All @@ -254,13 +270,17 @@ type UpdateFeeTokens struct {
Remove common.SnakedCell[common.AddressWrap] `tlb:"^"`
}

// UpdateTokenTransferFeeConfig is a value type stored in a dictionary, NOT a message.
// It represents per-destination-chain token transfer fee config updates.
type UpdateTokenTransferFeeConfig struct {
_ tlb.Magic `tlb:"#B2826316" json:"-"` //nolint:revive // Ignore opcode tag
Add map[*address.Address]TokenTransferFeeConfig
Remove []*address.Address `tlb:"addr"`
Add *cell.Dictionary `tlb:"dict 267"` // map<address, TokenTransferFeeConfig>
Remove common.SnakedCell[common.AddressWrap] `tlb:"^"` // SnakedCell<address>
}

// UpdateTokenTransferFeeConfigs is the message type for updating token transfer fee configs.
type UpdateTokenTransferFeeConfigs struct {
_ tlb.Magic `tlb:"#B2826316" json:"-"` //nolint:revive // Ignore opcode tag
_ tlb.Magic `tlb:"#B2826316" json:"-"` //nolint:revive // Ignore opcode tag
Updates *cell.Dictionary `tlb:"dict 64"` // map<uint64, UpdateTokenTransferFeeConfig>
}

type UpdateDestChainConfig struct {
Expand Down Expand Up @@ -288,9 +308,10 @@ var TLBs = tvm.MustNewTLBMap([]any{
// binding types that supports FetchResult interface with rpc client

type StaticConfig struct {
MaxFeeJuelsPerMsg *big.Int
LinkToken *address.Address
StalenessThreshold uint32
MaxFeeJuelsPerMsg *big.Int
LinkToken *address.Address
// TODO: Consider changing to uint32 for EVM compatibility once on-chain is updated.
StalenessThreshold uint64
}

// Deprecated: Use GetStaticConfig getter instead.
Expand Down
72 changes: 62 additions & 10 deletions pkg/ccip/bindings/feequoter/gaspriceutils.go
Original file line number Diff line number Diff line change
@@ -1,33 +1,85 @@
package feequoter

import "math/big"
import (
"errors"
"math/big"
)

var (
// ErrNilGasPrice is returned when a nil gas price is passed to PackGasPrice.
ErrNilGasPrice = errors.New("gas price cannot be nil")
// ErrNegativeGasPrice is returned when a negative gas price is passed to PackGasPrice.
ErrNegativeGasPrice = errors.New("gas price cannot be negative")
// ErrGasPriceExceeds112Bits is returned when a gas price exceeds 112 bits.
ErrGasPriceExceeds112Bits = errors.New("gas price exceeds 112 bits")
// ErrPackedPriceExceeds224Bits is returned when a packed price exceeds 224 bits.
ErrPackedPriceExceeds224Bits = errors.New("packed price exceeds 224 bits")
// ErrNilPackedPrice is returned when a nil packed price is passed to UnpackGasPrice.
ErrNilPackedPrice = errors.New("packed price cannot be nil")
// ErrNegativePackedPrice is returned when a negative packed price is passed to UnpackGasPrice.
ErrNegativePackedPrice = errors.New("packed price cannot be negative")
)

// maxUint112 is 2^112 - 1, can also be used as a mask for lower 112 bits sets to 1
var maxUint112 = new(big.Int).Sub(new(big.Int).Lsh(big.NewInt(1), 112), big.NewInt(1))

// maxUint224 is 2^224 - 1
var maxUint224 = new(big.Int).Sub(new(big.Int).Lsh(big.NewInt(1), 224), big.NewInt(1))

// PackGasPrice packs execution and data availability gas prices into a single 224-bit value.
// The packed format is: (dataAvailabilityGasPrice << 112) | executionGasPrice
//
// This matches the CCIP commit plugin's FeeComponentsToPackedFee function:
// https://github.com/smartcontractkit/chainlink-ccip/blob/main/commit/chainfee/outcome.go
func PackGasPrice(executionGasPrice, dataAvailabilityGasPrice *big.Int) *big.Int {
//
// Returns an error if:
// - Either input is nil
// - Either input is negative
// - Either input exceeds 112 bits
func PackGasPrice(executionGasPrice, dataAvailabilityGasPrice *big.Int) (*big.Int, error) {
if executionGasPrice == nil || dataAvailabilityGasPrice == nil {
return nil, ErrNilGasPrice
}
if executionGasPrice.Sign() < 0 || dataAvailabilityGasPrice.Sign() < 0 {
return nil, ErrNegativeGasPrice
}
if executionGasPrice.Cmp(maxUint112) > 0 {
return nil, ErrGasPriceExceeds112Bits
}
if dataAvailabilityGasPrice.Cmp(maxUint112) > 0 {
return nil, ErrGasPriceExceeds112Bits
}

daShifted := new(big.Int).Lsh(dataAvailabilityGasPrice, 112)
return new(big.Int).Or(daShifted, executionGasPrice)
return new(big.Int).Or(daShifted, executionGasPrice), nil
}

// UnpackGasPrice unpacks a 224-bit packed gas price value into separate execution
// and data availability gas prices.
//
// The packed format is: (dataAvailabilityGasPrice << 112) | executionGasPrice
//
// Returns an error if:
// - The input is nil
// - The input is negative
// - The input exceeds 224 bits
//
// Returns:
// - executionGasPrice: the lower 112 bits
// - dataAvailabilityGasPrice: the upper 112 bits
func UnpackGasPrice(packedPrice *big.Int) (executionGasPrice, dataAvailabilityGasPrice *big.Int) {
ones112 := big.NewInt(0)
for i := 0; i < 112; i++ {
ones112 = ones112.SetBit(ones112, i, 1)
// - dataAvailabilityGasPrice: the upper 112 bits (bits 112-223)
func UnpackGasPrice(packedPrice *big.Int) (executionGasPrice, dataAvailabilityGasPrice *big.Int, err error) {
if packedPrice == nil {
return nil, nil, ErrNilPackedPrice
}
if packedPrice.Sign() < 0 {
return nil, nil, ErrNegativePackedPrice
}
if packedPrice.Cmp(maxUint224) > 0 {
return nil, nil, ErrPackedPriceExceeds224Bits
}

executionGasPrice = new(big.Int).And(packedPrice, ones112)
executionGasPrice = new(big.Int).And(packedPrice, maxUint112)
dataAvailabilityGasPrice = new(big.Int).Rsh(packedPrice, 112)

return executionGasPrice, dataAvailabilityGasPrice
return executionGasPrice, dataAvailabilityGasPrice, nil
}
Loading
Loading