Skip to content
This repository was archived by the owner on Feb 8, 2026. It is now read-only.

🚚 Latest type imports, fixed names and policy version#93

Merged
johnnyluo merged 4 commits into
mainfrom
plugin-policy-breaking-changes-fix
Jun 12, 2025
Merged

🚚 Latest type imports, fixed names and policy version#93
johnnyluo merged 4 commits into
mainfrom
plugin-policy-breaking-changes-fix

Conversation

@garry-sharp

@garry-sharp garry-sharp commented Jun 12, 2025

Copy link
Copy Markdown
Contributor

Fixes breaking changes introduced in verifier

Summary by CodeRabbit

  • Refactor
    • Updated policy version handling to use a numeric value instead of a string across policy creation, validation, and transaction proposal features.
    • Standardized method signatures in plugins to accept the updated policy type for improved consistency.
  • Chores
    • Upgraded dependency versions for improved stability and compatibility.
  • Tests
    • Adjusted test cases to align with the new numeric policy version format.
  • New Features
    • Introduced a new fees plugin with configuration loading, Ethereum RPC integration, and basic plugin interface implementation.

@garry-sharp
garry-sharp requested review from RaghavSood and Copilot and removed request for Copilot June 12, 2025 17:57
@coderabbitai

coderabbitai Bot commented Jun 12, 2025

Copy link
Copy Markdown
Contributor

Walkthrough

The changes standardize the use of the vtypes.PluginPolicy type in place of vtypes.PluginPolicyCreateUpdate across various plugin methods and scripts. The PolicyVersion field is updated from a string to an integer in struct initializations and comparisons, reflecting a shift to treating it as a numeric nonce rather than a semantic version string. Dependency versions in go.mod are also updated. Additionally, a new fees plugin is introduced with configuration loading, Ethereum RPC client integration, and stubbed interface methods.

Changes

File(s) Change Summary
go.mod Updated versions of github.com/vultisig/recipes and github.com/vultisig/verifier dependencies.
plugin/dca/dca.go Replaced PluginPolicyCreateUpdate with PluginPolicy in method signatures; changed PolicyVersion comparison to integer-based.
plugin/payroll/policy.go
plugin/payroll/transaction.go
Updated method signatures to use PluginPolicy instead of PluginPolicyCreateUpdate.
scripts/dev/create_dca_policy/main.go
scripts/dev/create_payroll_policy/main.go
Changed PolicyVersion initialization from string to integer in policy struct creation.
storage/postgres/time_trigger_test.go Changed PolicyVersion field in test policy from string "1" to integer 1.
api/plugin.go Updated API handlers and validation to use PluginPolicy instead of PluginPolicyCreateUpdate; formatted PolicyVersion as string in message hex.
plugin/fees/config.go Added PluginConfig struct and loadPluginConfig function for fees plugin configuration loading and validation.
plugin/fees/constraints.go Added constants for fees plugin type and ERC20 transfer ABI JSON string.
plugin/fees/fees.go Added new FeePlugin struct implementing plugin interface with config loading, RPC client setup, and stubbed methods.

Sequence Diagram(s)

sequenceDiagram
    participant Caller
    participant Plugin (DCA/Payroll)
    Note over Plugin: All methods now accept PluginPolicy (int PolicyVersion)
    Caller->>Plugin: ProposeTransactions(policy: PluginPolicy)
    Plugin-->>Caller: []PluginKeysignRequest, error

    Caller->>Plugin: ValidatePluginPolicy(policyDoc: PluginPolicy)
    Plugin-->>Caller: error

    Caller->>Plugin: ValidateProposedTransactions(policy: PluginPolicy, txs)
    Plugin-->>Caller: error

    Caller->>Plugin: SigningComplete(ctx, signature, signRequest, policy: PluginPolicy)
    Plugin-->>Caller: error
Loading

Possibly related PRs

Suggested reviewers

  • RaghavSood
  • johnnyluo

Poem

A hop, a skip, a version leap,
From string to int, our policies keep.
Plugins align, their types now neat,
Dependencies fresh, the update’s complete!
With every change, the garden grows—
A bunny’s code, in tidy rows.
🐇✨

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 golangci-lint (1.64.8)

level=warning msg="[runner] Can't run linter goanalysis_metalinter: buildir: failed to load package session: could not load export data: no export data for "github.com/vultisig/go-wrappers/go-dkls/sessions""
level=error msg="Running error: can't run linter goanalysis_metalinter\nbuildir: failed to load package session: could not load export data: no export data for "github.com/vultisig/go-wrappers/go-dkls/sessions""

✨ Finishing Touches
  • 📝 Generate Docstrings

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Explain this complex logic.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai explain this code block.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and explain its main purpose.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@garry-sharp
garry-sharp requested a review from johnnyluo June 12, 2025 17:58

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull Request Overview

This PR updates type imports, fixes naming, and adjusts policy version handling to address breaking changes introduced in verifier.

  • Changes PolicyVersion fields from string to int in tests and policy creation scripts
  • Updates function signatures to use PluginPolicy instead of PluginPolicyCreateUpdate in payroll and DCA plugins
  • Updates dependency versions in go.mod to align with the new verifier changes

Reviewed Changes

Copilot reviewed 8 out of 8 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
storage/postgres/time_trigger_test.go Updated PolicyVersion type from string to int for test data
scripts/dev/create_payroll_policy/main.go Updated PolicyVersion type from string to int in payroll policy creation
scripts/dev/create_dca_policy/main.go Updated PolicyVersion type from string to int in DCA policy creation
plugin/payroll/transaction.go Changed function signatures to use PluginPolicy, removing ToPluginPolicy calls
plugin/payroll/policy.go Updated validation functions to use PluginPolicy
plugin/dca/dca.go Updated function signatures and adjusted PolicyVersion handling logic
go.mod Upgraded dependency versions to support the breaking changes

Comment thread plugin/dca/dca.go Outdated
Comment on lines 147 to 148
if fmt.Sprintf("%d", policyDoc.PolicyVersion) != policyVersion { //TODO policy version will essentially be a nonce that is incremented by 1, not, like plugin versions that will follow traditional engineering versioning e.g. v2.12.3. For now we can compare string types
return fmt.Errorf("policy does not match policy version, expected: %s, got: %s", policyVersion, policyDoc.PolicyVersion)

Copilot AI Jun 12, 2025

Copy link

Choose a reason for hiding this comment

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

[nitpick] Consider revising the conversion and comparison logic for PolicyVersion. If possible, compare integers directly or refactor the code to use a consistent type, rather than converting the integer to a string.

Suggested change
if fmt.Sprintf("%d", policyDoc.PolicyVersion) != policyVersion { //TODO policy version will essentially be a nonce that is incremented by 1, not, like plugin versions that will follow traditional engineering versioning e.g. v2.12.3. For now we can compare string types
return fmt.Errorf("policy does not match policy version, expected: %s, got: %s", policyVersion, policyDoc.PolicyVersion)
if policyDoc.PolicyVersion != policyVersion { // Policy version is now compared as an integer
return fmt.Errorf("policy does not match policy version, expected: %d, got: %d", policyVersion, policyDoc.PolicyVersion)

Copilot uses AI. Check for mistakes.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🔭 Outside diff range comments (7)
scripts/dev/create_dca_policy/main.go (1)

72-82: ⚠️ Potential issue

Missing Recipe assignment will break policy validation

policy.Recipe is still the empty string. Both the verifier and the plugin decode & validate this field – requests will immediately fail with “failed to decode policy recipe”.

@@
 	policy := vtypes.PluginPolicy{
 		ID:            policyId,
 		PublicKey:     key,
 		PluginID:      vtypes.PluginVultisigDCA_0000,
 		PluginVersion: "1.0.0",
-		PolicyVersion: 1,
+		PolicyVersion: 1,
 		Active:        true,
 		Signature:     "0x0000000000000000000000000000000000000000000000000000000000000000",
 	}
 
@@
 	fmt.Println("DCA policy", string(policyBytes))
+
+	// Attach recipe to policy (base64-url w/o padding, which verifier expects)
+	policy.Recipe = base64.RawStdEncoding.EncodeToString(policyBytes)
+
+	// missing import
+	// import "encoding/base64"
scripts/dev/create_payroll_policy/main.go (1)

96-105: ⚠️ Potential issue

Recipe not populated – policy creation will fail

Same issue as in the DCA helper: policy.Recipe is never set. The verifier rejects policies without a decodable recipe.

@@
 	policy := vtypes.PluginPolicy{
 		ID:            policyId,
 		PublicKey:     key,
 		PluginID:      vtypes.PluginVultisigPayroll_0000,
 		PluginVersion: "1.0.0",
-		PolicyVersion: 1,
+		PolicyVersion: 1,
 		Active:        true,
 		Signature:     "0x0000000000000000000000000000000000000000000000000000000000000000",
 	}
@@
 	fmt.Println("Payroll policy", string(policyBytes))
-	// policy.Policy = policyBytes
+
+	// Attach recipe in the format expected by verifier
+	policy.Recipe = base64.RawStdEncoding.EncodeToString(policyBytes)
+
+	// add import
+	// import "encoding/base64"
plugin/payroll/policy.go (1)

40-105: ⚠️ Potential issue

payrollPolicy never initialised – will panic on first Tx

payrollPolicy is declared but never populated; any access to payrollPolicy.TokenID[i] or Recipients causes an index-out-of-range or nil-value error as soon as len(txs) > 0.

Proposed quick fix: parse the recipe into the strongly-typed struct before validating transactions.

-	var payrollPolicy PayrollPolicy
-	// TODO: convert the recipes to PayrollPolicy
+	var payrollPolicy PayrollPolicy
+	// Decode base64-encoded JSON recipe
+	policyBytes, err := base64.RawStdEncoding.DecodeString(policy.Recipe)
+	if err != nil {
+		return fmt.Errorf("failed to decode policy recipe: %w", err)
+	}
+	if err := json.Unmarshal(policyBytes, &payrollPolicy); err != nil {
+		return fmt.Errorf("failed to unmarshal payroll policy: %w", err)
+	}
+
+	// ensure the recipe length matches the number of proposed txs
+	if len(payrollPolicy.TokenID) != len(txs) {
+		return fmt.Errorf("recipe contains %d token IDs but %d transactions supplied", len(payrollPolicy.TokenID), len(txs))
+	}

Add missing import:

import (
	"encoding/base64"
	"encoding/json"
	// existing imports…
)

Without this fix the method will compile but always fail at runtime.

plugin/payroll/transaction.go (3)

61-80: ⚠️ Potential issue

payrollPolicy is never initialised – all subsequent field access is undefined

var payrollPolicy PayrollPolicy leaves every slice (Recipients, ChainID, TokenID) nil / empty.
The for loop therefore produces zero transactions, but the function later blindly indexes txs[0], causing a panic.

Either:

  1. Populate payrollPolicy from the recipe before use, or
  2. Return an explicit error when the conversion is still TODO.
- var payrollPolicy PayrollPolicy
- // TODO: convert the recipes to PayrollPolicy
+ payrollPolicy, err := convertRecipeToPayrollPolicy(policy) // helper to be implemented
+ if err != nil {
+     return nil, fmt.Errorf("failed to build payroll policy: %w", err)
+ }

103-110: ⚠️ Potential issue

Potential panic: dereferencing txs[0] without verifying length

If no transactions were appended (see previous comment), txs[0] triggers an index out of range panic.

-if len(txs) == 0 {
-    return nil, fmt.Errorf("no transactions generated")
-}
-signRequest := txs[0]
+if len(txs) == 0 {
+    return nil, fmt.Errorf("no transactions generated")
+}
+signRequest := txs[0]

305-309: ⚠️ Potential issue

Uninitialised payrollPolicy.ChainID[0] leads to slice-bounds panic

payrollPolicy is still empty inside convertData; indexing [0] will panic.
Retrieve the chain-ID directly from originalTx (already available) or ensure the policy is properly converted before calling this helper.

- chainID = new(big.Int)
- chainID.SetString(payrollPolicy.ChainID[0], 10)
+chainID = originalTx.ChainId()
plugin/dca/dca.go (1)

143-149: ⚠️ Potential issue

Version checks compare incompatible types – will always fail

pluginVersion / policyVersion are string constants "0.0.1", whereas policyDoc.PluginVersion and .PolicyVersion have been migrated to numeric fields (int32 / int).
Current comparisons will never pass, causing every policy to be rejected.

- if policyDoc.PluginVersion != pluginVersion {
+ if fmt.Sprintf("%d", policyDoc.PluginVersion) != pluginVersion { // temporary workaround

…still mismatches. Prefer comparing integers and remove the semantic-version strings altogether:

-const (
-    pluginVersion = "0.0.1"
-    policyVersion = "0.0.1"
-)
+const (
+    pluginVersion = 1  // numeric
+    policyVersion = 1
+)
...
- if policyDoc.PluginVersion != pluginVersion {
+ if policyDoc.PluginVersion != pluginVersion {
...
- if fmt.Sprintf("%d", policyDoc.PolicyVersion) != policyVersion {
+ if policyDoc.PolicyVersion != policyVersion {
🧹 Nitpick comments (2)
plugin/payroll/transaction.go (1)

55-61: *Pass vtypes.PluginPolicy by pointer to avoid an unnecessary copy

PluginPolicy is not a tiny struct (it embeds recipes, schedules, etc.).
Passing it by value here copies the entire struct every call, whereas every use inside the method is read-only. Consider switching to a pointer (as was done for other heavy params such as context.Context).

-func (p *PayrollPlugin) ProposeTransactions(policy vtypes.PluginPolicy) ([]vtypes.PluginKeysignRequest, error) {
+func (p *PayrollPlugin) ProposeTransactions(policy *vtypes.PluginPolicy) ([]vtypes.PluginKeysignRequest, error) {
plugin/dca/dca.go (1)

273-309: completePolicy mutates a copy — changes are lost

policy is passed by value to ProposeTransactions; completePolicy receives that copy.
Inside completePolicy the struct’s Active field is flipped to false, but once the function returns the outer copy remains unchanged.

Either:

  1. Accept *vtypes.PluginPolicy everywhere, or
  2. Return the updated struct and persist it in the caller.

Tagging as lower priority since the DB update does persist the change, but the in-memory copy may later be reused with stale state.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ad8b277 and d516d54.

⛔ Files ignored due to path filters (1)
  • go.sum is excluded by !**/*.sum
📒 Files selected for processing (7)
  • go.mod (1 hunks)
  • plugin/dca/dca.go (7 hunks)
  • plugin/payroll/policy.go (2 hunks)
  • plugin/payroll/transaction.go (2 hunks)
  • scripts/dev/create_dca_policy/main.go (1 hunks)
  • scripts/dev/create_payroll_policy/main.go (1 hunks)
  • storage/postgres/time_trigger_test.go (1 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (3)
plugin/payroll/policy.go (2)
plugin/payroll/payroll.go (1)
  • PayrollPlugin (15-21)
internal/types/keysign.go (1)
  • PluginKeysignRequest (7-12)
plugin/payroll/transaction.go (2)
plugin/payroll/payroll.go (1)
  • PayrollPlugin (15-21)
internal/types/keysign.go (1)
  • PluginKeysignRequest (7-12)
plugin/dca/dca.go (1)
internal/types/keysign.go (1)
  • PluginKeysignRequest (7-12)
🔇 Additional comments (3)
storage/postgres/time_trigger_test.go (1)

41-47: PolicyVersion migrated to int – looks correct

InsertPluginPolicyTx now receives PolicyVersion: 1 (int) which matches the new verifier API. No further issues spotted in this hunk.

go.mod (1)

22-23: Dependency bumps – run go mod tidy & CI before merge

Two internal modules were upgraded. Please:

  1. go mod tidy to ensure no stale requirements.
  2. Re-run unit tests & go vet – both upstream repos often introduce breaking API tweaks between commits.
plugin/payroll/policy.go (1)

199-230: Interface alignment check

The signature change to ValidatePluginPolicy(policyDoc vtypes.PluginPolicy) matches the new verifier API. Confirm that PayrollPlugin still satisfies whatever interface the host expects (e.g. plugin.Plugin). A quick go vet run will flag any mismatch.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🔭 Outside diff range comments (1)
api/plugin.go (1)

195-217: ⚠️ Potential issue

Missing replay-protection: policy signature is verified after potential mutation

policy.ID may be autogenerated (lines 207-209) before signature verification (line 211).
If clients sign a payload without ID, the server mutates the struct and then validates the signature against mutated data → always fails.

Either:

  1. Require ID to be present and signed, or
  2. Generate ID before clients sign, or
  3. Exclude ID from the signed fields.
♻️ Duplicate comments (1)
api/plugin.go (1)

239-245: Same replay-protection concern for Update path

See comment above – signature is verified after possible field mutations, leading to systematic verification failures.

🧹 Nitpick comments (4)
plugin/fees/fees.go (2)

41-44: Logger field is copy-pasted from Payroll plugin

logrus.WithField("plugin", "payroll") breaks filtering/metrics for the new “fees” plugin.

-logger:    logrus.WithField("plugin", "payroll"),
+logger:    logrus.WithField("plugin", "fees"),

35-38: Wrap RPC dial error to include target URL

Returning the raw error drops the context of which URL failed.

-rpcClient, err := ethclient.Dial(cfg.RpcURL)
-if err != nil {
-    return nil, err
+rpcClient, err := ethclient.Dial(cfg.RpcURL)
+if err != nil {
+    return nil, fmt.Errorf("ethclient dial %s: %w", cfg.RpcURL, err)
 }
api/plugin.go (2)

63-66: Validate-then-sign pattern mixes concerns

ValidateProposedTransactions is invoked before the message hash check.
If the hash check fails, expensive validation work was wasted. Consider swapping the two blocks to fail fast.


398-398: Use strconv.Itoa instead of fmt.Sprintf for int-to-string

More idiomatic and allocates slightly less.

-    fmt.Sprintf("%d", policy.PolicyVersion),
+    strconv.Itoa(policy.PolicyVersion),
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between d516d54 and c4575e2.

⛔ Files ignored due to path filters (1)
  • go.sum is excluded by !**/*.sum
📒 Files selected for processing (5)
  • api/plugin.go (6 hunks)
  • plugin/dca/dca.go (7 hunks)
  • plugin/fees/config.go (1 hunks)
  • plugin/fees/constraints.go (1 hunks)
  • plugin/fees/fees.go (1 hunks)
✅ Files skipped from review due to trivial changes (1)
  • plugin/fees/constraints.go
🚧 Files skipped from review as they are similar to previous changes (1)
  • plugin/dca/dca.go
🧰 Additional context used
🧬 Code Graph Analysis (3)
plugin/fees/config.go (1)
plugin/fees/constraints.go (1)
  • PLUGIN_TYPE (3-3)
plugin/fees/fees.go (3)
storage/db.go (1)
  • DatabaseStorage (14-41)
plugin/fees/config.go (1)
  • PluginConfig (11-24)
internal/types/keysign.go (1)
  • PluginKeysignRequest (7-12)
api/plugin.go (1)
internal/types/keysign.go (1)
  • PluginKeysignRequest (7-12)
⏰ Context from checks skipped due to timeout of 90000ms (1)
  • GitHub Check: build
🔇 Additional comments (1)
plugin/fees/fees.go (1)

48-55: Hard-coded recipe ID / version & leftover TODO

"vultisig-fees-0000" and version 1 are hard-coded. This will diverge once the plugin evolves.

Clarify the TODO or derive these values from constants so they cannot get out of sync with constraints.go.

Comment thread plugin/fees/config.go
Comment on lines +42 to +44
if err := v.ReadInConfig(); err != nil {
return nil, fmt.Errorf("failed to read config: %w", err)
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Differentiate between “file not found” and real config parse errors

v.ReadInConfig() returns an error when the config file is missing.
Right now any error aborts startup, even when users intend to rely solely on environment variables. Handle viper.ConfigFileNotFoundError explicitly so env-only setups still work.

-if err := v.ReadInConfig(); err != nil {
-    return nil, fmt.Errorf("failed to read config: %w", err)
+if err := v.ReadInConfig(); err != nil {
+    // allow env-only configuration
+    if _, ok := err.(viper.ConfigFileNotFoundError); !ok {
+        return nil, fmt.Errorf("failed to read config: %w", err)
+    }
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if err := v.ReadInConfig(); err != nil {
return nil, fmt.Errorf("failed to read config: %w", err)
}
if err := v.ReadInConfig(); err != nil {
// allow env-only configuration
if _, ok := err.(viper.ConfigFileNotFoundError); !ok {
return nil, fmt.Errorf("failed to read config: %w", err)
}
}
🤖 Prompt for AI Agents
In plugin/fees/config.go around lines 42 to 44, the current code treats all
errors from v.ReadInConfig() as fatal, including when the config file is
missing. Modify the error handling to explicitly check if the error is a
viper.ConfigFileNotFoundError and ignore it in that case, allowing the program
to continue when users rely only on environment variables. For other errors,
keep returning the wrapped error as before.

Comment thread plugin/fees/config.go
Comment on lines +58 to +60
if config.Gas.LimitMultiplier <= 0 {
return nil, errors.New("gas limit multiplier must be positive")
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Validate Gas.PriceMultiplier as well

PriceMultiplier can silently be zero or negative, leading to an under-priced transaction and inevitable on-chain failure.

-if config.Gas.LimitMultiplier <= 0 {
-    return nil, errors.New("gas limit multiplier must be positive")
+if config.Gas.LimitMultiplier <= 0 {
+    return nil, errors.New("gas limit multiplier must be positive")
+}
+if config.Gas.PriceMultiplier <= 0 {
+    return nil, errors.New("gas price multiplier must be positive")
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if config.Gas.LimitMultiplier <= 0 {
return nil, errors.New("gas limit multiplier must be positive")
}
if config.Gas.LimitMultiplier <= 0 {
return nil, errors.New("gas limit multiplier must be positive")
}
if config.Gas.PriceMultiplier <= 0 {
return nil, errors.New("gas price multiplier must be positive")
}
🤖 Prompt for AI Agents
In plugin/fees/config.go around lines 58 to 60, add a validation check for
config.Gas.PriceMultiplier similar to the existing check for LimitMultiplier.
Ensure that PriceMultiplier is greater than zero, and if not, return an error
indicating that the gas price multiplier must be positive to prevent
under-priced transactions and on-chain failures.

Comment thread plugin/fees/fees.go
Comment on lines +40 to +45
return &FeePlugin{
db: db,
rpcClient: rpcClient,
logger: logrus.WithField("plugin", "payroll"),
config: cfg,
}, nil

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Remember to close the ethclient.Client

ethclient.Client holds open connections; without a Close() the process leaks file descriptors.
Expose a Close() on FeePlugin (implement io.Closer) or close it in caller.

🤖 Prompt for AI Agents
In plugin/fees/fees.go around lines 40 to 45, the ethclient.Client instance
assigned to FeePlugin is not closed, causing resource leaks. Implement a Close()
method on FeePlugin that calls Close() on the ethclient.Client to release
resources properly. Ensure FeePlugin implements io.Closer and update callers to
invoke this Close() method when done using the plugin.

@johnnyluo johnnyluo left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

LGTM

@johnnyluo
johnnyluo merged commit 03f2214 into main Jun 12, 2025
3 checks passed
@johnnyluo
johnnyluo deleted the plugin-policy-breaking-changes-fix branch June 12, 2025 22:53
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants