Skip to content

Commit

Permalink
Merge branch 'develop' into NONEVM-1065/events-for-standard-and-batch…
Browse files Browse the repository at this point in the history
…-reads
  • Loading branch information
reductionista authored Feb 7, 2025
2 parents 25c59c9 + 005a15b commit 9752635
Show file tree
Hide file tree
Showing 15 changed files with 118 additions and 58 deletions.
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ require (
github.com/pelletier/go-toml/v2 v2.2.3
github.com/prometheus/client_golang v1.20.5
github.com/smartcontractkit/chainlink-ccip v0.0.0-20250203132120-f0d42463e405
github.com/smartcontractkit/chainlink-ccip/chains/solana v0.0.0-20250128162345-af4c8fd4481a
github.com/smartcontractkit/chainlink-ccip/chains/solana v0.0.0-20250206215114-fb6c3c35e8e3
github.com/smartcontractkit/chainlink-common v0.4.2-0.20250205141137-8f50d72601bb
github.com/smartcontractkit/chainlink-framework/multinode v0.0.0-20250205165125-271e20f6de0a
github.com/smartcontractkit/libocr v0.0.0-20241223215956-e5b78d8e3919
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -578,8 +578,8 @@ github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
github.com/smartcontractkit/chainlink-ccip v0.0.0-20250203132120-f0d42463e405 h1:5QyaPGLmt+rlnvQL7drAE23Wq9rX5hO35kTZirAb97A=
github.com/smartcontractkit/chainlink-ccip v0.0.0-20250203132120-f0d42463e405/go.mod h1:UEnHaxkUsfreeA7rR45LMmua1Uen95tOFUR8/AI9BAo=
github.com/smartcontractkit/chainlink-ccip/chains/solana v0.0.0-20250128162345-af4c8fd4481a h1:1MrD2OiP/CRfyBSwTQE66R1+gLWBgWcU/SYl/+DmZ/Y=
github.com/smartcontractkit/chainlink-ccip/chains/solana v0.0.0-20250128162345-af4c8fd4481a/go.mod h1:Bmwq4lNb5tE47sydN0TKetcLEGbgl+VxHEWp4S0LI60=
github.com/smartcontractkit/chainlink-ccip/chains/solana v0.0.0-20250206215114-fb6c3c35e8e3 h1:f4F/7OCuMybsPKKXXvLQz+Q1hGq07I1cfoWy5EA9iRg=
github.com/smartcontractkit/chainlink-ccip/chains/solana v0.0.0-20250206215114-fb6c3c35e8e3/go.mod h1:Bmwq4lNb5tE47sydN0TKetcLEGbgl+VxHEWp4S0LI60=
github.com/smartcontractkit/chainlink-common v0.4.2-0.20250205141137-8f50d72601bb h1:1VC/hN1ojPiEWCsjxhvcw4p1Zveo90O38VQhktvo3Ag=
github.com/smartcontractkit/chainlink-common v0.4.2-0.20250205141137-8f50d72601bb/go.mod h1:Z2e1ynSJ4pg83b4Qldbmryc5lmnrI3ojOdg1FUloa68=
github.com/smartcontractkit/chainlink-framework/multinode v0.0.0-20250205165125-271e20f6de0a h1:ZG8v7aQxyp9cOYXpW6oodL+OWgwDku544qyzXPPgs7M=
Expand Down
2 changes: 1 addition & 1 deletion integration-tests/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -343,7 +343,7 @@ require (
github.com/smartcontractkit/chain-selectors v1.0.37 // indirect
github.com/smartcontractkit/chainlink-automation v0.8.1 // indirect
github.com/smartcontractkit/chainlink-ccip v0.0.0-20250203132120-f0d42463e405 // indirect
github.com/smartcontractkit/chainlink-ccip/chains/solana v0.0.0-20250128162345-af4c8fd4481a // indirect
github.com/smartcontractkit/chainlink-ccip/chains/solana v0.0.0-20250206215114-fb6c3c35e8e3 // indirect
github.com/smartcontractkit/chainlink-cosmos v0.5.2-0.20250130125138-3df261e09ddc // indirect
github.com/smartcontractkit/chainlink-data-streams v0.1.1-0.20250128203428-08031923fbe5 // indirect
github.com/smartcontractkit/chainlink-feeds v0.1.1 // indirect
Expand Down
4 changes: 2 additions & 2 deletions integration-tests/go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -1229,8 +1229,8 @@ github.com/smartcontractkit/chainlink-automation v0.8.1 h1:sTc9LKpBvcKPc1JDYAmgB
github.com/smartcontractkit/chainlink-automation v0.8.1/go.mod h1:Iij36PvWZ6blrdC5A/nrQUBuf3MH3JvsBB9sSyc9W08=
github.com/smartcontractkit/chainlink-ccip v0.0.0-20250203132120-f0d42463e405 h1:5QyaPGLmt+rlnvQL7drAE23Wq9rX5hO35kTZirAb97A=
github.com/smartcontractkit/chainlink-ccip v0.0.0-20250203132120-f0d42463e405/go.mod h1:UEnHaxkUsfreeA7rR45LMmua1Uen95tOFUR8/AI9BAo=
github.com/smartcontractkit/chainlink-ccip/chains/solana v0.0.0-20250128162345-af4c8fd4481a h1:1MrD2OiP/CRfyBSwTQE66R1+gLWBgWcU/SYl/+DmZ/Y=
github.com/smartcontractkit/chainlink-ccip/chains/solana v0.0.0-20250128162345-af4c8fd4481a/go.mod h1:Bmwq4lNb5tE47sydN0TKetcLEGbgl+VxHEWp4S0LI60=
github.com/smartcontractkit/chainlink-ccip/chains/solana v0.0.0-20250206215114-fb6c3c35e8e3 h1:f4F/7OCuMybsPKKXXvLQz+Q1hGq07I1cfoWy5EA9iRg=
github.com/smartcontractkit/chainlink-ccip/chains/solana v0.0.0-20250206215114-fb6c3c35e8e3/go.mod h1:Bmwq4lNb5tE47sydN0TKetcLEGbgl+VxHEWp4S0LI60=
github.com/smartcontractkit/chainlink-common v0.4.2-0.20250205141137-8f50d72601bb h1:1VC/hN1ojPiEWCsjxhvcw4p1Zveo90O38VQhktvo3Ag=
github.com/smartcontractkit/chainlink-common v0.4.2-0.20250205141137-8f50d72601bb/go.mod h1:Z2e1ynSJ4pg83b4Qldbmryc5lmnrI3ojOdg1FUloa68=
github.com/smartcontractkit/chainlink-cosmos v0.5.2-0.20250130125138-3df261e09ddc h1:WZERXv2hTYRA0NpWg79ci/ZZSxucmvkty39iUOV8d7I=
Expand Down
6 changes: 3 additions & 3 deletions pkg/solana/chainreader/account_read_binding.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,10 @@ type accountReadBinding struct {
codec types.RemoteCodec
key solana.PublicKey
isPda bool // flag to signify whether or not the account read is for a PDA
prefix string // only used for PDA public key calculation
prefix []byte // only used for PDA public key calculation
}

func newAccountReadBinding(namespace, genericName, prefix string, isPda bool) *accountReadBinding {
func newAccountReadBinding(namespace, genericName string, prefix []byte, isPda bool) *accountReadBinding {
return &accountReadBinding{
namespace: namespace,
genericName: genericName,
Expand Down Expand Up @@ -71,7 +71,7 @@ func (b *accountReadBinding) Decode(ctx context.Context, bts []byte, outVal any)
func (b *accountReadBinding) buildSeedsSlice(ctx context.Context, params any) ([][]byte, error) {
flattenedSeeds := make([]byte, 0, solana.MaxSeeds*solana.MaxSeedLength)
// Append the static prefix string first
flattenedSeeds = append(flattenedSeeds, []byte(b.prefix)...)
flattenedSeeds = append(flattenedSeeds, b.prefix...)
// Encode the seeds provided in the params
encodedParamSeeds, err := b.codec.Encode(ctx, params, codec.WrapItemType(true, b.namespace, b.genericName))
if err != nil {
Expand Down
25 changes: 4 additions & 21 deletions pkg/solana/chainreader/chain_reader.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import (
"github.com/smartcontractkit/chainlink-solana/pkg/solana/codec"
"github.com/smartcontractkit/chainlink-solana/pkg/solana/config"
"github.com/smartcontractkit/chainlink-solana/pkg/solana/logpoller"
"github.com/smartcontractkit/chainlink-solana/pkg/solana/utils"
)

type EventsReader interface {
Expand Down Expand Up @@ -316,7 +317,7 @@ func (s *ContractReaderService) addCodecDef(forEncoding bool, namespace, generic
func (s *ContractReaderService) init(namespaces map[string]config.ChainContractReader) error {
for namespace, nameSpaceDef := range namespaces {
for genericName, read := range nameSpaceDef.Reads {
injectAddressModifier(read.InputModifications, read.OutputModifications)
utils.InjectAddressModifier(read.InputModifications, read.OutputModifications)

switch read.ReadType {
case config.Account:
Expand Down Expand Up @@ -374,12 +375,12 @@ func (s *ContractReaderService) addAccountRead(namespace string, genericName str
)

// Create PDA read binding if PDA prefix or seeds configs are populated
if len(readDefinition.PDADefiniton.Prefix) > 0 || len(readDefinition.PDADefiniton.Seeds) > 0 {
if readDefinition.PDADefiniton.Prefix != nil || len(readDefinition.PDADefiniton.Seeds) > 0 {
inputAccountIDLDef = readDefinition.PDADefiniton
reader = newAccountReadBinding(namespace, genericName, readDefinition.PDADefiniton.Prefix, true)
} else {
inputAccountIDLDef = codec.NilIdlTypeDefTy
reader = newAccountReadBinding(namespace, genericName, "", false)
reader = newAccountReadBinding(namespace, genericName, nil, false)
}
if err := s.addCodecDef(true, namespace, genericName, idl, inputAccountIDLDef, readDefinition.InputModifications); err != nil {
return err
Expand Down Expand Up @@ -420,24 +421,6 @@ func (s *ContractReaderService) addEventRead(
return nil
}

// injectAddressModifier injects AddressModifier into OutputModifications.
// This is necessary because AddressModifier cannot be serialized and must be applied at runtime.
func injectAddressModifier(inputModifications, outputModifications commoncodec.ModifiersConfig) {
for i, modConfig := range inputModifications {
if addrModifierConfig, ok := modConfig.(*commoncodec.AddressBytesToStringModifierConfig); ok {
addrModifierConfig.Modifier = codec.SolanaAddressModifier{}
outputModifications[i] = addrModifierConfig
}
}

for i, modConfig := range outputModifications {
if addrModifierConfig, ok := modConfig.(*commoncodec.AddressBytesToStringModifierConfig); ok {
addrModifierConfig.Modifier = codec.SolanaAddressModifier{}
outputModifications[i] = addrModifierConfig
}
}
}

type accountDataReader struct {
client *rpc.Client
}
Expand Down
24 changes: 12 additions & 12 deletions pkg/solana/chainreader/chain_reader_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -274,7 +274,7 @@ func TestSolanaChainReaderService_GetLatestValue(t *testing.T) {
programID := solana.NewWallet().PublicKey()
pubKey := solana.NewWallet().PublicKey()
uint64Seed := uint64(5)
prefixString := "Prefix"
prefixBytes := []byte("Prefix")

readDef := config.ReadDefinition{
ChainSpecificName: testutils.TestStructWithNestedStruct,
Expand All @@ -294,7 +294,7 @@ func TestSolanaChainReaderService_GetLatestValue(t *testing.T) {
{
name: "happy path",
pdaDefinition: codec.PDATypeDef{
Prefix: prefixString,
Prefix: prefixBytes,
Seeds: []codec.PDASeed{
{
Name: "PubKey",
Expand All @@ -306,7 +306,7 @@ func TestSolanaChainReaderService_GetLatestValue(t *testing.T) {
},
},
},
expected: mustFindProgramAddress(t, programID, [][]byte{[]byte(prefixString), pubKey.Bytes(), go_binary.LittleEndian.AppendUint64([]byte{}, uint64Seed)}),
expected: mustFindProgramAddress(t, programID, [][]byte{prefixBytes, pubKey.Bytes(), go_binary.LittleEndian.AppendUint64([]byte{}, uint64Seed)}),
params: map[string]any{
"PubKey": pubKey,
"Uint64Seed": uint64Seed,
Expand All @@ -315,7 +315,7 @@ func TestSolanaChainReaderService_GetLatestValue(t *testing.T) {
{
name: "with modifier and random field",
pdaDefinition: codec.PDATypeDef{
Prefix: prefixString,
Prefix: prefixBytes,
Seeds: []codec.PDASeed{
{
Name: "PubKey",
Expand All @@ -330,7 +330,7 @@ func TestSolanaChainReaderService_GetLatestValue(t *testing.T) {
inputModifier: codeccommon.ModifiersConfig{
&codeccommon.RenameModifierConfig{Fields: map[string]string{"PubKey": "PublicKey"}},
},
expected: mustFindProgramAddress(t, programID, [][]byte{[]byte(prefixString), pubKey.Bytes(), go_binary.LittleEndian.AppendUint64([]byte{}, uint64Seed)}),
expected: mustFindProgramAddress(t, programID, [][]byte{prefixBytes, pubKey.Bytes(), go_binary.LittleEndian.AppendUint64([]byte{}, uint64Seed)}),
params: map[string]any{
"PublicKey": pubKey,
"randomField": "randomValue", // unused field should be ignored by the codec
Expand All @@ -340,15 +340,15 @@ func TestSolanaChainReaderService_GetLatestValue(t *testing.T) {
{
name: "only prefix",
pdaDefinition: codec.PDATypeDef{
Prefix: prefixString,
Prefix: prefixBytes,
},
expected: mustFindProgramAddress(t, programID, [][]byte{[]byte(prefixString)}),
expected: mustFindProgramAddress(t, programID, [][]byte{prefixBytes}),
params: nil,
},
{
name: "no prefix",
pdaDefinition: codec.PDATypeDef{
Prefix: "",
Prefix: nil,
Seeds: []codec.PDASeed{
{
Name: "PubKey",
Expand All @@ -369,15 +369,15 @@ func TestSolanaChainReaderService_GetLatestValue(t *testing.T) {
{
name: "public key seed provided as bytes",
pdaDefinition: codec.PDATypeDef{
Prefix: prefixString,
Prefix: prefixBytes,
Seeds: []codec.PDASeed{
{
Name: "PubKey",
Type: codec.IdlTypePublicKey,
},
},
},
expected: mustFindProgramAddress(t, programID, [][]byte{[]byte(prefixString), pubKey.Bytes()}),
expected: mustFindProgramAddress(t, programID, [][]byte{prefixBytes, pubKey.Bytes()}),
params: map[string]any{
"PubKey": pubKey.Bytes(),
},
Expand Down Expand Up @@ -424,12 +424,12 @@ func TestSolanaChainReaderService_GetLatestValue(t *testing.T) {
})

t.Run("PDA account read errors if missing param", func(t *testing.T) {
prefixString := "Prefix"
prefixBytes := []byte("Prefix")
readDef := config.ReadDefinition{
ChainSpecificName: testutils.TestStructWithNestedStruct,
ReadType: config.Account,
PDADefiniton: codec.PDATypeDef{
Prefix: prefixString,
Prefix: prefixBytes,
Seeds: []codec.PDASeed{
{
Name: "PubKey",
Expand Down
2 changes: 2 additions & 0 deletions pkg/solana/chainwriter/chain_writer.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import (
"github.com/smartcontractkit/chainlink-solana/pkg/solana/codec"
"github.com/smartcontractkit/chainlink-solana/pkg/solana/fees"
"github.com/smartcontractkit/chainlink-solana/pkg/solana/txm"
"github.com/smartcontractkit/chainlink-solana/pkg/solana/utils"
)

const ServiceName = "SolanaChainWriter"
Expand Down Expand Up @@ -91,6 +92,7 @@ func (s *SolanaChainWriterService) parsePrograms(config ChainWriterConfig) error
return fmt.Errorf("failed to unmarshal IDL for program: %s, error: %w", program, err)
}
for method, methodConfig := range programConfig.Methods {
utils.InjectAddressModifier(methodConfig.InputModifications, nil)
idlDef, err := codec.FindDefinitionFromIDL(codec.ChainConfigTypeInstructionDef, methodConfig.ChainSpecificName, idl)
if err != nil {
return err
Expand Down
36 changes: 30 additions & 6 deletions pkg/solana/chainwriter/chain_writer_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import (
"github.com/smartcontractkit/chainlink-common/pkg/utils/tests"

idl "github.com/smartcontractkit/chainlink-ccip/chains/solana"
"github.com/smartcontractkit/chainlink-ccip/chains/solana/gobindings/ccip_offramp"
"github.com/smartcontractkit/chainlink-ccip/chains/solana/gobindings/ccip_router"
"github.com/smartcontractkit/chainlink-ccip/pkg/types/ccipocr3"

Expand All @@ -37,6 +38,7 @@ type Arguments struct {
Seed2 []byte
}

var ccipOfframpIDL = idl.FetchCCIPOfframpIDL()
var ccipRouterIDL = idl.FetchCCIPRouterIDL()
var testContractIDL = chainwriter.FetchTestContractIDL()

Expand Down Expand Up @@ -584,14 +586,15 @@ func TestChainWriter_SubmitTransaction(t *testing.T) {
})
}

func TestChainWriter_CCIPRouter(t *testing.T) {
func TestChainWriter_CCIPOfframp(t *testing.T) {
t.Parallel()

// setup admin key
adminPk, err := solana.NewRandomPrivateKey()
require.NoError(t, err)
admin := adminPk.PublicKey()

offrampAddr := chainwriter.GetRandomPubKey(t)
routerAddr := chainwriter.GetRandomPubKey(t)
destTokenAddr := chainwriter.GetRandomPubKey(t)

Expand All @@ -601,7 +604,7 @@ func TestChainWriter_CCIPRouter(t *testing.T) {
// simplified CCIP Config - does not contain full account list
ccipCWConfig := chainwriter.ChainWriterConfig{
Programs: map[string]chainwriter.ProgramConfig{
"ccip_router": {
"ccip-offramp": {
Methods: map[string]chainwriter.MethodConfig{
"execute": {
FromAddress: admin.String(),
Expand Down Expand Up @@ -676,6 +679,10 @@ func TestChainWriter_CCIPRouter(t *testing.T) {
},
},
},
IDL: ccipOfframpIDL,
},
// Requires only the IDL for the CCIPArgsTransform to fetch the TokenAdminRegistry
"ccip-router": {
IDL: ccipRouterIDL,
},
},
Expand All @@ -702,13 +709,14 @@ func TestChainWriter_CCIPRouter(t *testing.T) {

lookupTable := mockTokenAdminRegistryLookupTable(t, rw, pda)

mockFetchRouterAddress(t, rw, routerAddr, offrampAddr)
mockFetchLookupTableAddresses(t, rw, lookupTable, poolKeys)

txID := uuid.NewString()
txm.On("Enqueue", mock.Anything, admin.String(), mock.MatchedBy(func(tx *solana.Transaction) bool {
txData := tx.Message.Instructions[0].Data
payload := txData[8:]
var decoded ccip_router.Execute
var decoded ccip_offramp.Execute
dec := ag_binary.NewBorshDecoder(payload)
err = dec.Decode(&decoded)
require.NoError(t, err)
Expand Down Expand Up @@ -746,7 +754,7 @@ func TestChainWriter_CCIPRouter(t *testing.T) {
},
}

submitErr := cw.SubmitTransaction(ctx, "ccip_router", "execute", args, txID, routerAddr.String(), nil, nil)
submitErr := cw.SubmitTransaction(ctx, "ccip-offramp", "execute", args, txID, offrampAddr.String(), nil, nil)
require.NoError(t, submitErr)
})

Expand Down Expand Up @@ -787,14 +795,14 @@ func TestChainWriter_CCIPRouter(t *testing.T) {
txm.On("Enqueue", mock.Anything, admin.String(), mock.MatchedBy(func(tx *solana.Transaction) bool {
txData := tx.Message.Instructions[0].Data
payload := txData[8:]
var decoded ccip_router.Commit
var decoded ccip_offramp.Commit
dec := ag_binary.NewBorshDecoder(payload)
err := dec.Decode(&decoded)
require.NoError(t, err)
return true
}), &txID, mock.Anything).Return(nil).Once()

submitErr := cw.SubmitTransaction(ctx, "ccip_router", "commit", args, txID, routerAddr.String(), nil, nil)
submitErr := cw.SubmitTransaction(ctx, "ccip-offramp", "commit", args, txID, offrampAddr.String(), nil, nil)
require.NoError(t, submitErr)
})
}
Expand Down Expand Up @@ -951,3 +959,19 @@ func mockFetchLookupTableAddresses(t *testing.T, rw *clientmocks.ReaderWriter, l
Value: &rpc.Account{Data: rpc.DataBytesOrJSONFromBytes(lookupTableStateBytes)},
}, nil)
}

func mockFetchRouterAddress(t *testing.T, rw *clientmocks.ReaderWriter, routerAddr, offrampAddr solana.PublicKey) {
pda, _, err := solana.FindProgramAddress([][]byte{[]byte("reference_addresses")}, offrampAddr)
require.NoError(t, err)
referenceAddresses := ccip_offramp.ReferenceAddresses{
Version: 1,
Router: routerAddr,
FeeQuoter: solana.PublicKey{},
OfframpLookupTable: solana.PublicKey{},
}
referenceAddressesBytes := mustBorshEncodeStruct(t, referenceAddresses)
rw.On("GetAccountInfoWithOpts", mock.Anything, pda, mock.Anything).Return(&rpc.GetAccountInfoResult{
RPCContext: rpc.RPCContext{},
Value: &rpc.Account{Data: rpc.DataBytesOrJSONFromBytes(referenceAddressesBytes)},
}, nil)
}
2 changes: 1 addition & 1 deletion pkg/solana/chainwriter/helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ type DataAccount struct {
//go:embed testContractIDL.json
var testContractIDL string

// FetchCCIPRouterIDL returns the IDL for chain components test contract
// FetchTestContractIDL returns the IDL for chain components test contract
func FetchTestContractIDL() string {
return testContractIDL
}
Expand Down
2 changes: 1 addition & 1 deletion pkg/solana/chainwriter/lookups.go
Original file line number Diff line number Diff line change
Expand Up @@ -282,7 +282,7 @@ func getSeedBytesCombinations(
} else if seed.Dynamic != nil {
dynamicSeed := seed.Dynamic
if lookupSeed, ok := dynamicSeed.(AccountLookup); ok {
// Get value from a location (This doens't have to be an address, it can be any value)
// Get value from a location (This doesn't have to be an address, it can be any value)
bytes, err := GetValuesAtLocation(args, lookupSeed.Location)
if err != nil {
return nil, fmt.Errorf("error getting address seed for location %q: %w", lookupSeed.Location, err)
Expand Down
Loading

0 comments on commit 9752635

Please sign in to comment.