Skip to content

Commit

Permalink
Updated execute args transform to use existing derived table map
Browse files Browse the repository at this point in the history
  • Loading branch information
amit-momin committed Feb 22, 2025
1 parent 4efa76d commit 78ee452
Show file tree
Hide file tree
Showing 3 changed files with 42 additions and 115 deletions.
2 changes: 1 addition & 1 deletion pkg/solana/chainwriter/chain_writer.go
Original file line number Diff line number Diff line change
Expand Up @@ -390,7 +390,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, accounts, err = transformFunc(ctx, s, args, accounts, toAddress)
args, accounts, err = transformFunc(ctx, args, accounts, derivedTableMap)
if err != nil {
return errorWithDebugID(fmt.Errorf("error transforming args: %w", err), debugID)
}
Expand Down
94 changes: 17 additions & 77 deletions pkg/solana/chainwriter/transform_registry.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import (

"github.com/gagliardetto/solana-go"
"github.com/smartcontractkit/chainlink-ccip/chains/solana/gobindings/ccip_offramp"
ccipconsts "github.com/smartcontractkit/chainlink-ccip/pkg/consts"
"github.com/smartcontractkit/chainlink-ccip/pkg/types/ccipocr3"
)

Expand All @@ -27,7 +26,7 @@ type ReportPostTransform struct {
TokenIndexes []byte
}

func FindTransform(id string) (func(context.Context, *SolanaChainWriterService, any, solana.AccountMetaSlice, string) (any, solana.AccountMetaSlice, error), error) {
func FindTransform(id string) (func(context.Context, any, solana.AccountMetaSlice, map[string]map[string][]*solana.AccountMeta) (any, solana.AccountMetaSlice, error), error) {
switch id {
case "CCIPExecute":
return CCIPExecuteArgsTransform, nil
Expand All @@ -40,73 +39,26 @@ func FindTransform(id string) (func(context.Context, *SolanaChainWriterService,

// 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 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]
func CCIPExecuteArgsTransform(ctx context.Context, args any, accounts solana.AccountMetaSlice, tableMap map[string]map[string][]*solana.AccountMeta) (any, solana.AccountMetaSlice, error) {
argsTyped, ok := args.(ReportPreTransform)
if !ok {
return nil, nil, fmt.Errorf("%s program not found in config", ccipconsts.ContractNameOffRamp)
}
// PDA lookup to fetch router address
routerAddrLookup := PDALookups{
Name: "ReferenceAddresses",
PublicKey: Lookup{AccountConstant: &AccountConstant{
Address: toAddress,
}},
Seeds: []Seed{
{Static: []byte("reference_addresses")},
},
// Reads the router address from the reference addresses PDA
InternalField: InternalField{
TypeName: "ReferenceAddresses",
Location: "Router",
IDL: offrampProgramConfig.IDL,
},
}
accountMetas, err := routerAddrLookup.Resolve(ctx, nil, nil, cw.client)
if err != nil {
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, nil, fmt.Errorf("expect 1 address to be returned for router address, received %d: %w", len(accountMetas), err)
return nil, nil, fmt.Errorf("args is not of type ReportPreTransform")
}

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

routerAddress := accountMetas[0].PublicKey
TokenPoolLookupTable := LookupTables{
DerivedLookupTables: []DerivedLookupTable{
{
Name: "PoolLookupTable",
Accounts: Lookup{PDALookups: &PDALookups{
Name: "TokenAdminRegistry",
PublicKey: Lookup{AccountConstant: &AccountConstant{
Address: routerAddress.String(),
}},
Seeds: []Seed{
{Static: []byte("token_admin_registry")},
{Dynamic: Lookup{AccountLookup: &AccountLookup{Location: "Info.AbstractReports.Messages.TokenAmounts.DestTokenAddress"}}},
},
IsSigner: false,
IsWritable: false,
InternalField: InternalField{
TypeName: "TokenAdminRegistry",
Location: "LookupTable",
IDL: routerProgramConfig.IDL,
},
}},
},
},
registryTables, exists := tableMap["PoolLookupTable"]
// If PoolLookupTable does not exist in the table map, token indexes are not needed
// Return with empty TokenIndexes
if !exists {
argsTransformed.TokenIndexes = []byte{}
return argsTransformed, accounts, nil
}

tableMap, _, err := cw.ResolveLookupTables(ctx, args, TokenPoolLookupTable)
if err != nil {
return nil, nil, err
}
registryTables := tableMap["PoolLookupTable"]
tokenPoolAddresses := []solana.PublicKey{}
for _, table := range registryTables {
tokenPoolAddresses = append(tokenPoolAddresses, table[0].PublicKey)
Expand All @@ -128,24 +80,12 @@ func CCIPExecuteArgsTransform(ctx context.Context, cw *SolanaChainWriterService,
return nil, nil, fmt.Errorf("missing token pools in accounts")
}

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

argsTransformed := ReportPostTransform{
ReportContext: argsTyped.ReportContext,
Report: argsTyped.Report,
AbstractReport: argsTyped.AbstractReport,
Info: argsTyped.Info,
TokenIndexes: tokenIndexes,
}

argsTransformed.TokenIndexes = tokenIndexes
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) {
func CCIPCommitAccountTransform(ctx context.Context, args any, accounts solana.AccountMetaSlice, _ map[string]map[string][]*solana.AccountMeta) (any, solana.AccountMetaSlice, error) {
var tokenPriceVals, gasPriceVals [][]byte
var err error
tokenPriceVals, err = GetValuesAtLocation(args, "Info.TokenPrices.TokenID")
Expand Down
61 changes: 24 additions & 37 deletions pkg/solana/chainwriter/transform_registry_test.go
Original file line number Diff line number Diff line change
@@ -1,46 +1,18 @@
package chainwriter_test

import (
"context"
"testing"

"github.com/gagliardetto/solana-go"
ccipconsts "github.com/smartcontractkit/chainlink-ccip/pkg/consts"
"github.com/smartcontractkit/chainlink-ccip/pkg/types/ccipocr3"
"github.com/smartcontractkit/chainlink-common/pkg/utils/tests"
"github.com/stretchr/testify/require"

"github.com/smartcontractkit/chainlink-solana/pkg/monitoring/testutils"
"github.com/smartcontractkit/chainlink-solana/pkg/solana/chainwriter"
"github.com/smartcontractkit/chainlink-solana/pkg/solana/client"
clientmocks "github.com/smartcontractkit/chainlink-solana/pkg/solana/client/mocks"
)

func Test_CCIPExecuteArgsTransform(t *testing.T) {
ctx := tests.Context(t)
offrampAddress := chainwriter.GetRandomPubKey(t)
routerAddress := chainwriter.GetRandomPubKey(t)

// simplified CCIP Config - only IDLs are required for CCIPExecute ArgsTransform
ccipCWConfig := chainwriter.ChainWriterConfig{
Programs: map[string]chainwriter.ProgramConfig{
ccipconsts.ContractNameOffRamp: {
IDL: ccipOfframpIDL,
},
// Requires only the IDL for the CCIPArgsTransform to fetch the TokenAdminRegistry
ccipconsts.ContractNameRouter: {
IDL: ccipRouterIDL,
},
},
}
// mock client
rw := clientmocks.NewReaderWriter(t)
mc := *client.NewMultiClient(func(context.Context) (client.ReaderWriter, error) {
return rw, nil
})
// initialize chain writer
cw, err := chainwriter.NewSolanaChainWriterService(testutils.NewNullLogger(), mc, nil, nil, ccipCWConfig)
require.NoError(t, err)

destTokenAddr := chainwriter.GetRandomPubKey(t)
poolKeys := []solana.PublicKey{destTokenAddr}
Expand All @@ -61,13 +33,18 @@ func Test_CCIPExecuteArgsTransform(t *testing.T) {
accounts := []*solana.AccountMeta{{PublicKey: poolKeys[0]}, {PublicKey: poolKeys[1]}}

t.Run("CCIPExecute ArgsTransform includes token indexes", func(t *testing.T) {
pda, _, err := solana.FindProgramAddress([][]byte{[]byte("token_admin_registry"), destTokenAddr.Bytes()}, routerAddress)
require.NoError(t, err)
tableMap := make(map[string]map[string][]*solana.AccountMeta)
tableMap["PoolLookupTable"] = make(map[string][]*solana.AccountMeta)
lookupTablePubkey := chainwriter.GetRandomPubKey(t)

poolKeysMeta := make([]*solana.AccountMeta, 0, 2)
for _, poolKey := range poolKeys {
poolKeysMeta = append(poolKeysMeta, &solana.AccountMeta{PublicKey: poolKey})

}
tableMap["PoolLookupTable"][lookupTablePubkey.String()] = poolKeysMeta

lookupTable := mockTokenAdminRegistryLookupTable(t, rw, pda)
mockFetchRouterAddress(t, rw, routerAddress, offrampAddress)
mockFetchLookupTableAddresses(t, rw, lookupTable, poolKeys)
transformedArgs, newAccounts, err := chainwriter.CCIPExecuteArgsTransform(ctx, cw, args, accounts, offrampAddress.String())
transformedArgs, newAccounts, err := chainwriter.CCIPExecuteArgsTransform(ctx, args, accounts, tableMap)
require.NoError(t, err)
// Accounts should be unchanged
require.Len(t, newAccounts, 2)
Expand All @@ -76,11 +53,21 @@ func Test_CCIPExecuteArgsTransform(t *testing.T) {
require.NotNil(t, typedArgs.TokenIndexes)
require.Len(t, typedArgs.TokenIndexes, 1)
})

t.Run("CCIPExecute ArgsTransform includes empty token indexes if lookup table not found", func(t *testing.T) {
transformedArgs, newAccounts, err := chainwriter.CCIPExecuteArgsTransform(ctx, args, accounts, nil)
require.NoError(t, err)
// Accounts should be unchanged
require.Len(t, newAccounts, 2)
typedArgs, ok := transformedArgs.(chainwriter.ReportPostTransform)
require.True(t, ok)
require.NotNil(t, typedArgs.TokenIndexes)
require.Len(t, typedArgs.TokenIndexes, 0)
})
}

func Test_CCIPCommitAccountTransform(t *testing.T) {
ctx := tests.Context(t)
offrampAddress := chainwriter.GetRandomPubKey(t)
key1 := chainwriter.GetRandomPubKey(t)
key2 := chainwriter.GetRandomPubKey(t)
t.Run("CCIPCommit ArgsTransform does not affect accounts if token prices exist", func(t *testing.T) {
Expand All @@ -92,7 +79,7 @@ func Test_CCIPCommitAccountTransform(t *testing.T) {
},
}
accounts := []*solana.AccountMeta{{PublicKey: key1}, {PublicKey: key2}}
_, newAccounts, err := chainwriter.CCIPCommitAccountTransform(ctx, nil, args, accounts, offrampAddress.String())
_, newAccounts, err := chainwriter.CCIPCommitAccountTransform(ctx, args, accounts, nil)
require.NoError(t, err)
require.Len(t, newAccounts, 2)
})
Expand All @@ -103,7 +90,7 @@ func Test_CCIPCommitAccountTransform(t *testing.T) {
Info: ccipocr3.CommitReportInfo{},
}
accounts := []*solana.AccountMeta{{PublicKey: key1}, {PublicKey: key2}}
_, newAccounts, err := chainwriter.CCIPCommitAccountTransform(ctx, nil, args, accounts, offrampAddress.String())
_, newAccounts, err := chainwriter.CCIPCommitAccountTransform(ctx, args, accounts, nil)
require.NoError(t, err)
require.Len(t, newAccounts, 1)
})
Expand Down

0 comments on commit 78ee452

Please sign in to comment.