Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
3 changes: 2 additions & 1 deletion bindings/bind/compile.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package bind

import (
"context"
"embed"
"encoding/base64"
"encoding/hex"
Expand Down Expand Up @@ -1094,7 +1095,7 @@ func getDynamicSuiRPC() (string, error) {
return envRPC, nil
}

cmd := exec.Command("docker", "ps", "--filter", "ancestor=mysten/sui-tools:mainnet-v1.72.5", "--format", "{{.Ports}}")
cmd := exec.CommandContext(context.Background(), "docker", "ps", "--filter", "ancestor=mysten/sui-tools:mainnet-v1.73.2", "--format", "{{.Ports}}")
out, err := cmd.Output()
if err != nil {
return "", fmt.Errorf("docker ps failed: %w", err)
Expand Down
2 changes: 1 addition & 1 deletion deployment/adapters/mcmsreader.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ type MCMSReader struct{}

// mcmsFieldsFromInput loads the on-chain state and selects the correct
// MCMSStateFields (normal or fastcurse) based on the Qualifier in the input.
// A Qualifier value of "fastcurse" selects the fastcurse MCMS instance.
// A Qualifier value of "RMNMCMS" (fastCurseQualifier) selects the fastcurse MCMS instance.
func mcmsFieldsFromInput(e cldf.Environment, chainSelector uint64, input mcms_utils.Input) (suideploy.MCMSStateFields, error) {
stateMap, err := suideploy.LoadOnchainStatesui(e)
if err != nil {
Expand Down
10 changes: 5 additions & 5 deletions deployment/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,10 @@ require (
github.com/ethereum/go-ethereum v1.17.3
github.com/google/go-cmp v0.7.0
github.com/smartcontractkit/chain-selectors v1.0.102
github.com/smartcontractkit/chainlink-ccip/deployment v0.0.0-20260625091148-e5618f5682ee
github.com/smartcontractkit/chainlink-ccip/deployment v0.0.0-20260624154507-ea7ff77a0ddb
github.com/smartcontractkit/chainlink-common v0.11.2-0.20260506120607-7f10be016c89
github.com/smartcontractkit/chainlink-deployments-framework v0.109.1-0.20260604174622-e26b8cddfa0a
github.com/smartcontractkit/chainlink-sui v0.0.0-20260527160341-aa3adc0abf67
github.com/smartcontractkit/chainlink-sui v0.0.0
github.com/smartcontractkit/mcms v0.45.2-0.20260604181544-da0bd7da623d
github.com/stretchr/testify v1.11.1
golang.org/x/crypto v0.52.0
Expand Down Expand Up @@ -131,9 +131,9 @@ require (
github.com/shopspring/decimal v1.4.0 // indirect
github.com/sigurn/crc16 v0.0.0-20211026045750-20ab5afb07e3 // indirect
github.com/smartcontractkit/chainlink-aptos v0.0.0-20260428085939-5c70de12dbfc // indirect
github.com/smartcontractkit/chainlink-ccip v0.1.1-solana.0.20260625091148-e5618f5682ee // indirect
github.com/smartcontractkit/chainlink-ccip/chains/solana v0.0.0-20260129103204-4c8453dd8139 // indirect
github.com/smartcontractkit/chainlink-ccip/chains/solana/gobindings v0.0.0-20260129103204-4c8453dd8139 // indirect
github.com/smartcontractkit/chainlink-ccip v0.1.1-solana.0.20260624154507-ea7ff77a0ddb // indirect
github.com/smartcontractkit/chainlink-ccip/chains/solana v0.0.0-20260624154507-ea7ff77a0ddb // indirect
github.com/smartcontractkit/chainlink-ccip/chains/solana/gobindings v0.0.0-20260624154507-ea7ff77a0ddb // indirect
github.com/smartcontractkit/chainlink-common/pkg/chipingress v0.0.10 // indirect
github.com/smartcontractkit/chainlink-protos/cre/go v0.0.0-20260505131349-78e491b80735 // indirect
github.com/smartcontractkit/chainlink-protos/job-distributor v0.18.0 // indirect
Expand Down
16 changes: 8 additions & 8 deletions deployment/go.sum

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

86 changes: 86 additions & 0 deletions deployment/lanes/adapter.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
package lanes

import (
"encoding/binary"
"math/big"

"github.com/smartcontractkit/chainlink-deployments-framework/datastore"

laneapi "github.com/smartcontractkit/chainlink-ccip/deployment/lanes"
)

// suiFamilySelector is bytes4(keccak256("CCIP ChainFamilySelector Sui")) = 0xc4e05953.
var suiFamilySelector = [4]byte{0xc4, 0xe0, 0x59, 0x53}

// SuiAdapter implements laneapi.LaneAdapter for Sui CCIP 1.6.0 lanes.
// Package IDs are resolved from addresses.json via LoadOnchainStatesui; the ds
// parameter is unused. ConnectChains must run inside WithConnectChainsEnvironment.
type SuiAdapter struct{}

func (a *SuiAdapter) GetOnRampAddress(_ datastore.DataStore, chainSelector uint64) ([]byte, error) {
e, err := connectChainsEnvironment()
if err != nil {
return nil, err
}
return resolveOnRampPackageID(e, chainSelector)
}

func (a *SuiAdapter) GetOffRampAddress(_ datastore.DataStore, chainSelector uint64) ([]byte, error) {
e, err := connectChainsEnvironment()
if err != nil {
return nil, err
}
return resolveOffRampPackageID(e, chainSelector)
}

func (a *SuiAdapter) GetFQAddress(_ datastore.DataStore, chainSelector uint64) ([]byte, error) {
e, err := connectChainsEnvironment()
if err != nil {
return nil, err
}
return resolveCCIPPackageID(e, chainSelector)
}

func (a *SuiAdapter) GetRouterAddress(_ datastore.DataStore, chainSelector uint64) ([]byte, error) {
e, err := connectChainsEnvironment()
if err != nil {
return nil, err
}
return resolveRouterPackageID(e, chainSelector)
}

// GetFeeQuoterDestChainConfig returns defaults applied on remote FeeQuoters when Sui is
// the destination chain. Values align with wired Sui testnet lanes (evm_feequoter_dest_configure)
// and Sui execution limits (MaxDataBytes 16_000).
func (a *SuiAdapter) GetFeeQuoterDestChainConfig() laneapi.FeeQuoterDestChainConfig {
return laneapi.FeeQuoterDestChainConfig{
IsEnabled: true,
MaxDataBytes: 16_000,
MaxPerMsgGasLimit: 3_000_000,
DestGasOverhead: 300_000,
DestGasPerPayloadByteBase: 16,
ChainFamilySelector: binary.BigEndian.Uint32(suiFamilySelector[:]),
DefaultTokenFeeUSDCents: 25,
DefaultTokenDestGasOverhead: 90_000,
DefaultTxGasLimit: 200_000,
NetworkFeeUSDCents: 10,
V1Params: &laneapi.FeeQuoterV1Params{
MaxNumberOfTokensPerMsg: 10,
DestGasPerPayloadByteHigh: 40,
DestGasPerPayloadByteThreshold: 3_000,
DestDataAvailabilityOverheadGas: 100,
DestGasPerDataAvailabilityByte: 16,
DestDataAvailabilityMultiplierBps: 1,
EnforceOutOfOrder: true,
GasMultiplierWeiPerEth: 11e17,
},
}
}

func (a *SuiAdapter) GetDefaultGasPrice() *big.Int {
return big.NewInt(15e11)
}

func (a *SuiAdapter) GetChainFamilySelector() [4]byte {
return suiFamilySelector
}
97 changes: 97 additions & 0 deletions deployment/lanes/adapter_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
package lanes_test

import (
"encoding/binary"
"math/big"
"testing"

"github.com/stretchr/testify/require"

laneapi "github.com/smartcontractkit/chainlink-ccip/deployment/lanes"

"github.com/smartcontractkit/chainlink-sui/deployment/lanes"
)

func TestSuiAdapter_GetFeeQuoterDestChainConfig(t *testing.T) {
t.Parallel()

cfg := (&lanes.SuiAdapter{}).GetFeeQuoterDestChainConfig()

require.True(t, cfg.IsEnabled)
require.Equal(t, uint32(16_000), cfg.MaxDataBytes)
require.Equal(t, uint32(3_000_000), cfg.MaxPerMsgGasLimit)
require.Equal(t, uint32(300_000), cfg.DestGasOverhead)
require.Equal(t, uint8(16), cfg.DestGasPerPayloadByteBase)
require.Equal(t, uint32(0xc4e05953), cfg.ChainFamilySelector)
require.Equal(t, uint16(25), cfg.DefaultTokenFeeUSDCents)
require.Equal(t, uint32(90_000), cfg.DefaultTokenDestGasOverhead)
require.Equal(t, uint32(200_000), cfg.DefaultTxGasLimit)
require.Equal(t, uint16(10), cfg.NetworkFeeUSDCents)
require.NotNil(t, cfg.V1Params)
require.Equal(t, uint16(10), cfg.V1Params.MaxNumberOfTokensPerMsg)
require.True(t, cfg.V1Params.EnforceOutOfOrder)
require.Equal(t, uint64(11e17), cfg.V1Params.GasMultiplierWeiPerEth)
}

func TestSuiAdapter_GetDefaultGasPrice(t *testing.T) {
t.Parallel()

require.Equal(t, big.NewInt(15e11), (&lanes.SuiAdapter{}).GetDefaultGasPrice())
}

func TestSuiAdapter_GetChainFamilySelector(t *testing.T) {
t.Parallel()

selector := (&lanes.SuiAdapter{}).GetChainFamilySelector()
require.Equal(t, [4]byte{0xc4, 0xe0, 0x59, 0x53}, selector)
require.Equal(t, uint32(0xc4e05953), binary.BigEndian.Uint32(selector[:]))
}

func TestTranslateDestChainConfig(t *testing.T) {
t.Parallel()

const destSel = uint64(945045181441419236)
cfg := laneapi.FeeQuoterDestChainConfig{
IsEnabled: true,
MaxDataBytes: 30_000,
MaxPerMsgGasLimit: 3_000_000,
DestGasOverhead: 300_000,
DestGasPerPayloadByteBase: 16,
ChainFamilySelector: 0x2812d52c,
DefaultTokenFeeUSDCents: 25,
DefaultTokenDestGasOverhead: 90_000,
DefaultTxGasLimit: 200_000,
NetworkFeeUSDCents: 10,
V1Params: &laneapi.FeeQuoterV1Params{
MaxNumberOfTokensPerMsg: 1,
DestGasPerPayloadByteHigh: 40,
DestGasPerPayloadByteThreshold: 3_000,
DestDataAvailabilityOverheadGas: 100,
DestGasPerDataAvailabilityByte: 16,
DestDataAvailabilityMultiplierBps: 1,
EnforceOutOfOrder: true,
GasMultiplierWeiPerEth: 1e18,
GasPriceStalenessThreshold: 1_000_000,
},
}

got := lanes.TranslateDestChainConfig(cfg, destSel)

require.Equal(t, destSel, got.DestChainSelector)
require.True(t, got.IsEnabled)
require.Equal(t, uint16(1), got.MaxNumberOfTokensPerMsg)
require.Equal(t, uint32(30_000), got.MaxDataBytes)
require.Equal(t, byte(16), got.DestGasPerPayloadByteBase)
require.Equal(t, []byte{0x28, 0x12, 0xd5, 0x2c}, got.ChainFamilySelector)
require.Equal(t, uint64(1e18), got.GasMultiplierWeiPerEth)
require.Equal(t, uint32(10), got.NetworkFeeUsdCents)
}

func TestDefaultLinkTokenTransferFees(t *testing.T) {
t.Parallel()

require.Equal(t, uint32(3000), lanes.DefaultLinkTokenTransferMinFeeUsdCents)
require.Equal(t, uint32(30000), lanes.DefaultLinkTokenTransferMaxFeeUsdCents)
require.Equal(t, uint16(1000), lanes.DefaultLinkTokenTransferDeciBps)
require.Equal(t, uint64(900_000_000_000_000_000), lanes.DefaultLinkPremiumMultiplierWeiPerEth)
}
91 changes: 91 additions & 0 deletions deployment/lanes/addresses.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
package lanes

import (
"encoding/hex"
"fmt"

cldf "github.com/smartcontractkit/chainlink-deployments-framework/deployment"

suideploy "github.com/smartcontractkit/chainlink-sui/deployment"
)

// packageIDToBytes encodes a Sui package ID as a 32-byte, left-padded address for
// cross-chain CCIP references (same convention as EVM offramp source onramp config).
func packageIDToBytes(address string) ([]byte, error) {
out, err := suideploy.StrTo32(address)
if err != nil {
return nil, fmt.Errorf("invalid Sui package id %q: %w", address, err)
}
return out, nil
}

// remoteAddressBytesToHex encodes opaque remote address bytes from ConnectChains as a
// Sui address string. Bytes are left-padded to 32 bytes when shorter, matching EVM
// OffRamp source OnRamp encoding and deployment.StrTo32.
func remoteAddressBytesToHex(b []byte) (string, error) {
if len(b) == 0 {
return "", fmt.Errorf("empty address bytes")
}
if len(b) > 32 {
return "", fmt.Errorf("address longer than 32 bytes: %d", len(b))
}
padded := make([]byte, 32)
copy(padded[32-len(b):], b)
return "0x" + hex.EncodeToString(padded), nil
}

func loadChainState(env cldf.Environment, chainSelector uint64) (suideploy.CCIPChainState, error) {
stateMap, err := suideploy.LoadOnchainStatesui(env)
if err != nil {
return suideploy.CCIPChainState{}, fmt.Errorf("load sui onchain state: %w", err)
}
state, ok := stateMap[chainSelector]
if !ok {
return suideploy.CCIPChainState{}, fmt.Errorf("sui chain %d not found in address book state", chainSelector)
}
return state, nil
}

func resolveOnRampPackageID(env cldf.Environment, chainSelector uint64) ([]byte, error) {
state, err := loadChainState(env, chainSelector)
if err != nil {
return nil, err
}
if state.OnRampAddress == "" {
return nil, fmt.Errorf("no SuiOnRamp package for chain %d in address book", chainSelector)
}
return packageIDToBytes(state.OnRampAddress)
}

func resolveOffRampPackageID(env cldf.Environment, chainSelector uint64) ([]byte, error) {
state, err := loadChainState(env, chainSelector)
if err != nil {
return nil, err
}
if state.OffRampAddress == "" {
return nil, fmt.Errorf("no SuiOffRamp package for chain %d in address book", chainSelector)
}
return packageIDToBytes(state.OffRampAddress)
}

func resolveCCIPPackageID(env cldf.Environment, chainSelector uint64) ([]byte, error) {
state, err := loadChainState(env, chainSelector)
if err != nil {
return nil, err
}
if state.CCIPAddress == "" {
return nil, fmt.Errorf("no SuiCCIP package for chain %d in address book", chainSelector)
}
return packageIDToBytes(state.CCIPAddress)
}

func resolveRouterPackageID(env cldf.Environment, chainSelector uint64) ([]byte, error) {
state, err := loadChainState(env, chainSelector)
if err != nil {
return nil, err
}
if state.CCIPRouterAddress == "" {
return nil, fmt.Errorf("no SuiRouter package for chain %d in address book", chainSelector)
}
return packageIDToBytes(state.CCIPRouterAddress)
}
Loading
Loading