From 0ba6e5fdfcc17a003830efdbd57ba3185c844cc7 Mon Sep 17 00:00:00 2001 From: Will Winder Date: Fri, 23 Aug 2024 15:40:59 -0400 Subject: [PATCH 1/6] Initialize nonce manager. --- internal/reader/ccip.go | 81 +++++++++++++++++++++++++++++++++++++++-- pkg/consts/consts.go | 5 +++ 2 files changed, 82 insertions(+), 4 deletions(-) diff --git a/internal/reader/ccip.go b/internal/reader/ccip.go index ade05ccd0..a5da06fda 100644 --- a/internal/reader/ccip.go +++ b/internal/reader/ccip.go @@ -485,14 +485,17 @@ func (r *CCIPChainReader) GasPrices(ctx context.Context, chains []cciptypes.Chai return gasPrices, nil } -func (r *CCIPChainReader) Sync(ctx context.Context) (bool, error) { +// bindOnRamps reads the onchain configuration to discover source ramp addresses. +func (r *CCIPChainReader) bindOnramps( + ctx context.Context, +) error { chains := make([]cciptypes.ChainSelector, 0, len(r.contractReaders)) for chain := range r.contractReaders { chains = append(chains, chain) } sourceConfigs, err := r.getSourceChainsConfig(ctx, chains) if err != nil { - return false, fmt.Errorf("get onramps: %w", err) + return fmt.Errorf("get onramps: %w", err) } r.lggr.Infow("got source chain configs", "onramps", func() []string { @@ -505,7 +508,7 @@ func (r *CCIPChainReader) Sync(ctx context.Context) (bool, error) { for chain, cfg := range sourceConfigs { if len(cfg.OnRamp) == 0 { - return false, fmt.Errorf("onRamp address not found for chain %d", chain) + return fmt.Errorf("onRamp address not found for chain %d", chain) } // We only want to produce reports for enabled source chains. @@ -523,10 +526,52 @@ func (r *CCIPChainReader) Sync(ctx context.Context) (bool, error) { Name: consts.ContractNameOnRamp, }, }); err != nil { - return false, fmt.Errorf("bind onRamp: %w", err) + return fmt.Errorf("bind onRamp: %w", err) } } + return nil +} + +func (r *CCIPChainReader) bindNonceManager(ctx context.Context) error { + staticConfig, err := r.getOfframpStaticConfig(ctx) + if err != nil { + return fmt.Errorf("get onramps: %w", err) + } + + if _, ok := r.contractReaders[r.destChain]; !ok { + r.lggr.Debugw("skipping nonce manager, dest chain not configured for this deployment", + "destChain", r.destChain) + return nil + } + + // Bind the onRamp contract address to the reader. + // If the same address exists -> no-op + // If the address is changed -> updates the address, overwrites the existing one + // If the contract not binded -> binds to the new address + if err := r.contractReaders[r.destChain].Bind(ctx, []types.BoundContract{ + { + Address: typeconv.AddressBytesToString(staticConfig.nonceManager, uint64(r.destChain)), + Name: consts.ContractNameNonceManager, + }, + }); err != nil { + return fmt.Errorf("bind nonce manager: %w", err) + } + + return nil +} + +func (r *CCIPChainReader) Sync(ctx context.Context) (bool, error) { + err := r.bindOnramps(ctx) + if err != nil { + return false, err + } + + err = r.bindNonceManager(ctx) + if err != nil { + return false, err + } + return true, nil } @@ -605,5 +650,33 @@ func (r *CCIPChainReader) validateWriterExistence(chains ...cciptypes.ChainSelec return nil } +// getSourceChainsConfig returns the destination offRamp contract's static chain configuration. +func (r *CCIPChainReader) getOfframpStaticConfig(ctx context.Context) (offrampStaticChainConfig, error) { + if err := r.validateReaderExistence(r.destChain); err != nil { + return offrampStaticChainConfig{}, err + } + + resp := offrampStaticChainConfig{} + err := r.contractReaders[r.destChain].GetLatestValue( + ctx, + consts.ContractNameOffRamp, + consts.MethodNameOfframpGetStaticConfig, + primitives.Unconfirmed, + map[string]any{}, + &resp, + ) + if err != nil { + return offrampStaticChainConfig{}, fmt.Errorf("failed to get source chain config: %w", err) + } + return resp, nil +} + +type offrampStaticChainConfig struct { + chainSelector uint64 `json:"chainSelector"` + rmnProxy []byte `json:"rmnProxy"` + tokenAdminRegistry []byte `json:"tokenAdminRegistry"` + nonceManager []byte `json:"nonceManager"` +} + // Interface compliance check var _ CCIP = (*CCIPChainReader)(nil) diff --git a/pkg/consts/consts.go b/pkg/consts/consts.go index 19a8f8c2f..dead94d33 100644 --- a/pkg/consts/consts.go +++ b/pkg/consts/consts.go @@ -10,6 +10,7 @@ const ( ContractNameCapabilitiesRegistry = "CapabilitiesRegistry" ContractNameCCIPConfig = "CCIPConfig" ContractNamePriceAggregator = "AggregatorV3Interface" + ContractNameNonceManager = "NonceManager" ) // Method Names @@ -43,6 +44,10 @@ const ( MethodNameGetLatestRoundData = "latestRoundData" MethodNameGetDecimals = "decimals" + // NonceManager methods + MethodNameGetInboundNonce = "GetInboundNonce" + MethodNameGetOutboundNonce = "GetOutboundNonce" + /* // On EVM: function commit( From fd2b994023161c9ebf234bd2bd8eb89c8f9ccfec Mon Sep 17 00:00:00 2001 From: Will Winder Date: Fri, 23 Aug 2024 15:52:56 -0400 Subject: [PATCH 2/6] Make struct tagged variables public. --- internal/reader/ccip.go | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/internal/reader/ccip.go b/internal/reader/ccip.go index a5da06fda..fa749e77a 100644 --- a/internal/reader/ccip.go +++ b/internal/reader/ccip.go @@ -551,7 +551,7 @@ func (r *CCIPChainReader) bindNonceManager(ctx context.Context) error { // If the contract not binded -> binds to the new address if err := r.contractReaders[r.destChain].Bind(ctx, []types.BoundContract{ { - Address: typeconv.AddressBytesToString(staticConfig.nonceManager, uint64(r.destChain)), + Address: typeconv.AddressBytesToString(staticConfig.NonceManager, uint64(r.destChain)), Name: consts.ContractNameNonceManager, }, }); err != nil { @@ -672,10 +672,10 @@ func (r *CCIPChainReader) getOfframpStaticConfig(ctx context.Context) (offrampSt } type offrampStaticChainConfig struct { - chainSelector uint64 `json:"chainSelector"` - rmnProxy []byte `json:"rmnProxy"` - tokenAdminRegistry []byte `json:"tokenAdminRegistry"` - nonceManager []byte `json:"nonceManager"` + ChainSelector uint64 `json:"chainSelector"` + RmnProxy []byte `json:"rmnProxy"` + TokenAdminRegistry []byte `json:"tokenAdminRegistry"` + NonceManager []byte `json:"nonceManager"` } // Interface compliance check From 8aea87c6ae072154244f6aa307eb7888ebd2879f Mon Sep 17 00:00:00 2001 From: Will Winder Date: Wed, 28 Aug 2024 09:51:49 -0400 Subject: [PATCH 3/6] Merge error. --- pkg/consts/consts.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/consts/consts.go b/pkg/consts/consts.go index dead94d33..91bc53b19 100644 --- a/pkg/consts/consts.go +++ b/pkg/consts/consts.go @@ -36,7 +36,7 @@ const ( MethodNameGetPremiumMultiplierWeiPerEth = "GetPremiumMultiplierWeiPerEth" MethodNameGetTokenTransferFeeConfig = "GetTokenTransferFeeConfig" MethodNameProcessMessageArgs = "ProcessMessageArgs" - MethodNameValidatePoolReturnData = "ValidatePoolReturnData" + MethodNameProcessPoolReturnData = "ProcessPoolReturnData" MethodNameGetValidatedTokenPrice = "GetValidatedTokenPrice" MethodNameGetFeeTokens = "GetFeeTokens" From 207ea302722481bffe807795353236675732f488 Mon Sep 17 00:00:00 2001 From: Will Winder Date: Wed, 28 Aug 2024 09:52:01 -0400 Subject: [PATCH 4/6] Implement nonces. --- internal/reader/ccip.go | 45 +++++++++++++++++++++++++++++++++++++++-- 1 file changed, 43 insertions(+), 2 deletions(-) diff --git a/internal/reader/ccip.go b/internal/reader/ccip.go index fa749e77a..6db7629ef 100644 --- a/internal/reader/ccip.go +++ b/internal/reader/ccip.go @@ -454,10 +454,51 @@ func (r *CCIPChainReader) NextSeqNum( func (r *CCIPChainReader) Nonces( ctx context.Context, - source, dest cciptypes.ChainSelector, + sourceChainSelector, destChainSelector cciptypes.ChainSelector, addresses []string, ) (map[string]uint64, error) { - return nil, fmt.Errorf("implement me") + if err := r.validateReaderExistence(destChainSelector); err != nil { + return nil, err + } + + res := make(map[string]uint64) + mu := new(sync.Mutex) + eg := new(errgroup.Group) + + for _, address := range addresses { + address := address + eg.Go(func() error { + sender, err := typeconv.AddressStringToBytes(address, uint64(destChainSelector)) + if err != nil { + return fmt.Errorf("failed to convert address %s to bytes: %w", address, err) + } + + var resp uint64 + err = r.contractReaders[destChainSelector].GetLatestValue( + ctx, + consts.ContractNameNonceManager, + consts.MethodNameGetInboundNonce, + primitives.Unconfirmed, + map[string]any{ + "sourceChainSelector": sourceChainSelector, + "sender": sender, + }, + &resp, + ) + if err != nil { + return fmt.Errorf("failed to get nonce for address %s: %w", address, err) + } + mu.Lock() + defer mu.Unlock() + res[address] = resp + return nil + }) + } + + if err := eg.Wait(); err != nil { + return nil, err + } + return res, nil } func (r *CCIPChainReader) GasPrices(ctx context.Context, chains []cciptypes.ChainSelector) ([]cciptypes.BigInt, error) { From ce4325e8b307590ffd7d213600387ce92f87db64 Mon Sep 17 00:00:00 2001 From: Will Winder Date: Wed, 28 Aug 2024 11:21:16 -0400 Subject: [PATCH 5/6] Apply suggestions from code review Co-authored-by: Makram Co-authored-by: Abdelrahman Soliman (Boda) <2677789+asoliman92@users.noreply.github.com> --- internal/reader/ccip.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/internal/reader/ccip.go b/internal/reader/ccip.go index 6db7629ef..25216e121 100644 --- a/internal/reader/ccip.go +++ b/internal/reader/ccip.go @@ -577,7 +577,7 @@ func (r *CCIPChainReader) bindOnramps( func (r *CCIPChainReader) bindNonceManager(ctx context.Context) error { staticConfig, err := r.getOfframpStaticConfig(ctx) if err != nil { - return fmt.Errorf("get onramps: %w", err) + return fmt.Errorf("get offramp static config: %w", err) } if _, ok := r.contractReaders[r.destChain]; !ok { @@ -586,7 +586,7 @@ func (r *CCIPChainReader) bindNonceManager(ctx context.Context) error { return nil } - // Bind the onRamp contract address to the reader. + // Bind the nonceManager contract address to the reader. // If the same address exists -> no-op // If the address is changed -> updates the address, overwrites the existing one // If the contract not binded -> binds to the new address From 2bbfcbee14500484a43ce564d5a3d542b5b1fa11 Mon Sep 17 00:00:00 2001 From: Will Winder Date: Wed, 28 Aug 2024 13:00:39 -0400 Subject: [PATCH 6/6] Link to relevant struct definition. --- internal/reader/ccip.go | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/internal/reader/ccip.go b/internal/reader/ccip.go index 25216e121..50e6f48b0 100644 --- a/internal/reader/ccip.go +++ b/internal/reader/ccip.go @@ -665,6 +665,10 @@ func (r *CCIPChainReader) getSourceChainsConfig( return res, nil } +// sourceChainConfig is used to parse the response from the offRamp contract's getSourceChainConfig method. +// See: https://github.com/smartcontractkit/ccip/blob/a3f61f7458e4499c2c62eb38581c60b4942b1160/contracts/src/v0.8/ccip/offRamp/OffRamp.sol#L94 +// +//nolint:lll // It's a URL. type sourceChainConfig struct { IsEnabled bool OnRamp []byte @@ -712,6 +716,10 @@ func (r *CCIPChainReader) getOfframpStaticConfig(ctx context.Context) (offrampSt return resp, nil } +// offrampStaticChainConfig is used to parse the response from the offRamp contract's getStaticConfig method. +// See: https://github.com/smartcontractkit/ccip/blob/a3f61f7458e4499c2c62eb38581c60b4942b1160/contracts/src/v0.8/ccip/offRamp/OffRamp.sol#L86 +// +//nolint:lll // It's a URL. type offrampStaticChainConfig struct { ChainSelector uint64 `json:"chainSelector"` RmnProxy []byte `json:"rmnProxy"`