11package fees
22
33import (
4+ "context"
45 "encoding/hex"
56 "fmt"
67 "math/big"
@@ -11,8 +12,13 @@ import (
1112 "github.com/ethereum/go-ethereum/common/hexutil"
1213 etypes "github.com/ethereum/go-ethereum/core/types"
1314 "github.com/ethereum/go-ethereum/rlp"
15+ "github.com/google/uuid"
16+ "github.com/vultisig/plugin/common"
1417 reth "github.com/vultisig/recipes/ethereum"
1518 "github.com/vultisig/recipes/sdk/evm"
19+ "github.com/vultisig/verifier/address"
20+ vcommon "github.com/vultisig/verifier/common"
21+ vtypes "github.com/vultisig/verifier/types"
1622)
1723
1824func getHash (inTx evm.UnsignedTx , r , s , v []byte , chainID * big.Int ) (* etypes.Transaction , error ) {
@@ -34,76 +40,87 @@ func getHash(inTx evm.UnsignedTx, r, s, v []byte, chainID *big.Int) (*etypes.Tra
3440 return outTx , nil
3541}
3642
37- type erc20tx struct {
43+ type erc20Data struct {
3844 to ecommon.Address `json:"to"`
3945 amount * big.Int `json:"amount"`
4046 token ecommon.Address `json:"token"`
4147}
4248
43- func decodeTx (rawHex string ) (* erc20tx , error ) {
44- type unsignedDynamicFeeTx struct {
45- ChainID * big.Int
46- Nonce uint64
47- GasTipCap * big.Int
48- GasFeeCap * big.Int
49- Gas uint64
50- To * ecommon.Address
51- Value * big.Int
52- Data []byte
53- AccessList etypes.AccessList
54- }
49+ type unsignedDynamicFeeTx struct {
50+ ChainID * big.Int
51+ Nonce uint64
52+ GasTipCap * big.Int
53+ GasFeeCap * big.Int
54+ Gas uint64
55+ To * ecommon.Address
56+ Value * big.Int
57+ Data []byte
58+ AccessList etypes.AccessList
59+ }
5560
61+ func decodeUnsignedTx (rawHex string ) (* unsignedDynamicFeeTx , error ) {
5662 rawHex = strings .TrimPrefix (rawHex , "0x" )
57-
5863 rawBytes , err := hex .DecodeString (rawHex )
5964 if err != nil {
6065 return nil , fmt .Errorf ("hex decode failed: %w" , err )
6166 }
6267
63- // Check transaction type (EIP-1559 is 0x02)
6468 if len (rawBytes ) == 0 || rawBytes [0 ] != 0x02 {
6569 return nil , fmt .Errorf ("unsupported transaction type: 0x%02x" , rawBytes [0 ])
6670 }
6771
72+ // Decode RLP (strip type byte)
6873 tx := new (unsignedDynamicFeeTx )
69- err = rlp .DecodeBytes (rawBytes [1 :], tx )
70- if err != nil {
74+ if err := rlp .DecodeBytes (rawBytes [1 :], tx ); err != nil {
7175 return nil , fmt .Errorf ("rlp decode failed: %w" , err )
7276 }
7377
74- // Parse the ERC20 transfer ABI
78+ return tx , nil
79+ }
80+
81+ // pass in a types.Transaction or a unsignedDynamicFeeTx
82+ func parseErc20Tx [T any ](transaction T ) (* erc20Data , error ) {
83+ // Decode ERC20 transfer ABI
7584 const transferABI = `[{"name":"transfer","type":"function","inputs":[{"name":"to","type":"address"},{"name":"value","type":"uint256"}],"outputs":[{"name":"","type":"bool"}]}]`
76- parsedABI , err := abi .JSON (strings .NewReader (transferABI ))
77- if err != nil {
78- return nil , fmt .Errorf ("failed to parse ABI" )
85+ parsedABI , _ := abi .JSON (strings .NewReader (transferABI ))
86+
87+ var data []byte
88+ var to * ecommon.Address
89+
90+ switch tx := any (transaction ).(type ) {
91+ case * etypes.Transaction :
92+ data = tx .Data ()
93+ to = tx .To ()
94+ case * unsignedDynamicFeeTx :
95+ data = tx .Data
96+ to = tx .To
97+ default :
98+ return nil , fmt .Errorf ("unsupported transaction struct type" )
7999 }
80100
81- // Get the method by selector
82- method , err := parsedABI .MethodById (tx .Data [:4 ])
101+ method , err := parsedABI .MethodById (data [:4 ])
83102 if err != nil {
84103 return nil , fmt .Errorf ("unknown method ID" )
85104 }
86105
87- // Decode the arguments
88106 args := make (map [string ]interface {})
89- if err := method .Inputs .UnpackIntoMap (args , tx . Data [4 :]); err != nil {
90- return nil , fmt .Errorf ("failed get recipient and amount from tx " )
107+ if err := method .Inputs .UnpackIntoMap (args , data [4 :]); err != nil {
108+ return nil , fmt .Errorf ("failed to unpack args " )
91109 }
92110
93111 recipient , ok := args ["to" ].(ecommon.Address )
94112 if ! ok {
95- return nil , fmt .Errorf ("invalid recipient address in tx data " )
113+ return nil , fmt .Errorf ("invalid recipient type " )
96114 }
97-
98115 amount , ok := args ["value" ].(* big.Int )
99116 if ! ok {
100- return nil , fmt .Errorf ("invalid amount in tx data " )
117+ return nil , fmt .Errorf ("invalid amount type " )
101118 }
102119
103- return & erc20tx {
120+ return & erc20Data {
104121 to : recipient ,
105122 amount : amount ,
106- token : * tx . To ,
123+ token : * to ,
107124 }, nil
108125}
109126
@@ -113,3 +130,45 @@ func hexutilDecode(hexStr string) ([]byte, error) {
113130 }
114131 return hexutil .Decode (hexStr )
115132}
133+
134+ func appendSignature (inTx evm.UnsignedTx , r , s , v []byte , chainID * big.Int ) (* etypes.Transaction , error ) {
135+ var sig []byte
136+ sig = append (sig , r ... )
137+ sig = append (sig , s ... )
138+ sig = append (sig , v ... )
139+
140+ inTxDecoded , err := reth .DecodeUnsignedPayload (inTx )
141+ if err != nil {
142+ return nil , fmt .Errorf ("reth.DecodeUnsignedPayload: %w" , err )
143+ }
144+
145+ outTx , err := etypes .NewTx (inTxDecoded ).WithSignature (etypes .LatestSignerForChainID (chainID ), sig )
146+ if err != nil {
147+ return nil , fmt .Errorf ("types.NewTx.WithSignature: %w" , err )
148+ }
149+
150+ return outTx , nil
151+ }
152+
153+ func (fp * FeePlugin ) getEthAddressFromFeePolicy (policy vtypes.PluginPolicy ) (string , error ) {
154+ vault , err := common .GetVaultFromPolicy (fp .vaultStorage , policy , fp .encryptionSecret )
155+ if err != nil {
156+ return "" , fmt .Errorf ("failed to get vault from policy: %v" , err )
157+ }
158+
159+ // Get the ethereum derived addresses from the vaults master public key
160+ fromAddress , _ , _ , err := address .GetAddress (vault .PublicKeyEcdsa , vault .HexChainCode , vcommon .Ethereum )
161+ if err != nil {
162+ return "" , fmt .Errorf ("failed to get eth address: %v" , err )
163+ }
164+
165+ return fromAddress , nil
166+ }
167+
168+ func (fp * FeePlugin ) getEthAddressFromFeePolicyId (policyId uuid.UUID ) (string , error ) {
169+ policy , err := fp .db .GetPluginPolicy (context .Background (), policyId )
170+ if err != nil {
171+ return "" , fmt .Errorf ("failed to get policy: %v" , err )
172+ }
173+ return fp .getEthAddressFromFeePolicy (* policy )
174+ }
0 commit comments