Skip to content

Commit 2d0d0aa

Browse files
committed
Added codec to solana accessor to decode events
1 parent 5b7e8b3 commit 2d0d0aa

File tree

3 files changed

+58
-11
lines changed

3 files changed

+58
-11
lines changed

pkg/solana/ccip/chainaccessor/config.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import (
1717
"github.com/smartcontractkit/chainlink-ccip/pkg/consts"
1818
)
1919

20+
// https://github.com/smartcontractkit/chainlink-ccip/blob/7cae1b8434dd376eb70f2ddaace43093982f3a57/chains/solana/contracts/programs/rmn-remote/src/state.rs#L20-L27
2021
var globalCurseValue = []byte{1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}
2122

2223
// getOffRampStaticConfig retrieves static configuration for the off-ramp contract

pkg/solana/ccip/chainaccessor/event.go

Lines changed: 47 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package chainaccessor
33
import (
44
"context"
55
"crypto/sha3"
6+
"encoding/json"
67
"errors"
78
"fmt"
89
"math/big"
@@ -72,6 +73,7 @@ var (
7273
execStateChangedStatePath = consts.EventAttributeState
7374
)
7475

76+
// Map of relevant events and their metadata required to build the codec and bind program addresses
7577
var eventFilterConfigMap = map[string]filterConfig{
7678
consts.EventNameCCIPMessageSent: {
7779
idl: ccipRouterIDL,
@@ -126,6 +128,40 @@ func (a *SolanaAccessor) bindContractEvent(ctx context.Context, contractName str
126128
return nil
127129
}
128130

131+
func buildEventsCodec() (types.RemoteCodec, error) {
132+
parsed := &codec.ParsedTypes{
133+
DecoderDefs: make(map[string]codec.Entry),
134+
}
135+
136+
for eventName, eventCfg := range eventFilterConfigMap {
137+
var codecIDL codec.IDL
138+
if err := json.Unmarshal([]byte(eventCfg.idl), &codecIDL); err != nil {
139+
return nil, fmt.Errorf("unexpected error: invalid CCIP OffRamp IDL, error: %w", err)
140+
}
141+
142+
idlDef, err := codec.FindDefinitionFromIDL(codec.ChainConfigTypeInstructionDef, eventName, codecIDL)
143+
if err != nil {
144+
return nil, err
145+
}
146+
eventIdl, isOk := idlDef.(codec.IdlEvent)
147+
if !isOk {
148+
return nil, fmt.Errorf("unexpected type from IDL definition for event read: %q", eventName)
149+
}
150+
entry, err := codec.CreateCodecEntry(eventIdl, eventName, codecIDL, nil)
151+
if err != nil {
152+
return nil, fmt.Errorf("failed to create codec entry for event %s, error: %w", eventName, err)
153+
}
154+
parsed.DecoderDefs[eventName] = entry
155+
}
156+
157+
codec, err := parsed.ToCodec()
158+
if err != nil {
159+
return nil, fmt.Errorf("failed to build codec: %w", err)
160+
}
161+
162+
return codec, nil
163+
}
164+
129165
// registerFilterIfNotExists registers a filter for the given event if it doesn't already exist.
130166
func (a *SolanaAccessor) registerFilterIfNotExists(
131167
ctx context.Context,
@@ -173,13 +209,13 @@ func (a *SolanaAccessor) registerFilterIfNotExists(
173209
// chainaccessor.SendRequestedEvent. This function is idempotent and performs a
174210
// one-to-one mapping of event fields from the Solana format to the standard CCIP format.
175211
func (a *SolanaAccessor) convertCCIPMessageSent(ctx context.Context, logs []logpollertypes.Log, onrampAddr solana.PublicKey) ([]*chainaccessor.SendRequestedEvent, error) {
176-
iter, err := a.decodeLogsIntoSequences(ctx, logs, &ccip.EventCCIPMessageSent{})
212+
iter, err := a.decodeLogsIntoSequences(ctx, consts.EventNameCCIPMessageSent, logs, &ccip.EventCCIPMessageSent{})
177213
if err != nil {
178214
return nil, fmt.Errorf("failed to decode logs into sequences: %w", err)
179215
}
180216

181217
if len(logs) != len(iter) {
182-
return nil, fmt.Errorf("failed to convert all logs into generic ccip event, logs %d, events %s", len(logs), len(iter))
218+
return nil, fmt.Errorf("failed to convert all logs into generic ccip event, logs %d, events %d", len(logs), len(iter))
183219
}
184220

185221
genericEvents := make([]*chainaccessor.SendRequestedEvent, 0)
@@ -269,7 +305,7 @@ func processSubKeyPaths(cfg filterConfig) [][]string {
269305
func (a *SolanaAccessor) processCommitReports(
270306
ctx context.Context, logs []logpollertypes.Log, ts time.Time, limit int,
271307
) ([]ccipocr3.CommitPluginReportWithMeta, error) {
272-
iter, err := a.decodeLogsIntoSequences(ctx, logs, &ccip.EventCommitReportAccepted{})
308+
iter, err := a.decodeLogsIntoSequences(ctx, consts.EventNameCommitReportAccepted, logs, &ccip.EventCommitReportAccepted{})
273309
if err != nil {
274310
return nil, fmt.Errorf("failed to decode logs into sequences: %w", err)
275311
}
@@ -490,7 +526,7 @@ func createExecutedMessagesKeyFilter(rangesPerChain map[ccipocr3.ChainSelector][
490526
}
491527

492528
func (a *SolanaAccessor) processExecutionStateChangesEvents(ctx context.Context, logs []logpollertypes.Log, nonEmptyRangesPerChain map[ccipocr3.ChainSelector][]ccipocr3.SeqNumRange) (map[ccipocr3.ChainSelector][]ccipocr3.SeqNum, error) {
493-
iter, err := a.decodeLogsIntoSequences(ctx, logs, &ccip.EventExecutionStateChanged{})
529+
iter, err := a.decodeLogsIntoSequences(ctx, consts.EventNameExecutionStateChanged, logs, &ccip.EventExecutionStateChanged{})
494530
if err != nil {
495531
return nil, fmt.Errorf("failed to decode logs into sequences: %w", err)
496532
}
@@ -545,6 +581,7 @@ func validateExecutionStateChangedEvent(
545581

546582
func (a *SolanaAccessor) decodeLogsIntoSequences(
547583
ctx context.Context,
584+
eventName string,
548585
logs []logpollertypes.Log,
549586
into any,
550587
) ([]types.Sequence, error) {
@@ -572,15 +609,17 @@ func (a *SolanaAccessor) decodeLogsIntoSequences(
572609
// create a new value of the same type as 'into' for the data to be extracted to
573610
sequences[idx].Data = typeVal.Interface()
574611

575-
if err := a.decodeLog(ctx, &logs[idx], sequences[idx].Data); err != nil {
612+
if err := a.decodeLog(ctx, eventName, logs[idx], sequences[idx].Data); err != nil {
576613
return nil, err
577614
}
578615
}
579616

580617
return sequences, nil
581618
}
582619

583-
func (a *SolanaAccessor) decodeLog(ctx context.Context, log *logpollertypes.Log, into any) error {
584-
// TODO: add decoding logic
585-
return errors.New("not implemented")
620+
func (a *SolanaAccessor) decodeLog(ctx context.Context, eventName string, log logpollertypes.Log, into any) error {
621+
if err := a.codec.Decode(ctx, log.Data, into, eventName); err != nil {
622+
return fmt.Errorf("%w: failed to decode log data for event %s: %s", types.ErrInvalidType, eventName, err.Error())
623+
}
624+
return nil
586625
}

pkg/solana/ccip/chainaccessor/solana_accessor.go

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import (
1111
"github.com/gagliardetto/solana-go"
1212

1313
"github.com/smartcontractkit/chainlink-common/pkg/logger"
14+
"github.com/smartcontractkit/chainlink-common/pkg/types"
1415
"github.com/smartcontractkit/chainlink-common/pkg/types/ccipocr3"
1516
"github.com/smartcontractkit/chainlink-common/pkg/types/query"
1617
"github.com/smartcontractkit/chainlink-common/pkg/types/query/primitives"
@@ -31,11 +32,9 @@ import (
3132
var ErrNoBindings = errors.New("no bindings found")
3233

3334
type AccessorLogPoller interface {
34-
Start(ctx context.Context) error
3535
Ready() error
3636
HasFilter(context.Context, string) bool
3737
RegisterFilter(context.Context, logpollertypes.Filter) error
38-
UnregisterFilter(ctx context.Context, name string) error
3938
FilteredLogs(context.Context, []query.Expression, query.LimitAndSort, string) ([]logpollertypes.Log, error)
4039
}
4140

@@ -50,6 +49,7 @@ type SolanaAccessor struct {
5049
bindingsMu sync.RWMutex
5150
addrCodec ccipocr3.ChainSpecificAddressCodec
5251
fee fees.Estimator
52+
codec types.RemoteCodec
5353
}
5454

5555
var _ ccipocr3.ChainAccessor = (*SolanaAccessor)(nil)
@@ -61,9 +61,15 @@ func NewSolanaAccessor(
6161
logPoller AccessorLogPoller,
6262
fee fees.Estimator,
6363
addrCodec ccipocr3.ChainSpecificAddressCodec,
64-
) (ccipocr3.ChainAccessor, error) {
64+
) (*SolanaAccessor, error) {
6565
// TODO: validate state of client and logPoller (should be initialized in NewChain)
6666
lggr := logger.Named(l, "SolanaAccessor")
67+
68+
codec, err := buildEventsCodec()
69+
if err != nil {
70+
return nil, fmt.Errorf("failed to build codec parsed types: %w", err)
71+
}
72+
6773
return &SolanaAccessor{
6874
lggr: lggr,
6975
chainSelector: chainSelector,
@@ -73,6 +79,7 @@ func NewSolanaAccessor(
7379
bindingsMu: sync.RWMutex{},
7480
fee: fee,
7581
addrCodec: addrCodec,
82+
codec: codec,
7683
}, nil
7784
}
7885

0 commit comments

Comments
 (0)