diff --git a/go.mod b/go.mod index c04745d..e37d190 100644 --- a/go.mod +++ b/go.mod @@ -18,7 +18,7 @@ require ( github.com/spf13/viper v1.20.1 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-20250729120802-9b1d07f8262a + github.com/vultisig/recipes v0.0.0-20250805093243-2060ffd4754e github.com/vultisig/verifier v0.0.0-20250731092019-00d44a4b02b9 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 b35937c..04c3e80 100644 --- a/go.sum +++ b/go.sum @@ -757,8 +757,14 @@ 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-20250729120802-9b1d07f8262a h1:KoAwytLj092KNgWHh0a5tcupsSm5HtentT0yUbDGWFQ= -github.com/vultisig/recipes v0.0.0-20250729120802-9b1d07f8262a/go.mod h1:Ot3lrUnnSw67Hep+MelclVPgNLDxJP01Ezixw/1RYRE= +github.com/vultisig/recipes v0.0.0-20250804144750-c21977a17e72 h1:Vypx8sZVTbF7GIaTU9VJt3rY35OdI36ZeWIT6GF09Sk= +github.com/vultisig/recipes v0.0.0-20250804144750-c21977a17e72/go.mod h1:Ot3lrUnnSw67Hep+MelclVPgNLDxJP01Ezixw/1RYRE= +github.com/vultisig/recipes v0.0.0-20250804165337-98ae4a2efa18 h1:zEPPC4TE+CJhVPFhTzFFCIHXpa91sMn30tFzYLGP1UE= +github.com/vultisig/recipes v0.0.0-20250804165337-98ae4a2efa18/go.mod h1:Ot3lrUnnSw67Hep+MelclVPgNLDxJP01Ezixw/1RYRE= +github.com/vultisig/recipes v0.0.0-20250804215735-744a9a61f45c h1:942aary32eEcYuXcUURUDUsKWLYgoTjxjDC5VtozdM0= +github.com/vultisig/recipes v0.0.0-20250804215735-744a9a61f45c/go.mod h1:Ot3lrUnnSw67Hep+MelclVPgNLDxJP01Ezixw/1RYRE= +github.com/vultisig/recipes v0.0.0-20250805093243-2060ffd4754e h1:RhIQVc28MzLWsE7Fn0KsltSr+00ESc1eKKNUHcLgFB4= +github.com/vultisig/recipes v0.0.0-20250805093243-2060ffd4754e/go.mod h1:Ot3lrUnnSw67Hep+MelclVPgNLDxJP01Ezixw/1RYRE= github.com/vultisig/verifier v0.0.0-20250731092019-00d44a4b02b9 h1:j4duFCHur0x8vtAh3YHcwmRZKqMwcEkl/u7wjOBrMbc= github.com/vultisig/verifier v0.0.0-20250731092019-00d44a4b02b9/go.mod h1:nFUize5jJAi5P7V0dloNUB+ykJZvgNe4S80h6uAA9SQ= github.com/vultisig/vultiserver v0.0.0-20250715212748-4b23f9849e4b h1:Ed2DOWo8fA0KG6e36rzUmGpxcOQjmWTbxWyvUbI5by8= diff --git a/plugin/dca/dca.go b/plugin/dca/dca.go index 39d9cb8..92dd018 100644 --- a/plugin/dca/dca.go +++ b/plugin/dca/dca.go @@ -772,53 +772,5 @@ func (p *DCAPlugin) completePolicy(ctx context.Context, policy vtypes.PluginPoli } func (p *DCAPlugin) GetRecipeSpecification() (*rtypes.RecipeSchema, error) { - return &rtypes.RecipeSchema{ - Version: 1, // Schema version - ScheduleVersion: 1, // Schedule specification version - PluginId: string(vtypes.PluginVultisigDCA_0000), - PluginName: "Dollar-Cost Averaging", - PluginVersion: 1, // Convert from "0.0.1" to int32 - SupportedResources: []*rtypes.ResourcePattern{ - { - ResourcePath: &rtypes.ResourcePath{ - ChainId: "ethereum", - ProtocolId: "uniswap", - FunctionId: "swap", - Full: "ethereum.uniswap.swap", - }, - ParameterCapabilities: []*rtypes.ParameterConstraintCapability{ - { - ParameterName: "source_token", - SupportedTypes: []rtypes.ConstraintType{ - rtypes.ConstraintType_CONSTRAINT_TYPE_FIXED, - rtypes.ConstraintType_CONSTRAINT_TYPE_WHITELIST, - }, - Required: true, - }, - { - ParameterName: "destination_token", - SupportedTypes: []rtypes.ConstraintType{ - rtypes.ConstraintType_CONSTRAINT_TYPE_FIXED, - rtypes.ConstraintType_CONSTRAINT_TYPE_WHITELIST, - }, - Required: true, - }, - { - ParameterName: "amount", - SupportedTypes: []rtypes.ConstraintType{ - rtypes.ConstraintType_CONSTRAINT_TYPE_FIXED, - rtypes.ConstraintType_CONSTRAINT_TYPE_MAX, - rtypes.ConstraintType_CONSTRAINT_TYPE_RANGE, - }, - Required: true, - }, - }, - Required: true, - }, - }, - Requirements: &rtypes.PluginRequirements{ - MinVultisigVersion: 1, - SupportedChains: []string{"ethereum"}, - }, - }, nil + return &rtypes.RecipeSchema{}, nil } diff --git a/plugin/fees/policy.go b/plugin/fees/policy.go index 181ac5b..5903557 100644 --- a/plugin/fees/policy.go +++ b/plugin/fees/policy.go @@ -33,11 +33,10 @@ func (fp *FeePlugin) GetRecipeSpecification() (*rtypes.RecipeSchema, error) { } return &rtypes.RecipeSchema{ - Version: 1, // Schema version - ScheduleVersion: 1, // Schedule specification version - PluginId: vtypes.PluginVultisigFees_feee.String(), - PluginName: "Fee Plugin", - PluginVersion: 1, + Version: 1, // Schema version + PluginId: vtypes.PluginVultisigFees_feee.String(), + PluginName: "Fee Plugin", + PluginVersion: 1, SupportedResources: []*rtypes.ResourcePattern{ { ResourcePath: &rtypes.ResourcePath{ @@ -46,27 +45,17 @@ func (fp *FeePlugin) GetRecipeSpecification() (*rtypes.RecipeSchema, error) { FunctionId: "transfer", Full: "ethereum.erc20.transfer", }, + Target: rtypes.TargetType_TARGET_TYPE_ADDRESS, ParameterCapabilities: []*rtypes.ParameterConstraintCapability{ { - ParameterName: "recipient", - SupportedTypes: []rtypes.ConstraintType{ - rtypes.ConstraintType_CONSTRAINT_TYPE_MAGIC_CONSTANT, - }, - Required: true, + ParameterName: "recipient", + SupportedTypes: rtypes.ConstraintType_CONSTRAINT_TYPE_MAGIC_CONSTANT, + Required: true, }, { - ParameterName: "amount", - SupportedTypes: []rtypes.ConstraintType{ - rtypes.ConstraintType_CONSTRAINT_TYPE_MAX, - }, - Required: true, - }, - { - ParameterName: "token", - SupportedTypes: []rtypes.ConstraintType{ - rtypes.ConstraintType_CONSTRAINT_TYPE_FIXED, - }, - Required: true, + ParameterName: "amount", + SupportedTypes: rtypes.ConstraintType_CONSTRAINT_TYPE_MAX, + Required: true, }, }, Required: true, diff --git a/plugin/fees/transaction.go b/plugin/fees/transaction.go index 26cde70..093fe0a 100644 --- a/plugin/fees/transaction.go +++ b/plugin/fees/transaction.go @@ -7,12 +7,11 @@ import ( "fmt" "math/big" "strconv" - "strings" "github.com/google/uuid" "github.com/vultisig/mobile-tss-lib/tss" "github.com/vultisig/plugin/common" - "github.com/vultisig/recipes/chain" + rcommon "github.com/vultisig/recipes/common" "github.com/vultisig/recipes/engine" reth "github.com/vultisig/recipes/ethereum" resolver "github.com/vultisig/recipes/resolver" @@ -78,12 +77,6 @@ func (fp *FeePlugin) ProposeTransactions(policy vtypes.PluginPolicy) ([]vtypes.P } magicConstantRecipientValue = rtypes.MagicConstant(iv) } - if constraint.ParameterName == "token" { - if constraint.Constraint.Type != rtypes.ConstraintType_CONSTRAINT_TYPE_FIXED { - return nil, fmt.Errorf("token constraint is not a fixed value") - } - token = constraint.Constraint.GetFixedValue() - } } default: return nil, fmt.Errorf("unsupported rule: %v", rule.Id) @@ -217,27 +210,16 @@ func (fp *FeePlugin) ValidateProposedTransactions(policy vtypes.PluginPolicy, tx // Validate each proposed transaction for _, tx := range txs { for _, keysignMessage := range tx.Messages { - // Get the chain for the transaction - messageChain, err := chain.GetChain(strings.ToLower(keysignMessage.Chain.String())) + txBytes, err := base64.StdEncoding.DecodeString(keysignMessage.Message) if err != nil { - return fmt.Errorf("failed to get chain: %w", err) - } - - // Parse the transaction to validate its structure - decodedTx, err := messageChain.ParseTransaction(keysignMessage.Message) - if err != nil { - return fmt.Errorf("failed to parse transaction: %w", err) + return fmt.Errorf("failed to decode transaction: %w", err) } // Evaluate if the transaction is allowed by the policy - transactionAllowed, _, err := eng.Evaluate(recipe, messageChain, decodedTx) + _, err = eng.Evaluate(recipe, rcommon.Chain(keysignMessage.Chain), txBytes) if err != nil { return fmt.Errorf("failed to evaluate transaction: %w", err) } - - if !transactionAllowed { - return fmt.Errorf("transaction %s on %s not allowed by policy", keysignMessage.Hash, keysignMessage.Chain) - } } } diff --git a/plugin/payroll/policy.go b/plugin/payroll/policy.go index 15952fe..9a02163 100644 --- a/plugin/payroll/policy.go +++ b/plugin/payroll/policy.go @@ -1,10 +1,10 @@ package payroll import ( + "encoding/base64" "fmt" - "strings" - "github.com/vultisig/recipes/chain" + rcommon "github.com/vultisig/recipes/common" "github.com/vultisig/recipes/engine" "github.com/vultisig/verifier/plugin" vtypes "github.com/vultisig/verifier/types" @@ -25,24 +25,15 @@ func (p *Plugin) ValidateProposedTransactions(policy vtypes.PluginPolicy, txs [] for _, tx := range txs { for _, keysignMessage := range tx.Messages { - messageChain, err := chain.GetChain(strings.ToLower(keysignMessage.Chain.String())) + txBytes, err := base64.StdEncoding.DecodeString(keysignMessage.Message) if err != nil { - return fmt.Errorf("failed to get chain: %w", err) + return fmt.Errorf("failed to decode transaction: %w", err) } - decodedTx, err := messageChain.ParseTransaction(keysignMessage.Message) - if err != nil { - return fmt.Errorf("failed to parse transaction: %w", err) - } - - transactionAllowed, _, err := eng.Evaluate(recipe, messageChain, decodedTx) + _, err = eng.Evaluate(recipe, rcommon.Chain(keysignMessage.Chain), txBytes) if err != nil { return fmt.Errorf("failed to evaluate transaction: %w", err) } - - if !transactionAllowed { - return fmt.Errorf("transaction %s on %s not allowed by policy", keysignMessage.Hash, keysignMessage.Chain) - } } } diff --git a/plugin/payroll/transaction.go b/plugin/payroll/transaction.go index 280bf71..8aebd25 100644 --- a/plugin/payroll/transaction.go +++ b/plugin/payroll/transaction.go @@ -10,15 +10,17 @@ import ( "sync" "time" - gcommon "github.com/ethereum/go-ethereum/common" + ecommon "github.com/ethereum/go-ethereum/common" etypes "github.com/ethereum/go-ethereum/core/types" "github.com/hibiken/asynq" "github.com/sirupsen/logrus" "github.com/vultisig/mobile-tss-lib/tss" "github.com/vultisig/plugin/internal/scheduler" + rcommon "github.com/vultisig/recipes/common" "github.com/vultisig/recipes/ethereum" "github.com/vultisig/recipes/sdk/evm" rtypes "github.com/vultisig/recipes/types" + "github.com/vultisig/recipes/util" "github.com/vultisig/verifier/address" vcommon "github.com/vultisig/verifier/common" "github.com/vultisig/verifier/plugin" @@ -101,46 +103,37 @@ func (p *Plugin) initSign( return nil } -func getTokenID(rule *rtypes.Rule) (string, error) { - if rule == nil { - return "", fmt.Errorf("rule is nil") - } - - for _, constraint := range rule.GetParameterConstraints() { - if constraint.ParameterName == "token" { - return constraint.GetConstraint().GetFixedValue(), nil - } - } - return evm.ZeroAddress.Hex(), nil -} - func (p *Plugin) ProposeTransactions(policy vtypes.PluginPolicy) ([]vtypes.PluginKeysignRequest, error) { ctx := context.Background() err := p.ValidatePluginPolicy(policy) if err != nil { - return nil, fmt.Errorf("failed to validate plugin policy: %v", err) + return nil, fmt.Errorf("failed to validate plugin policy: %w", err) } vault, err := common.GetVaultFromPolicy(p.vaultStorage, policy, p.vaultEncryptionSecret) if err != nil { - return nil, fmt.Errorf("failed to get vault from policy: %v", err) + return nil, fmt.Errorf("failed to get vault from policy: %w", err) } ethAddress, _, _, err := address.GetAddress(vault.PublicKeyEcdsa, vault.HexChainCode, vcommon.Ethereum) if err != nil { - return nil, fmt.Errorf("failed to get eth address: %v", err) + return nil, fmt.Errorf("failed to get eth address: %w", err) } recipe, err := policy.GetRecipe() if err != nil { - return nil, fmt.Errorf("failed to get recipe from policy: %v", err) + return nil, fmt.Errorf("failed to get recipe from policy: %w", err) } - chain := vcommon.Ethereum + chain := rcommon.Ethereum ethEvmID, err := chain.EvmID() if err != nil { - return nil, fmt.Errorf("failed to get EVM ID for chain %s: %v", chain, err) + return nil, fmt.Errorf("failed to get EVM ID for chain %s: %w", chain, err) + } + nativeSymbol, err := chain.NativeSymbol() + if err != nil { + return nil, fmt.Errorf("failed to get native symbol for chain %s: %w", chain, err) } var ( @@ -152,9 +145,17 @@ func (p *Plugin) ProposeTransactions(policy vtypes.PluginPolicy) ([]vtypes.Plugi for _, _rule := range recipe.Rules { rule := _rule - tokenID, er := getTokenID(rule) + resource, er := util.ParseResource(rule.GetResource()) if er != nil { - return nil, fmt.Errorf("getTokenID: %v", er) + return nil, fmt.Errorf("failed to parse resource: %w", er) + } + + var tokenID string + switch resource.GetProtocolId() { + case nativeSymbol: + tokenID = evm.ZeroAddress.Hex() + default: + tokenID = rule.GetTarget().GetAddress() } recipient, amountStr, er := RuleToRecipientAndAmount(rule) @@ -175,7 +176,7 @@ func (p *Plugin) ProposeTransactions(policy vtypes.PluginPolicy) ([]vtypes.Plugi return fmt.Errorf("p.genUnsignedTx: %w", e) } - txHex := gcommon.Bytes2Hex(tx) + txHex := ecommon.Bytes2Hex(tx) txData, e := ethereum.DecodeUnsignedPayload(tx) if e != nil { @@ -186,7 +187,7 @@ func (p *Plugin) ProposeTransactions(policy vtypes.PluginPolicy) ([]vtypes.Plugi txToTrack, e := p.txIndexerService.CreateTx(ctx, storage.CreateTxDto{ PluginID: policy.PluginID, PolicyID: policy.ID, - ChainID: chain, + ChainID: vcommon.Chain(chain), TokenID: tokenID, FromPublicKey: policy.PublicKey, ToPublicKey: recipient, @@ -206,7 +207,7 @@ func (p *Plugin) ProposeTransactions(policy vtypes.PluginPolicy) ([]vtypes.Plugi { TxIndexerID: txToTrack.ID.String(), Message: base64.StdEncoding.EncodeToString(txHashToSign.Bytes()), - Chain: chain, + Chain: vcommon.Chain(chain), Hash: base64.StdEncoding.EncodeToString(msgHash[:]), HashFunction: vtypes.HashFunction_SHA256, }, @@ -241,10 +242,10 @@ func (p *Plugin) SigningComplete( ) error { tx, err := p.eth.Send( ctx, - gcommon.FromHex(signRequest.Transaction), - gcommon.Hex2Bytes(signature.R), - gcommon.Hex2Bytes(signature.S), - gcommon.Hex2Bytes(signature.RecoveryID), + ecommon.FromHex(signRequest.Transaction), + ecommon.Hex2Bytes(signature.R), + ecommon.Hex2Bytes(signature.S), + ecommon.Hex2Bytes(signature.RecoveryID), ) if err != nil { p.logger.WithError(err).WithField("tx_hex", signRequest.Transaction).Error("p.eth.Send") @@ -300,11 +301,10 @@ func (p *Plugin) GetRecipeSpecification() (*rtypes.RecipeSchema, error) { } return &rtypes.RecipeSchema{ - Version: 1, // Schema version - ScheduleVersion: 1, // Schedule specification version - PluginId: string(vtypes.PluginVultisigPayroll_0000), - PluginName: "Payroll Management", - PluginVersion: 1, // Convert from "0.1.0" to int32 + Version: 1, // Schema version + PluginId: string(vtypes.PluginVultisigPayroll_0000), + PluginName: "Payroll Management", + PluginVersion: 1, // Convert from "0.1.0" to int32 SupportedResources: []*rtypes.ResourcePattern{ { ResourcePath: &rtypes.ResourcePath{ @@ -313,27 +313,17 @@ func (p *Plugin) GetRecipeSpecification() (*rtypes.RecipeSchema, error) { FunctionId: "transfer", Full: "ethereum.erc20.transfer", }, + Target: rtypes.TargetType_TARGET_TYPE_ADDRESS, ParameterCapabilities: []*rtypes.ParameterConstraintCapability{ { - ParameterName: "recipient", - SupportedTypes: []rtypes.ConstraintType{ - rtypes.ConstraintType_CONSTRAINT_TYPE_FIXED, - }, - Required: true, + ParameterName: "recipient", + SupportedTypes: rtypes.ConstraintType_CONSTRAINT_TYPE_FIXED, + Required: true, }, { - ParameterName: "amount", - SupportedTypes: []rtypes.ConstraintType{ - rtypes.ConstraintType_CONSTRAINT_TYPE_FIXED, - }, - Required: true, - }, - { - ParameterName: "token", // ERC20/TRC20/etc contract address - SupportedTypes: []rtypes.ConstraintType{ - rtypes.ConstraintType_CONSTRAINT_TYPE_FIXED, - }, - Required: true, + ParameterName: "amount", + SupportedTypes: rtypes.ConstraintType_CONSTRAINT_TYPE_FIXED, + Required: true, }, }, Required: true, @@ -349,11 +339,11 @@ func (p *Plugin) GetRecipeSpecification() (*rtypes.RecipeSchema, error) { func (p *Plugin) genUnsignedTx( ctx context.Context, - chain vcommon.Chain, + chain rcommon.Chain, senderAddress, tokenID, amount, to string, ) ([]byte, error) { switch chain { - case vcommon.Ethereum: + case rcommon.Ethereum: amt, ok := new(big.Int).SetString(amount, 10) if !ok { return nil, fmt.Errorf("failed to parse amount: %s", amount) @@ -361,9 +351,9 @@ func (p *Plugin) genUnsignedTx( tx, err := p.eth.MakeAnyTransfer( ctx, - gcommon.HexToAddress(senderAddress), - gcommon.HexToAddress(to), - gcommon.HexToAddress(tokenID), + ecommon.HexToAddress(senderAddress), + ecommon.HexToAddress(to), + ecommon.HexToAddress(tokenID), amt, ) if err != nil {