Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Initialize nonce manager. #77

Merged
merged 6 commits into from
Aug 28, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
134 changes: 128 additions & 6 deletions internal/reader/ccip.go
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down Expand Up @@ -485,14 +526,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 {
Expand All @@ -505,7 +549,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.
Expand All @@ -523,10 +567,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 offramp static config: %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 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
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) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@dimkouv why the (bool, error) return signature? I feel like error is enough?

err := r.bindOnramps(ctx)
if err != nil {
return false, err
}

err = r.bindNonceManager(ctx)
if err != nil {
return false, err
}

return true, nil
}

Expand Down Expand Up @@ -579,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
Expand All @@ -605,5 +695,37 @@ 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
}

// 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 {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you put link to the offramp type? Will make it easier to find it.

Copy link
Contributor Author

@winder winder Aug 26, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, what did you have in mind? A comment? I copied the pattern used for type sourceChainConfig struct {, so I'll update that as well.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A permalink to the solidity struct would be good enough IMO

ChainSelector uint64 `json:"chainSelector"`
RmnProxy []byte `json:"rmnProxy"`
TokenAdminRegistry []byte `json:"tokenAdminRegistry"`
NonceManager []byte `json:"nonceManager"`
Comment on lines +724 to +727
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

json tags should not be required

}

// Interface compliance check
var _ CCIP = (*CCIPChainReader)(nil)
7 changes: 6 additions & 1 deletion pkg/consts/consts.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ const (
ContractNameCapabilitiesRegistry = "CapabilitiesRegistry"
ContractNameCCIPConfig = "CCIPConfig"
ContractNamePriceAggregator = "AggregatorV3Interface"
ContractNameNonceManager = "NonceManager"
)

// Method Names
Expand All @@ -35,14 +36,18 @@ const (
MethodNameGetPremiumMultiplierWeiPerEth = "GetPremiumMultiplierWeiPerEth"
MethodNameGetTokenTransferFeeConfig = "GetTokenTransferFeeConfig"
MethodNameProcessMessageArgs = "ProcessMessageArgs"
MethodNameValidatePoolReturnData = "ValidatePoolReturnData"
MethodNameProcessPoolReturnData = "ProcessPoolReturnData"
MethodNameGetValidatedTokenPrice = "GetValidatedTokenPrice"
MethodNameGetFeeTokens = "GetFeeTokens"

// Aggregator methods
MethodNameGetLatestRoundData = "latestRoundData"
MethodNameGetDecimals = "decimals"

// NonceManager methods
MethodNameGetInboundNonce = "GetInboundNonce"
MethodNameGetOutboundNonce = "GetOutboundNonce"

/*
// On EVM:
function commit(
Expand Down
Loading