Skip to content

Commit

Permalink
Merge branch 'develop' into NONEVM-746-LogPoller-ObservedORM
Browse files Browse the repository at this point in the history
  • Loading branch information
DylanTinianov authored Feb 21, 2025
2 parents 2448a1e + c2466cd commit f69f225
Show file tree
Hide file tree
Showing 12 changed files with 689 additions and 356 deletions.
3 changes: 3 additions & 0 deletions CODEOWNERS
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@
# global ownership
* @smartcontractkit/bix-build

# offchain code ownership
/pkg/solana @smartcontractkit/bix-framework @smartcontractkit/bix-build

# e2e test ownership
/integration-tests @smartcontractkit/qa @smartcontractkit/bix-build

Expand Down
4 changes: 2 additions & 2 deletions contracts/examples/hello-world/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@
"@coral-xyz/anchor": "^0.29.0"
},
"devDependencies": {
"@types/mocha": "^9.0.0",
"@types/mocha": "^10.0.0",
"chai": "^4.3.4",
"mocha": "^9.0.3",
"mocha": "^11.0.0",
"ts-mocha": "^10.0.0",
"typescript": "^4.3.5"
},
Expand Down
402 changes: 242 additions & 160 deletions contracts/examples/hello-world/pnpm-lock.yaml

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions contracts/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,14 @@
"@solana/spl-token": "^0.3.5",
"@solana/web3.js": "^1.50.1 <=1.92.3",
"@types/chai": "^4.2.22",
"@types/mocha": "^9.0.0",
"@types/mocha": "^10.0.0",
"@types/node": "^14.14.37",
"@types/secp256k1": "^4.0.3",
"bn.js": "^5.2.0",
"borsh": "^0.7.0",
"chai": "^4.3.4",
"ethereum-cryptography": "^0.1.3",
"mocha": "^9.0.0",
"mocha": "^11.0.0",
"prettier": "^2.5.1",
"rpc-websockets": "<=7.10.0",
"secp256k1": "^4.0.2",
Expand Down
418 changes: 252 additions & 166 deletions contracts/pnpm-lock.yaml

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion integration-tests/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ require (
github.com/lib/pq v1.10.9
github.com/pelletier/go-toml/v2 v2.2.3
github.com/rs/zerolog v1.33.0
github.com/smartcontractkit/chainlink-ccip v0.0.0-20250212131315-e9b53b05b02a
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-solana v1.1.2-0.20250213035259-e727e73f6181
Expand Down Expand Up @@ -327,7 +328,6 @@ require (
github.com/slack-go/slack v0.15.0 // indirect
github.com/smartcontractkit/chain-selectors v1.0.40 // indirect
github.com/smartcontractkit/chainlink-automation v0.8.1 // indirect
github.com/smartcontractkit/chainlink-ccip v0.0.0-20250212131315-e9b53b05b02a // indirect
github.com/smartcontractkit/chainlink-data-streams v0.1.1-0.20250128203428-08031923fbe5 // indirect
github.com/smartcontractkit/chainlink-feeds v0.1.1 // indirect
github.com/smartcontractkit/chainlink-framework/chains v0.0.0-20250207205350-420ccacab78a // indirect
Expand Down
32 changes: 24 additions & 8 deletions integration-tests/relayinterface/lookups_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,13 @@ package relayinterface

import (
"context"
"encoding/binary"
"testing"
"time"

"github.com/gagliardetto/solana-go"
"github.com/gagliardetto/solana-go/rpc"
"github.com/smartcontractkit/chainlink-ccip/pkg/types/ccipocr3"
"github.com/stretchr/testify/require"

"github.com/smartcontractkit/chainlink-ccip/chains/solana/utils/tokens"
Expand Down Expand Up @@ -310,9 +312,15 @@ func TestPDALookups(t *testing.T) {
})
t.Run("PDALookup resolves valid PDA with non-address lookup seeds", func(t *testing.T) {
seed1 := []byte("test_seed")
seed2 := []byte("another_seed")

pda, _, err := solana.FindProgramAddress([][]byte{seed1, seed2}, programID)
seed2 := uint64(4)
bufSeed2 := make([]byte, 8)
binary.LittleEndian.PutUint64(bufSeed2, seed2)
seed3 := ccipocr3.ChainSelector(4)
bufSeed3 := make([]byte, 8)
binary.LittleEndian.PutUint64(bufSeed3, uint64(seed3))
seed4 := ccipocr3.Bytes32(chainwriter.GetRandomPubKey(t).Bytes())

pda, _, err := solana.FindProgramAddress([][]byte{seed1, bufSeed2, bufSeed3, seed4[:]}, programID)
require.NoError(t, err)

expectedMeta := []*solana.AccountMeta{
Expand All @@ -329,14 +337,18 @@ func TestPDALookups(t *testing.T) {
Seeds: []chainwriter.Seed{
{Dynamic: chainwriter.Lookup{AccountLookup: &chainwriter.AccountLookup{Name: "seed1", Location: "test_seed"}}},
{Dynamic: chainwriter.Lookup{AccountLookup: &chainwriter.AccountLookup{Name: "seed2", Location: "another_seed"}}},
{Dynamic: chainwriter.Lookup{AccountLookup: &chainwriter.AccountLookup{Name: "seed3", Location: "ccip_chain_selector"}}},
{Dynamic: chainwriter.Lookup{AccountLookup: &chainwriter.AccountLookup{Name: "seed4", Location: "ccip_bytes"}}},
},
IsSigner: false,
IsWritable: true,
}}

args := map[string]interface{}{
"test_seed": seed1,
"another_seed": seed2,
"test_seed": seed1,
"another_seed": seed2,
"ccip_chain_selector": seed3,
"ccip_bytes": seed4,
}

result, err := pdaLookup.Resolve(ctx, args, nil, client.MultiClient{})
Expand Down Expand Up @@ -366,8 +378,10 @@ func TestPDALookups(t *testing.T) {
t.Run("PDALookup resolves valid PDA with address lookup seeds", func(t *testing.T) {
seed1 := chainwriter.GetRandomPubKey(t)
seed2 := chainwriter.GetRandomPubKey(t)
addr3 := chainwriter.GetRandomPubKey(t)
seed3 := ccipocr3.UnknownEncodedAddress(addr3.String())

pda, _, err := solana.FindProgramAddress([][]byte{seed1.Bytes(), seed2.Bytes()}, programID)
pda, _, err := solana.FindProgramAddress([][]byte{seed1.Bytes(), seed2.Bytes(), addr3.Bytes()}, programID)
require.NoError(t, err)

expectedMeta := []*solana.AccountMeta{
Expand All @@ -384,14 +398,16 @@ func TestPDALookups(t *testing.T) {
Seeds: []chainwriter.Seed{
{Dynamic: chainwriter.Lookup{AccountLookup: &chainwriter.AccountLookup{Name: "seed1", Location: "test_seed"}}},
{Dynamic: chainwriter.Lookup{AccountLookup: &chainwriter.AccountLookup{Name: "seed2", Location: "another_seed"}}},
{Dynamic: chainwriter.Lookup{AccountLookup: &chainwriter.AccountLookup{Name: "seed3", Location: "unknown_encoded_address"}}},
},
IsSigner: false,
IsWritable: true,
}}

args := map[string]interface{}{
"test_seed": seed1,
"another_seed": seed2,
"test_seed": seed1,
"another_seed": seed2,
"unknown_encoded_address": seed3,
}

result, err := pdaLookup.Resolve(ctx, args, nil, client.MultiClient{})
Expand Down
2 changes: 1 addition & 1 deletion pkg/solana/chainwriter/chain_writer.go
Original file line number Diff line number Diff line change
Expand Up @@ -384,7 +384,7 @@ func (s *SolanaChainWriterService) SubmitTransaction(ctx context.Context, contra
return errorWithDebugID(fmt.Errorf("error finding transform function: %w", tfErr), debugID)
}
s.lggr.Debugw("Applying args transformation", "contract", contractName, "method", method)
args, err = transformFunc(ctx, s, args, accounts, toAddress)
args, accounts, err = transformFunc(ctx, s, args, accounts, toAddress)
if err != nil {
return errorWithDebugID(fmt.Errorf("error transforming args: %w", err), debugID)
}
Expand Down
8 changes: 5 additions & 3 deletions pkg/solana/chainwriter/chain_writer_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -778,7 +778,7 @@ func TestChainWriter_CCIPOfframp(t *testing.T) {
},
},
ChainSpecificName: "execute",
ArgsTransform: "CCIP",
ArgsTransform: "CCIPExecute",
LookupTables: chainwriter.LookupTables{},
Accounts: []chainwriter.Lookup{
{AccountConstant: &chainwriter.AccountConstant{
Expand Down Expand Up @@ -822,7 +822,7 @@ func TestChainWriter_CCIPOfframp(t *testing.T) {
},
},
ChainSpecificName: "commit",
ArgsTransform: "",
ArgsTransform: "CCIPCommit",
LookupTables: chainwriter.LookupTables{},
Accounts: []chainwriter.Lookup{
{AccountConstant: &chainwriter.AccountConstant{
Expand Down Expand Up @@ -922,7 +922,7 @@ func TestChainWriter_CCIPOfframp(t *testing.T) {
require.NoError(t, submitErr)
})

t.Run("CCIP commit is encoded successfully", func(t *testing.T) {
t.Run("CCIP commit is encoded successfully and ArgsTransform is applied correctly.", func(t *testing.T) {
// mock txm
txm := txmMocks.NewTxManager(t)
// initialize chain writer
Expand Down Expand Up @@ -963,6 +963,8 @@ func TestChainWriter_CCIPOfframp(t *testing.T) {
dec := ag_binary.NewBorshDecoder(payload)
err := dec.Decode(&decoded)
require.NoError(t, err)
// The CCIPCommit ArgsTransform should remove the last account since no price updates were provided in the report
require.Len(t, tx.Message.Instructions[0].Accounts, 2)
return true
}), &txID, mock.Anything).Return(nil).Once()

Expand Down
12 changes: 12 additions & 0 deletions pkg/solana/chainwriter/helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -77,10 +77,22 @@ func GetValuesAtLocation(args any, location string) ([][]byte, error) {
vals = append(vals, value.Bytes())
case ccipocr3.UnknownAddress:
vals = append(vals, value)
case ccipocr3.UnknownEncodedAddress:
decoded, err := solana.PublicKeyFromBase58(string(value))
if err != nil {
return nil, err
}
vals = append(vals, decoded[:])
case uint64:
buf := make([]byte, 8)
binary.LittleEndian.PutUint64(buf, value)
vals = append(vals, buf)
case ccipocr3.ChainSelector:
buf := make([]byte, 8)
binary.LittleEndian.PutUint64(buf, uint64(value))
vals = append(vals, buf)
case ccipocr3.Bytes32:
vals = append(vals, value[:])
case [32]uint8:
vals = append(vals, value[:])
default:
Expand Down
48 changes: 35 additions & 13 deletions pkg/solana/chainwriter/transform_registry.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package chainwriter

import (
"context"
"errors"
"fmt"

"github.com/gagliardetto/solana-go"
Expand All @@ -26,22 +27,24 @@ type ReportPostTransform struct {
TokenIndexes []byte
}

func FindTransform(id string) (func(context.Context, *SolanaChainWriterService, any, solana.AccountMetaSlice, string) (any, error), error) {
func FindTransform(id string) (func(context.Context, *SolanaChainWriterService, any, solana.AccountMetaSlice, string) (any, solana.AccountMetaSlice, error), error) {
switch id {
case "CCIP":
return CCIPArgsTransform, nil
case "CCIPExecute":
return CCIPExecuteArgsTransform, nil
case "CCIPCommit":
return CCIPCommitAccountTransform, nil
default:
return nil, fmt.Errorf("transform not found")
}
}

// This Transform function looks up the token pool addresses in the accounts slice and augments the args
// with the indexes of the token pool addresses in the accounts slice.
func CCIPArgsTransform(ctx context.Context, cw *SolanaChainWriterService, args any, accounts solana.AccountMetaSlice, toAddress string) (any, error) {
func CCIPExecuteArgsTransform(ctx context.Context, cw *SolanaChainWriterService, args any, accounts solana.AccountMetaSlice, toAddress string) (any, solana.AccountMetaSlice, error) {
// Fetch offramp config to use to fetch the router address
offrampProgramConfig, ok := cw.config.Programs[ccipconsts.ContractNameOffRamp]
if !ok {
return nil, fmt.Errorf("%s program not found in config", ccipconsts.ContractNameOffRamp)
return nil, nil, fmt.Errorf("%s program not found in config", ccipconsts.ContractNameOffRamp)
}
// PDA lookup to fetch router address
routerAddrLookup := PDALookups{
Expand All @@ -61,16 +64,16 @@ func CCIPArgsTransform(ctx context.Context, cw *SolanaChainWriterService, args a
}
accountMetas, err := routerAddrLookup.Resolve(ctx, nil, nil, cw.client)
if err != nil {
return nil, fmt.Errorf("failed to fetch the router program address from the reference addresses account: %w", err)
return nil, nil, fmt.Errorf("failed to fetch the router program address from the reference addresses account: %w", err)
}
if len(accountMetas) != 1 {
return nil, fmt.Errorf("expect 1 address to be returned for router address, received %d: %w", len(accountMetas), err)
return nil, nil, fmt.Errorf("expect 1 address to be returned for router address, received %d: %w", len(accountMetas), err)
}

// Fetch router config to use to fetch TokenAdminRegistry
routerProgramConfig, ok := cw.config.Programs[ccipconsts.ContractNameRouter]
if !ok {
return nil, fmt.Errorf("%s program not found in config", ccipconsts.ContractNameRouter)
return nil, nil, fmt.Errorf("%s program not found in config", ccipconsts.ContractNameRouter)
}

routerAddress := accountMetas[0].PublicKey
Expand Down Expand Up @@ -101,7 +104,7 @@ func CCIPArgsTransform(ctx context.Context, cw *SolanaChainWriterService, args a

tableMap, _, err := cw.ResolveLookupTables(ctx, args, TokenPoolLookupTable)
if err != nil {
return nil, err
return nil, nil, err
}
registryTables := tableMap["PoolLookupTable"]
tokenPoolAddresses := []solana.PublicKey{}
Expand All @@ -114,20 +117,20 @@ func CCIPArgsTransform(ctx context.Context, cw *SolanaChainWriterService, args a
for _, address := range tokenPoolAddresses {
if account.PublicKey == address {
if i > 255 {
return nil, fmt.Errorf("index %d out of range for uint8", i)
return nil, nil, fmt.Errorf("index %d out of range for uint8", i)
}
tokenIndexes = append(tokenIndexes, uint8(i)) //nolint:gosec
}
}
}

if len(tokenIndexes) != len(tokenPoolAddresses) {
return nil, fmt.Errorf("missing token pools in accounts")
return nil, nil, fmt.Errorf("missing token pools in accounts")
}

argsTyped, ok := args.(ReportPreTransform)
if !ok {
return nil, fmt.Errorf("args is not of type ReportPreTransform")
return nil, nil, fmt.Errorf("args is not of type ReportPreTransform")
}

argsTransformed := ReportPostTransform{
Expand All @@ -138,5 +141,24 @@ func CCIPArgsTransform(ctx context.Context, cw *SolanaChainWriterService, args a
TokenIndexes: tokenIndexes,
}

return argsTransformed, nil
return argsTransformed, accounts, nil
}

// This Transform function trims off the GlobalState account from commit transactions if there are no token or gas price updates
func CCIPCommitAccountTransform(ctx context.Context, cw *SolanaChainWriterService, args any, accounts solana.AccountMetaSlice, toAddress string) (any, solana.AccountMetaSlice, error) {
var tokenPriceVals, gasPriceVals [][]byte
var err error
tokenPriceVals, err = GetValuesAtLocation(args, "Info.TokenPrices.TokenID")
if err != nil && !errors.Is(err, errFieldNotFound) {
return nil, nil, fmt.Errorf("error getting values at location: %w", err)
}
gasPriceVals, err = GetValuesAtLocation(args, "Info.GasPrices.ChainSel")
if err != nil && !errors.Is(err, errFieldNotFound) {
return nil, nil, fmt.Errorf("error getting values at location: %w", err)
}
transformedAccounts := accounts
if len(tokenPriceVals) == 0 && len(gasPriceVals) == 0 {
transformedAccounts = accounts[:len(accounts)-1]
}
return args, transformedAccounts, nil
}
Loading

0 comments on commit f69f225

Please sign in to comment.