Skip to content

Commit

Permalink
pdas in ab
Browse files Browse the repository at this point in the history
  • Loading branch information
tt-cll committed Jan 31, 2025
1 parent 6118cc6 commit d249150
Show file tree
Hide file tree
Showing 4 changed files with 85 additions and 23 deletions.
40 changes: 24 additions & 16 deletions deployment/ccip/changeset/solana/cs_chain_contracts.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"context"
"errors"
"fmt"
"strconv"

"github.com/gagliardetto/solana-go"

Expand Down Expand Up @@ -132,7 +133,7 @@ func (cfg AddRemoteChainToSolanaConfig) Validate(e deployment.Environment) error
if err != nil {
return fmt.Errorf("failed to load onchain state: %w", err)
}

supportedChains := state.SupportedChains()
for chainSel, updates := range cfg.UpdatesByChain {
chainState, ok := state.SolChains[chainSel]
Expand Down Expand Up @@ -175,17 +176,22 @@ func AddRemoteChainToSolana(e deployment.Environment, cfg AddRemoteChainToSolana
return deployment.ChangesetOutput{}, err
}

ab := deployment.NewMemoryAddressBook()
for chainSel, updates := range cfg.UpdatesByChain {
_, err := doAddRemoteChainToSolana(e, s, chainSel, updates)
err := doAddRemoteChainToSolana(e, s, chainSel, updates, ab)
if err != nil {
return deployment.ChangesetOutput{}, err
return deployment.ChangesetOutput{AddressBook: ab}, err
}
}

return deployment.ChangesetOutput{}, nil
return deployment.ChangesetOutput{AddressBook: ab}, nil
}

func doAddRemoteChainToSolana(e deployment.Environment, s cs.CCIPOnChainState, chainSel uint64, updates map[uint64]RemoteChainConfigSolana) (deployment.ChangesetOutput, error) {
func doAddRemoteChainToSolana(
e deployment.Environment,
s cs.CCIPOnChainState,
chainSel uint64,
updates map[uint64]RemoteChainConfigSolana,
ab deployment.AddressBook) error {
chain := e.SolChains[chainSel]
ccipRouterID := s.SolChains[chainSel].Router

Expand All @@ -195,11 +201,11 @@ func doAddRemoteChainToSolana(e deployment.Environment, s cs.CCIPOnChainState, c
remoteChainFamily, _ := chainsel.GetSelectorFamily(remoteChainSel)
switch remoteChainFamily {
case chainsel.FamilySolana:
return deployment.ChangesetOutput{}, fmt.Errorf("support for solana chain as remote chain is not implemented yet %d", remoteChainSel)
return fmt.Errorf("support for solana chain as remote chain is not implemented yet %d", remoteChainSel)
case chainsel.FamilyEVM:
onRampAddress := s.Chains[remoteChainSel].OnRamp.Address().String()
if onRampAddress == "" {
return deployment.ChangesetOutput{}, fmt.Errorf("onramp address not found for chain %d", remoteChainSel)
return fmt.Errorf("onramp address not found for chain %d", remoteChainSel)
}
addressBytes := []byte(onRampAddress)
copy(onRampBytes[:], addressBytes)
Expand All @@ -226,18 +232,22 @@ func doAddRemoteChainToSolana(e deployment.Environment, s cs.CCIPOnChainState, c
).ValidateAndBuild()

if err != nil {
return deployment.ChangesetOutput{}, fmt.Errorf("failed to generate instructions: %w", err)
return fmt.Errorf("failed to generate instructions: %w", err)
}

err = chain.Confirm([]solana.Instruction{instruction})

if err != nil {
return deployment.ChangesetOutput{}, fmt.Errorf("failed to confirm instructions: %w", err)
return fmt.Errorf("failed to confirm instructions: %w", err)
}
e.Logger.Infow("Confirmed instruction", "instruction", instruction)

err = ab.Save(chainSel, cs.SerializeSolanaStateForAB(cs.RemoteChain, strconv.FormatUint(remoteChainSel, 10)), deployment.NewTypeAndVersion(cs.RemoteChain, deployment.Version1_0_0))
if err != nil {
return fmt.Errorf("failed to save source chain state to address book: %w", err)
}
}

return deployment.ChangesetOutput{}, nil
return nil
}

// SET OCR3 CONFIG
Expand Down Expand Up @@ -282,10 +292,8 @@ func SetOCR3ConfigSolana(e deployment.Environment, cfg cs.SetOCR3OffRampConfig)
// TODO: check if ocr3 has already been set
// set, err := isOCR3ConfigSetSolana(e.Logger, e.Chains[remote], state.Chains[remote].OffRamp, args)
var instructions []solana.Instruction
ccipRouterID := solChains[remote].Router
// addressing errcheck in the next PR
routerConfigPDA, _, _ := solState.FindConfigPDA(ccipRouterID)
routerStatePDA, _, _ := solState.FindStatePDA(ccipRouterID)
routerConfigPDA := solChains[remote].RouterConfigPDA
routerStatePDA := solChains[remote].RouterStatePDA
for _, arg := range args {
instruction, err := solRouter.NewSetOcrConfigInstruction(
arg.OCRPluginType,
Expand Down
7 changes: 3 additions & 4 deletions deployment/ccip/changeset/solana/cs_chain_contracts_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ func TestAddRemoteChain(t *testing.T) {
state, err := changeset.LoadOnchainState(tenv.Env)
require.NoError(t, err)

_, err = commonchangeset.ApplyChangesets(t, tenv.Env, nil, []commonchangeset.ChangesetApplication{
tenv.Env, err = commonchangeset.ApplyChangesets(t, tenv.Env, nil, []commonchangeset.ChangesetApplication{
{
Changeset: commonchangeset.WrapChangeSet(changeset.UpdateOnRampsDestsChangeset),
Config: changeset.UpdateOnRampDestsConfig{
Expand Down Expand Up @@ -80,16 +80,15 @@ func TestAddRemoteChain(t *testing.T) {

state, err = changeset.LoadOnchainStateSolana(tenv.Env)
require.NoError(t, err)

var sourceChainStateAccount solRouter.SourceChain
evmSourceChainStatePDA, _ := solState.FindSourceChainStatePDA(evmChain, state.SolChains[solChain].Router)
evmSourceChainStatePDA := state.SolChains[solChain].SourceChainStatePDAs[evmChain]
err = tenv.Env.SolChains[solChain].GetAccountDataBorshInto(ctx, evmSourceChainStatePDA, &sourceChainStateAccount)
require.NoError(t, err)
require.Equal(t, uint64(1), sourceChainStateAccount.State.MinSeqNr)
require.True(t, sourceChainStateAccount.Config.IsEnabled)

var destChainStateAccount solRouter.DestChain
evmDestChainStatePDA, _ := solState.FindDestChainStatePDA(evmChain, state.SolChains[solChain].Router)
evmDestChainStatePDA := state.SolChains[solChain].DestChainStatePDAs[evmChain]
err = tenv.Env.SolChains[solChain].GetAccountDataBorshInto(ctx, evmDestChainStatePDA, &destChainStateAccount)
require.NoError(t, err)
}
Expand Down
56 changes: 55 additions & 1 deletion deployment/ccip/changeset/solana_state.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,13 @@ package changeset
import (
"errors"
"fmt"
"strconv"
"strings"

"github.com/gagliardetto/solana-go"

solState "github.com/smartcontractkit/chainlink-ccip/chains/solana/utils/state"

"github.com/smartcontractkit/chainlink/deployment"
commontypes "github.com/smartcontractkit/chainlink/deployment/common/types"
)
Expand All @@ -16,6 +20,8 @@ var (
Receiver deployment.ContractType = "Receiver"
SPL2022Tokens deployment.ContractType = "SPL2022Tokens"
WSOL deployment.ContractType = "WSOL"
// for PDAs from AddRemoteChainToSolana
RemoteChain deployment.ContractType = "RemoteChain"
)

// SolChainState holds a Go binding for all the currently deployed CCIP programs
Expand All @@ -29,6 +35,11 @@ type SolCCIPChainState struct {
SPL2022Tokens []solana.PublicKey
TokenPool solana.PublicKey
WSOL solana.PublicKey
// PDAs to avoid redundant lookups
RouterStatePDA solana.PublicKey
RouterConfigPDA solana.PublicKey
SourceChainStatePDAs map[uint64]solana.PublicKey
DestChainStatePDAs map[uint64]solana.PublicKey
}

func LoadOnchainStateSolana(e deployment.Environment) (CCIPOnChainState, error) {
Expand All @@ -55,8 +66,12 @@ func LoadOnchainStateSolana(e deployment.Environment) (CCIPOnChainState, error)

// LoadChainStateSolana Loads all state for a SolChain into state
func LoadChainStateSolana(chain deployment.SolChain, addresses map[string]deployment.TypeAndVersion) (SolCCIPChainState, error) {
var state SolCCIPChainState
state := SolCCIPChainState{
SourceChainStatePDAs: make(map[uint64]solana.PublicKey),
DestChainStatePDAs: make(map[uint64]solana.PublicKey),
}
var spl2022Tokens []solana.PublicKey
var selectors []uint64
for address, tvStr := range addresses {
switch tvStr.String() {
case deployment.NewTypeAndVersion(commontypes.LinkToken, deployment.Version1_0_0).String():
Expand All @@ -65,6 +80,16 @@ func LoadChainStateSolana(chain deployment.SolChain, addresses map[string]deploy
case deployment.NewTypeAndVersion(Router, deployment.Version1_0_0).String():
pub := solana.MustPublicKeyFromBase58(address)
state.Router = pub
routerStatePDA, _, err := solState.FindStatePDA(state.Router)
if err != nil {
return state, err
}
state.RouterStatePDA = routerStatePDA
routerConfigPDA, _, err := solState.FindConfigPDA(state.Router)
if err != nil {
return state, err
}
state.RouterConfigPDA = routerConfigPDA
case deployment.NewTypeAndVersion(AddressLookupTable, deployment.Version1_0_0).String():
pub := solana.MustPublicKeyFromBase58(address)
state.AddressLookupTable = pub
Expand All @@ -77,11 +102,40 @@ func LoadChainStateSolana(chain deployment.SolChain, addresses map[string]deploy
case deployment.NewTypeAndVersion(TokenPool, deployment.Version1_0_0).String():
pub := solana.MustPublicKeyFromBase58(address)
state.TokenPool = pub
case deployment.NewTypeAndVersion(RemoteChain, deployment.Version1_0_0).String():
selStr, err := DeserializeSolanaStateFromAB(address)
if err != nil {
return state, err
}
selector, err := strconv.ParseUint(selStr, 10, 64)
if err != nil {
return state, err
}
selectors = append(selectors, selector)
default:
return state, fmt.Errorf("unknown contract %s", tvStr)
}
}
// store PDAs
for _, selector := range selectors {
// safe to ignore error here, as we know the contract is deployed
// Need to initialize PDAs after all contracts are loaded, so router is set
state.SourceChainStatePDAs[selector], _ = solState.FindSourceChainStatePDA(selector, state.Router)
state.DestChainStatePDAs[selector], _ = solState.FindDestChainStatePDA(selector, state.Router)
}
state.WSOL = solana.SolMint
state.SPL2022Tokens = spl2022Tokens
return state, nil
}

func SerializeSolanaStateForAB(contractType deployment.ContractType, suffix string) string {
return string(contractType) + "_" + suffix
}

func DeserializeSolanaStateFromAB(address string) (string, error) {
parts := strings.Split(address, "_")
if len(parts) != 2 {
return "", fmt.Errorf("invalid address type: %s", address)
}
return parts[1], nil
}
5 changes: 3 additions & 2 deletions deployment/ccip/changeset/testhelpers/test_helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -1383,14 +1383,15 @@ func ValidateSolanaState(t *testing.T, e deployment.Environment, solChainSelecto
// Validate addresses
require.False(t, chainState.LinkToken.IsZero(), "Link token address is zero for chain %d", sel)
require.False(t, chainState.Router.IsZero(), "Router address is zero for chain %d", sel)
require.False(t, chainState.RouterConfigPDA.IsZero(), "RouterConfigPDA is zero for chain %d", sel)
require.False(t, chainState.RouterStatePDA.IsZero(), "RouterStatePDA is zero for chain %d", sel)
require.False(t, chainState.AddressLookupTable.IsZero(), "Address lookup table is zero for chain %d", sel)

// Get router config
var routerConfigAccount solRouter.Config
configPDA, _, _ := solState.FindConfigPDA(chainState.Router)

// Check if account exists first
err = e.SolChains[sel].GetAccountDataBorshInto(testcontext.Get(t), configPDA, &routerConfigAccount)
err = e.SolChains[sel].GetAccountDataBorshInto(testcontext.Get(t), chainState.RouterConfigPDA, &routerConfigAccount)
require.NoError(t, err, "Failed to deserialize router config for chain %d", sel)
}
}
Expand Down

0 comments on commit d249150

Please sign in to comment.