diff --git a/go.mod b/go.mod index e07ab47..56fe04d 100644 --- a/go.mod +++ b/go.mod @@ -20,7 +20,7 @@ require ( github.com/stretchr/testify v1.10.0 github.com/vultisig/commondata v0.0.0-20250710214228-61d9ed8f7778 github.com/vultisig/mobile-tss-lib v0.0.0-20250316003201-2e7e570a4a74 - github.com/vultisig/recipes v0.0.0-20250723142134-153c7f486070 + github.com/vultisig/recipes v0.0.0-20250724185247-fde1a4c02314 github.com/vultisig/verifier v0.0.0-20250723150319-a51c59a884bf github.com/vultisig/vultiserver v0.0.0-20250715212748-4b23f9849e4b golang.org/x/sync v0.14.0 diff --git a/go.sum b/go.sum index b01e87c..512113e 100644 --- a/go.sum +++ b/go.sum @@ -759,6 +759,8 @@ github.com/vultisig/mobile-tss-lib v0.0.0-20250316003201-2e7e570a4a74 h1:goqwk4n github.com/vultisig/mobile-tss-lib v0.0.0-20250316003201-2e7e570a4a74/go.mod h1:nOykk4nOy1L3yXtLSlYvVsgizBnCQ3tR2N5uwGPdvaM= github.com/vultisig/recipes v0.0.0-20250723142134-153c7f486070 h1:AcE6x2dxkl/4P/TxaMJ70pa2BcZ3+FJWnVmlPVAq4Dk= github.com/vultisig/recipes v0.0.0-20250723142134-153c7f486070/go.mod h1:30NOW5y2BnMCmEYFeVls3NhxkbMvuv7BNyRQfoFhGIM= +github.com/vultisig/recipes v0.0.0-20250724185247-fde1a4c02314 h1:817gEvSIW4cNqmQxZIpwttnuzaw3RYIGbHFtIsm9Eyk= +github.com/vultisig/recipes v0.0.0-20250724185247-fde1a4c02314/go.mod h1:30NOW5y2BnMCmEYFeVls3NhxkbMvuv7BNyRQfoFhGIM= github.com/vultisig/verifier v0.0.0-20250723150319-a51c59a884bf h1:3VhxFQR0oJEieaajNDvKlOGBDyTBbSMJSxow3zwUlmw= github.com/vultisig/verifier v0.0.0-20250723150319-a51c59a884bf/go.mod h1:9f3yGSZWKZeXn30mU+/brufkbkLoGlFmNLKOpRbQgeI= github.com/vultisig/vultiserver v0.0.0-20250715212748-4b23f9849e4b h1:Ed2DOWo8fA0KG6e36rzUmGpxcOQjmWTbxWyvUbI5by8= diff --git a/plugin/payroll/transaction.go b/plugin/payroll/transaction.go index bd79e82..99060b8 100644 --- a/plugin/payroll/transaction.go +++ b/plugin/payroll/transaction.go @@ -186,8 +186,6 @@ func (p *Plugin) ProposeTransactions(policy vtypes.PluginPolicy) ([]vtypes.Plugi return nil, fmt.Errorf("failed to get EVM ID for chain %s: %v", chain, err) } - schedule := recipe.Schedule - var ( mu = &sync.Mutex{} txs = make([]vtypes.PluginKeysignRequest, 0) @@ -202,99 +200,71 @@ func (p *Plugin) ProposeTransactions(policy vtypes.PluginPolicy) ([]vtypes.Plugi return nil, fmt.Errorf("getTokenID: %v", er) } - recipients, amountStr, er := RuleToRecipientsAndAmount(rule) + recipient, amountStr, er := RuleToRecipientAndAmount(rule) if er != nil { - return nil, fmt.Errorf("failed to get recipients and amount: %v", er) + return nil, fmt.Errorf("failed to get recipient and amount: %v", er) } - for _, _recipient := range recipients { - recipient := _recipient - - eg.Go(func() error { - isAlreadyProposed, e := p.IsAlreadyProposed( - ctx, - schedule.Frequency, - schedule.StartTime.AsTime(), - int(schedule.Interval), - chain, - policy.PluginID, - policy.ID, - tokenID, - recipient, - ) - if e != nil { - return fmt.Errorf("p.IsAlreadyProposed: %w", e) - } - if isAlreadyProposed { - p.logger.WithFields(logrus.Fields{ - "recipient": recipient, - "amount": amountStr, - "chain_id": chain, - "token_id": tokenID, - }).Info("transaction already proposed, skipping") - return nil - } - - tx, e := p.genUnsignedTx( - ctx, - chain, - ethAddress, - tokenID, - amountStr, - recipient, - ) - if e != nil { - return fmt.Errorf("p.genUnsignedTx: %w", e) - } - - txHex := gcommon.Bytes2Hex(tx) - - txData, e := ethereum.DecodeUnsignedPayload(tx) - if e != nil { - return fmt.Errorf("ethereum.DecodeUnsignedPayload: %w", e) - } - txHashToSign := etypes.LatestSignerForChainID(ethEvmID).Hash(etypes.NewTx(txData)) - - txToTrack, e := p.txIndexerService.CreateTx(ctx, storage.CreateTxDto{ - PluginID: policy.PluginID, - PolicyID: policy.ID, - ChainID: chain, - TokenID: tokenID, - FromPublicKey: policy.PublicKey, - ToPublicKey: recipient, - ProposedTxHex: txHex, - }) - if e != nil { - return fmt.Errorf("p.txIndexerService.CreateTx: %w", e) - } - - msgHash := sha256.Sum256(txHashToSign.Bytes()) - - // Create signing request - signRequest := vtypes.PluginKeysignRequest{ - KeysignRequest: vtypes.KeysignRequest{ - PublicKey: policy.PublicKey, - Messages: []vtypes.KeysignMessage{ - { - TxIndexerID: txToTrack.ID.String(), - Message: base64.StdEncoding.EncodeToString(txHashToSign.Bytes()), - Chain: chain, - Hash: base64.StdEncoding.EncodeToString(msgHash[:]), - HashFunction: vtypes.HashFunction_SHA256, - }, + eg.Go(func() error { + tx, e := p.genUnsignedTx( + ctx, + chain, + ethAddress, + tokenID, + amountStr, + recipient, + ) + if e != nil { + return fmt.Errorf("p.genUnsignedTx: %w", e) + } + + txHex := gcommon.Bytes2Hex(tx) + + txData, e := ethereum.DecodeUnsignedPayload(tx) + if e != nil { + return fmt.Errorf("ethereum.DecodeUnsignedPayload: %w", e) + } + txHashToSign := etypes.LatestSignerForChainID(ethEvmID).Hash(etypes.NewTx(txData)) + + txToTrack, e := p.txIndexerService.CreateTx(ctx, storage.CreateTxDto{ + PluginID: policy.PluginID, + PolicyID: policy.ID, + ChainID: chain, + TokenID: tokenID, + FromPublicKey: policy.PublicKey, + ToPublicKey: recipient, + ProposedTxHex: txHex, + }) + if e != nil { + return fmt.Errorf("p.txIndexerService.CreateTx: %w", e) + } + + msgHash := sha256.Sum256(txHashToSign.Bytes()) + + // Create signing request + signRequest := vtypes.PluginKeysignRequest{ + KeysignRequest: vtypes.KeysignRequest{ + PublicKey: policy.PublicKey, + Messages: []vtypes.KeysignMessage{ + { + TxIndexerID: txToTrack.ID.String(), + Message: base64.StdEncoding.EncodeToString(txHashToSign.Bytes()), + Chain: chain, + Hash: base64.StdEncoding.EncodeToString(msgHash[:]), + HashFunction: vtypes.HashFunction_SHA256, }, - PolicyID: policy.ID, - PluginID: policy.PluginID.String(), }, - Transaction: txHex, - } + PolicyID: policy.ID, + PluginID: policy.PluginID.String(), + }, + Transaction: txHex, + } - mu.Lock() - txs = append(txs, signRequest) - mu.Unlock() - return nil - }) - } + mu.Lock() + txs = append(txs, signRequest) + mu.Unlock() + return nil + }) } err = eg.Wait() @@ -391,7 +361,6 @@ func (p *Plugin) GetRecipeSpecification() (*rtypes.RecipeSchema, error) { ParameterName: "recipient", SupportedTypes: []rtypes.ConstraintType{ rtypes.ConstraintType_CONSTRAINT_TYPE_FIXED, - rtypes.ConstraintType_CONSTRAINT_TYPE_WHITELIST, }, Required: true, }, @@ -457,38 +426,38 @@ func (p *Plugin) genUnsignedTx( return nil, fmt.Errorf("unsupported chain: %s", chain) } -func RuleToRecipientsAndAmount(rule *rtypes.Rule) ([]string, string, error) { - var recipients []string - var amountStr string - +func RuleToRecipientAndAmount(rule *rtypes.Rule) (string, string, error) { if len(rule.ParameterConstraints) == 0 { - return nil, "", fmt.Errorf("no parameter constraints found") + return "", "", fmt.Errorf("no parameter constraints found") } if len(rule.ParameterConstraints) > 3 { - return nil, "", fmt.Errorf("too many parameter constraints found") + return "", "", fmt.Errorf("too many parameter constraints found") } + var ( + recipient string + amountStr string + ) for _, constraint := range rule.ParameterConstraints { + if recipient != "" && amountStr != "" { + break + } + if constraint.ParameterName == "recipient" { - switch constraint.Constraint.Type { - case rtypes.ConstraintType_CONSTRAINT_TYPE_FIXED: - recipients = []string{constraint.Constraint.GetFixedValue()} - case rtypes.ConstraintType_CONSTRAINT_TYPE_WHITELIST: - recipients = constraint.Constraint.GetWhitelistValues().GetValues() + if constraint.Constraint.Type != rtypes.ConstraintType_CONSTRAINT_TYPE_FIXED { + return "", "", fmt.Errorf("recipient constraint is not a fixed value") } - + recipient = constraint.Constraint.GetFixedValue() } if constraint.ParameterName == "amount" { if constraint.Constraint.Type != rtypes.ConstraintType_CONSTRAINT_TYPE_FIXED { - return nil, "", fmt.Errorf("amount constraint is not a fixed value") + return "", "", fmt.Errorf("amount constraint is not a fixed value") } - - fixedValue := constraint.Constraint.GetValue().(*rtypes.Constraint_FixedValue) - amountStr = fixedValue.FixedValue + amountStr = constraint.Constraint.GetFixedValue() } } - return recipients, amountStr, nil + return recipient, amountStr, nil }