Skip to content
This repository was archived by the owner on Feb 8, 2026. It is now read-only.
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
42 changes: 27 additions & 15 deletions api/plugin.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ func (s *Server) SignPluginMessages(c echo.Context) error {
return fmt.Errorf("policy plugin ID mismatch")
}

if err := s.plugin.ValidateProposedTransactions(policy, []vtypes.PluginKeysignRequest{req}); err != nil {
if err := s.plugin.ValidateProposedTransactions(policy.ToPluginPolicyCreateUpdate(), []vtypes.PluginKeysignRequest{req}); err != nil {
return fmt.Errorf("failed to validate transaction proposal: %w", err)
}

Expand Down Expand Up @@ -146,8 +146,14 @@ func (s *Server) GetPluginPolicyById(c echo.Context) error {
if policyID == "" {
return c.JSON(http.StatusBadRequest, NewErrorResponse("invalid policy ID"))
}

policy, err := s.policyService.GetPluginPolicy(c.Request().Context(), policyID)
uPolicyID, err := uuid.Parse(policyID)
if err != nil {
s.logger.WithError(err).
WithField("policy_id", policyID).
Error("failed to parse policy ID")
return c.JSON(http.StatusBadRequest, NewErrorResponse("invalid policy ID"))
}
policy, err := s.policyService.GetPluginPolicy(c.Request().Context(), uPolicyID)
if err != nil {
s.logger.WithError(err).
WithField("policy_id", policyID).
Expand Down Expand Up @@ -183,7 +189,7 @@ func (s *Server) GetAllPluginPolicies(c echo.Context) error {
}

func (s *Server) CreatePluginPolicy(c echo.Context) error {
var policy vtypes.PluginPolicy
var policy vtypes.PluginPolicyCreateUpdate
if err := c.Bind(&policy); err != nil {
return fmt.Errorf("fail to parse request, err: %w", err)
}
Expand All @@ -199,12 +205,12 @@ func (s *Server) CreatePluginPolicy(c echo.Context) error {
policy.ID = uuid.New()
}

if !s.verifyPolicySignature(policy, false) {
if !s.verifyPolicySignature(policy.ToPluginPolicy()) {
s.logger.Error("invalid policy signature")
return c.JSON(http.StatusForbidden, NewErrorResponse("Invalid policy signature"))
}

newPolicy, err := s.policyService.CreatePolicy(c.Request().Context(), policy)
newPolicy, err := s.policyService.CreatePolicy(c.Request().Context(), policy.ToPluginPolicy())
if err != nil {
s.logger.WithError(err).Error("Failed to create plugin policy")
return c.JSON(http.StatusInternalServerError, NewErrorResponse("failed to create policy"))
Expand All @@ -214,7 +220,7 @@ func (s *Server) CreatePluginPolicy(c echo.Context) error {
}

func (s *Server) UpdatePluginPolicyById(c echo.Context) error {
var policy vtypes.PluginPolicy
var policy vtypes.PluginPolicyCreateUpdate
if err := c.Bind(&policy); err != nil {
return fmt.Errorf("fail to parse request, err: %w", err)
}
Expand All @@ -227,12 +233,12 @@ func (s *Server) UpdatePluginPolicyById(c echo.Context) error {
return c.JSON(http.StatusBadRequest, NewErrorResponse("failed to validate policy"))
}

if !s.verifyPolicySignature(policy, true) {
if !s.verifyPolicySignature(policy.ToPluginPolicy()) {
s.logger.Error("invalid policy signature")
return c.JSON(http.StatusForbidden, NewErrorResponse("Invalid policy signature"))
}

updatedPolicy, err := s.policyService.UpdatePolicy(c.Request().Context(), policy)
updatedPolicy, err := s.policyService.UpdatePolicy(c.Request().Context(), policy.ToPluginPolicy())
if err != nil {
s.logger.WithError(err).Error("Failed to update plugin policy")
return c.JSON(http.StatusInternalServerError, NewErrorResponse("failed to update policy"))
Expand All @@ -254,8 +260,14 @@ func (s *Server) DeletePluginPolicyById(c echo.Context) error {
if policyID == "" {
return c.JSON(http.StatusBadRequest, NewErrorResponse("invalid policy ID"))
}

policy, err := s.policyService.GetPluginPolicy(c.Request().Context(), policyID)
uPolicyID, err := uuid.Parse(policyID)
if err != nil {
s.logger.WithError(err).
WithField("policy_id", policyID).
Error("Failed to parse policy ID")
return c.JSON(http.StatusBadRequest, NewErrorResponse("invalid policy ID"))
}
policy, err := s.policyService.GetPluginPolicy(c.Request().Context(), uPolicyID)
if err != nil {
s.logger.WithError(err).
WithField("policy_id", policyID).
Expand All @@ -266,11 +278,11 @@ func (s *Server) DeletePluginPolicyById(c echo.Context) error {
// This is because we have different signature stored in the database.
policy.Signature = reqBody.Signature

if !s.verifyPolicySignature(policy, true) {
if !s.verifyPolicySignature(policy) {
return c.JSON(http.StatusForbidden, NewErrorResponse("Invalid policy signature"))
}

if err := s.policyService.DeletePolicy(c.Request().Context(), policyID, reqBody.Signature); err != nil {
if err := s.policyService.DeletePolicy(c.Request().Context(), uPolicyID, reqBody.Signature); err != nil {
s.logger.WithError(err).
WithField("policy_id", policyID).
Error("Failed to delete plugin policy")
Expand Down Expand Up @@ -304,7 +316,7 @@ func (s *Server) GetPolicySchema(c echo.Context) error {

func (s *Server) GetRecipeSpecification(c echo.Context) error {
recipeSpec := s.plugin.GetRecipeSpecification()
return c.JSON(http.StatusOK, recipeSpec)
return c.JSON(http.StatusOK, &recipeSpec)
Comment thread
johnnyluo marked this conversation as resolved.
}

func (s *Server) GetPluginPolicyTransactionHistory(c echo.Context) error {
Expand All @@ -325,7 +337,7 @@ func (s *Server) GetPluginPolicyTransactionHistory(c echo.Context) error {
return c.JSON(http.StatusOK, policyHistory)
}

func (s *Server) verifyPolicySignature(policy vtypes.PluginPolicy, update bool) bool {
func (s *Server) verifyPolicySignature(policy vtypes.PluginPolicy) bool {
msgBytes, err := policyToMessageHex(policy)
if err != nil {
s.logger.WithError(err).Error("Failed to convert policy to message hex")
Expand Down
1 change: 1 addition & 0 deletions cmd/payroll/worker/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ type PayrollWorkerConfig struct {
Database struct {
DSN string `mapstructure:"dsn" json:"dsn,omitempty"`
} `mapstructure:"database" json:"database,omitempty"`
BaseConfigPath string `mapstructure:"base_file_path" json:"base_file_path,omitempty"`
}

func GetConfigure() (*PayrollWorkerConfig, error) {
Expand Down
9 changes: 8 additions & 1 deletion cmd/payroll/worker/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (

"github.com/vultisig/plugin/internal/scheduler"
"github.com/vultisig/plugin/internal/tasks"
"github.com/vultisig/plugin/plugin/payroll"
"github.com/vultisig/plugin/storage/postgres"
)

Expand Down Expand Up @@ -56,14 +57,20 @@ func main() {
if err != nil {
panic(fmt.Errorf("failed to create postgres backend: %w", err))
}
p, err := payroll.NewPayrollPlugin(postgressDB, cfg.BaseConfigPath)
if err != nil {
panic(fmt.Errorf("failed to create payroll plugin: %w", err))
}
schedulerSvc, err := scheduler.NewSchedulerService(postgressDB, client, redisOptions)
if err != nil {
panic(fmt.Errorf("failed to create scheduler service: %w", err))
}

schedulerSvc.Start()
defer schedulerSvc.Stop()

mux := asynq.NewServeMux()
// mux.HandleFunc(tasks.TypePluginTransaction, vaultService.HandlePluginTransaction)
mux.HandleFunc(tasks.TypePluginTransaction, p.HandleSchedulerTrigger)
mux.HandleFunc(tasks.TypeKeySignDKLS, vaultService.HandleKeySignDKLS)
mux.HandleFunc(tasks.TypeReshareDKLS, vaultService.HandleReshareDKLS)
if err := srv.Run(mux); err != nil {
Expand Down
4 changes: 2 additions & 2 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@ require (
github.com/spf13/viper v1.20.1
github.com/vultisig/commondata v0.0.0-20250430024109-a2492623ef05
github.com/vultisig/mobile-tss-lib v0.0.0-20250316003201-2e7e570a4a74
github.com/vultisig/recipes v0.0.0-20250603213257-b4a6b0afe2a0
github.com/vultisig/verifier v0.0.0-20250531103513-c1f5cb38b103
github.com/vultisig/recipes v0.0.0-20250604212709-58772375f814
github.com/vultisig/verifier v0.0.0-20250606071917-2f9ec25b5689
github.com/vultisig/vultiserver v0.0.0-20250515110921-82d56d3d9cc9
)

Expand Down
10 changes: 4 additions & 6 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -738,12 +738,10 @@ github.com/vultisig/go-wrappers v0.0.0-20250403041248-86911e8aa33f h1:124Xlloih1
github.com/vultisig/go-wrappers v0.0.0-20250403041248-86911e8aa33f/go.mod h1:UfGCxUQW08kiwxyNBiHwXe+ePPuBmHVVS+BS51aU/Jg=
github.com/vultisig/mobile-tss-lib v0.0.0-20250316003201-2e7e570a4a74 h1:goqwk4nQ/NEVIb3OPP9SUx7/u9ZfsUIcd5fIN/e4DVU=
github.com/vultisig/mobile-tss-lib v0.0.0-20250316003201-2e7e570a4a74/go.mod h1:nOykk4nOy1L3yXtLSlYvVsgizBnCQ3tR2N5uwGPdvaM=
github.com/vultisig/recipes v0.0.0-20250531132511-82a0eb621885 h1:hXju0II1G1CydXchhjTla1JxK8WceDUGJhQhJHJSUIE=
github.com/vultisig/recipes v0.0.0-20250531132511-82a0eb621885/go.mod h1:BXXJ25U75xaexJLoiiaLEZ6TQrcy8+8UGJGd4c+hNSE=
github.com/vultisig/recipes v0.0.0-20250603213257-b4a6b0afe2a0 h1:JauwztTOr7EqVhhXIQw+fWX6+VKDc5T6zLVBFIvGuBk=
github.com/vultisig/recipes v0.0.0-20250603213257-b4a6b0afe2a0/go.mod h1:BXXJ25U75xaexJLoiiaLEZ6TQrcy8+8UGJGd4c+hNSE=
github.com/vultisig/verifier v0.0.0-20250531103513-c1f5cb38b103 h1:1Qgt3uwzG11UqKoizd5Ft65Ij/8c2Kxpg3VB6N3/Fb0=
github.com/vultisig/verifier v0.0.0-20250531103513-c1f5cb38b103/go.mod h1:8b9k8CJtF1KwjMoZuWFYmz0Q+zdikrx3q0ExbZqsLgk=
github.com/vultisig/recipes v0.0.0-20250604212709-58772375f814 h1:uY4x91tXkpDwuUOLBxUrG0ASvJzlvNKnbGqOMrBs0U8=
github.com/vultisig/recipes v0.0.0-20250604212709-58772375f814/go.mod h1:BXXJ25U75xaexJLoiiaLEZ6TQrcy8+8UGJGd4c+hNSE=
github.com/vultisig/verifier v0.0.0-20250606071917-2f9ec25b5689 h1:hD5rRrAImxdqN7yyI5BqqtZWye9Eoera9bbvimf0dhU=
github.com/vultisig/verifier v0.0.0-20250606071917-2f9ec25b5689/go.mod h1:Llu11hCj/HxVdzWIvnpwAJ9s4QnooaCCxZr42ExZS4g=
github.com/vultisig/vultiserver v0.0.0-20250515110921-82d56d3d9cc9 h1:pZhGN8q8+gPB1JJjVDC1hDg8qn6Tbj0XBJymgTQ8qQg=
github.com/vultisig/vultiserver v0.0.0-20250515110921-82d56d3d9cc9/go.mod h1:HwP2IgW6Mcu/gX8paFuKvfibrGE9UmPgkOFTub6dskM=
github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
Expand Down
20 changes: 12 additions & 8 deletions plugin/dca/dca.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,14 +21,16 @@ import (
"github.com/vultisig/mobile-tss-lib/tss"
"github.com/vultisig/verifier/address"
vcommon "github.com/vultisig/verifier/common"
"github.com/vultisig/verifier/plugin"
vtypes "github.com/vultisig/verifier/types"

rtypes "github.com/vultisig/recipes/types"

"github.com/vultisig/plugin/common"
"github.com/vultisig/plugin/internal/sigutil"
"github.com/vultisig/plugin/internal/types"
"github.com/vultisig/plugin/pkg/uniswap"
"github.com/vultisig/plugin/storage"
rtypes "github.com/vultisig/recipes/types"
)

const (
Expand All @@ -46,6 +48,8 @@ var (
ErrCompletedPolicy = errors.New("policy completed all swaps")
)

var _ plugin.Plugin = (*DCAPlugin)(nil)

type DCAPlugin struct {
uniswapClient *uniswap.Client
rpcClient *ethclient.Client
Expand Down Expand Up @@ -90,7 +94,7 @@ func (p *DCAPlugin) SigningComplete(
ctx context.Context,
signature tss.KeysignResponse,
signRequest vtypes.PluginKeysignRequest,
policy vtypes.PluginPolicy,
policy vtypes.PluginPolicyCreateUpdate,
) error {
var dcaPolicy DCAPolicy
// TODO: convert recipe to DCAPolicy
Expand Down Expand Up @@ -131,7 +135,7 @@ func (p *DCAPlugin) SigningComplete(
return nil
}

func (p *DCAPlugin) ValidatePluginPolicy(policyDoc vtypes.PluginPolicy) error {
func (p *DCAPlugin) ValidatePluginPolicy(policyDoc vtypes.PluginPolicyCreateUpdate) error {
if policyDoc.PluginID != vtypes.PluginVultisigDCA_0000 {
return fmt.Errorf("policy does not match plugin type, expected: %s, got: %s", pluginType, policyDoc.PluginID)
}
Expand Down Expand Up @@ -266,7 +270,7 @@ func validateInterval(intervalStr string, frequency string) error {
return nil
}

func (p *DCAPlugin) ProposeTransactions(policy vtypes.PluginPolicy) ([]vtypes.PluginKeysignRequest, error) {
func (p *DCAPlugin) ProposeTransactions(policy vtypes.PluginPolicyCreateUpdate) ([]vtypes.PluginKeysignRequest, error) {
p.logger.Info("DCA: PROPOSE TRANSACTIONS")

var txs []vtypes.PluginKeysignRequest
Expand Down Expand Up @@ -297,7 +301,7 @@ func (p *DCAPlugin) ProposeTransactions(policy vtypes.PluginPolicy) ([]vtypes.Pl
}

if completedSwaps >= totalOrders.Int64() {
if err := p.completePolicy(context.Background(), policy); err != nil {
if err := p.completePolicy(context.Background(), policy.ToPluginPolicy()); err != nil {
return txs, fmt.Errorf("fail to complete policy: %w", err)
}
return txs, ErrCompletedPolicy
Expand Down Expand Up @@ -343,9 +347,9 @@ func (p *DCAPlugin) ProposeTransactions(policy vtypes.PluginPolicy) ([]vtypes.Pl
common.PluginPartyID,
common.VerifierPartyID},
PluginID: policy.PluginID.String(),
PolicyID: policy.ID,
},
Transaction: hex.EncodeToString(data.RlpTxBytes),
PolicyID: policy.ID.String(),
TransactionType: data.Type,
}
txs = append(txs, signRequest)
Expand All @@ -354,7 +358,7 @@ func (p *DCAPlugin) ProposeTransactions(policy vtypes.PluginPolicy) ([]vtypes.Pl
return txs, nil
}

func (p *DCAPlugin) ValidateProposedTransactions(policy vtypes.PluginPolicy, txs []vtypes.PluginKeysignRequest) error {
func (p *DCAPlugin) ValidateProposedTransactions(policy vtypes.PluginPolicyCreateUpdate, txs []vtypes.PluginKeysignRequest) error {
p.logger.Info("DCA: VALIDATE TRANSACTION PROPOSAL")

if len(txs) == 0 {
Expand Down Expand Up @@ -403,7 +407,7 @@ func (p *DCAPlugin) ValidateProposedTransactions(policy vtypes.PluginPolicy, txs
}
// TODO: Change this to make the policy to status COMPLETED if: completed swaps == total orders.
if completedSwaps >= totalOrders.Int64() {
if err := p.completePolicy(context.Background(), policy); err != nil {
if err := p.completePolicy(context.Background(), policy.ToPluginPolicy()); err != nil {
return fmt.Errorf("fail to complete policy: %w", err)
}
p.logger.Info("DCA: COMPLETED SWAPS: ", totalOrders.Int64())
Expand Down
3 changes: 3 additions & 0 deletions plugin/payroll/payroll.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,13 @@ import (

"github.com/ethereum/go-ethereum/ethclient"
"github.com/sirupsen/logrus"
"github.com/vultisig/verifier/plugin"

"github.com/vultisig/plugin/storage"
)

var _ plugin.Plugin = (*PayrollPlugin)(nil)

type PayrollPlugin struct {
db storage.DatabaseStorage
nonceManager *NonceManager
Expand Down
4 changes: 2 additions & 2 deletions plugin/payroll/policy.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ type Schedule struct {
EndTime string `json:"end_time,omitempty"`
}

func (p *PayrollPlugin) ValidateProposedTransactions(policy vtypes.PluginPolicy, txs []vtypes.PluginKeysignRequest) error {
func (p *PayrollPlugin) ValidateProposedTransactions(policy vtypes.PluginPolicyCreateUpdate, txs []vtypes.PluginKeysignRequest) error {
err := p.ValidatePluginPolicy(policy)
if err != nil {
return fmt.Errorf("failed to validate plugin policy: %v", err)
Expand Down Expand Up @@ -196,7 +196,7 @@ func (p *PayrollPlugin) checkRule(rule *rtypes.Rule) error {
}
return nil
}
func (p *PayrollPlugin) ValidatePluginPolicy(policyDoc vtypes.PluginPolicy) error {
func (p *PayrollPlugin) ValidatePluginPolicy(policyDoc vtypes.PluginPolicyCreateUpdate) error {
if policyDoc.PluginID != vtypes.PluginVultisigPayroll_0000 {
return fmt.Errorf("policy does not match plugin type, expected: %s, got: %s", vtypes.PluginVultisigPayroll_0000, policyDoc.PluginID)
}
Expand Down
31 changes: 26 additions & 5 deletions plugin/payroll/transaction.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,14 @@ package payroll
import (
"context"
"encoding/hex"
"encoding/json"
"fmt"
"math/big"
"strconv"
"strings"

"github.com/google/uuid"
"github.com/vultisig/vultiserver/contexthelper"

"github.com/ethereum/go-ethereum"
"github.com/ethereum/go-ethereum/accounts/abi"
Expand All @@ -22,14 +24,35 @@ import (
"github.com/vultisig/verifier/address"
vcommon "github.com/vultisig/verifier/common"
vtypes "github.com/vultisig/verifier/types"

"github.com/vultisig/plugin/internal/types"
)

// TODO: remove once the plugin installation is implemented
const (
hexEncryptionKey = "hexencryptionkey"
)

func (p *PayrollPlugin) ProposeTransactions(policy vtypes.PluginPolicy) ([]vtypes.PluginKeysignRequest, error) {
func (p *PayrollPlugin) HandleSchedulerTrigger(ctx context.Context, t *asynq.Task) error {
if err := contexthelper.CheckCancellation(ctx); err != nil {
p.logger.WithError(err).Warn("Context cancelled, skipping scheduler trigger")
return err
}
var trigger types.TimeTrigger
if err := json.Unmarshal(t.Payload(), &trigger); err != nil {
p.logger.WithError(err).Error("Failed to unmarshal trigger payload")
return fmt.Errorf("failed to unmarshal trigger payload: %s, %w", err, asynq.SkipRetry)
}
pluginPolicy, err := p.db.GetPluginPolicy(ctx, trigger.PolicyID)
if err != nil {
p.logger.WithError(err).Error("Failed to get plugin policy from database")
return fmt.Errorf("failed to get plugin policy: %s, %w", err, asynq.SkipRetry)
}
// propose transaction and get it signed
_ = pluginPolicy

Copilot AI Jun 6, 2025

Copy link

Choose a reason for hiding this comment

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

[nitpick] The placeholder _ = pluginPolicy is unused. You should implement the intended logic or remove this line to avoid confusion.

Suggested change
_ = pluginPolicy

Copilot uses AI. Check for mistakes.
return nil
}
Comment thread
johnnyluo marked this conversation as resolved.
func (p *PayrollPlugin) ProposeTransactions(policy vtypes.PluginPolicyCreateUpdate) ([]vtypes.PluginKeysignRequest, error) {
var txs []vtypes.PluginKeysignRequest
err := p.ValidatePluginPolicy(policy)
if err != nil {
Expand Down Expand Up @@ -73,8 +96,6 @@ func (p *PayrollPlugin) ProposeTransactions(policy vtypes.PluginPolicy) ([]vtype
PluginID: policy.PluginID.String(),
},
Transaction: hex.EncodeToString(rawTx),

PolicyID: policy.ID.String(),
}
txs = append(txs, signRequest)
}
Expand Down Expand Up @@ -211,8 +232,8 @@ func (p *PayrollPlugin) generatePayrollTransaction(amountString, recipientString
return txHash, rawTx, nil
}

func (p *PayrollPlugin) SigningComplete(ctx context.Context, signature tss.KeysignResponse, signRequest vtypes.PluginKeysignRequest, policy vtypes.PluginPolicy) error {
R, S, V, originalTx, chainID, _, err := p.convertData(signature, signRequest, policy)
func (p *PayrollPlugin) SigningComplete(ctx context.Context, signature tss.KeysignResponse, signRequest vtypes.PluginKeysignRequest, policy vtypes.PluginPolicyCreateUpdate) error {
R, S, V, originalTx, chainID, _, err := p.convertData(signature, signRequest, policy.ToPluginPolicy())
if err != nil {
return fmt.Errorf("failed to convert R and S: %v", err)
}
Expand Down
Loading