github.com/status-im/status-go@v1.1.0/services/wallet/transfer/transaction_manager.go (about) 1 package transfer 2 3 import ( 4 "fmt" 5 "math/big" 6 "time" 7 8 ethTypes "github.com/ethereum/go-ethereum/core/types" 9 10 "github.com/ethereum/go-ethereum/common" 11 "github.com/ethereum/go-ethereum/common/hexutil" 12 "github.com/ethereum/go-ethereum/event" 13 "github.com/status-im/status-go/account" 14 "github.com/status-im/status-go/eth-node/crypto" 15 "github.com/status-im/status-go/eth-node/types" 16 "github.com/status-im/status-go/multiaccounts/accounts" 17 "github.com/status-im/status-go/params" 18 wallet_common "github.com/status-im/status-go/services/wallet/common" 19 "github.com/status-im/status-go/services/wallet/router/pathprocessor" 20 "github.com/status-im/status-go/transactions" 21 ) 22 23 type SignatureDetails struct { 24 R string `json:"r"` 25 S string `json:"s"` 26 V string `json:"v"` 27 } 28 29 type TransactionDescription struct { 30 chainID uint64 31 from common.Address 32 builtTx *ethTypes.Transaction 33 signature []byte 34 } 35 36 type TransactionManager struct { 37 storage MultiTransactionStorage 38 gethManager *account.GethManager 39 transactor transactions.TransactorIface 40 config *params.NodeConfig 41 accountsDB accounts.AccountsStorage 42 pendingTracker *transactions.PendingTxTracker 43 eventFeed *event.Feed 44 45 multiTransactionForKeycardSigning *MultiTransaction 46 multipathTransactionsData []*pathprocessor.MultipathProcessorTxArgs 47 transactionsForKeycardSigning map[common.Hash]*TransactionDescription 48 } 49 50 type MultiTransactionStorage interface { 51 CreateMultiTransaction(tx *MultiTransaction) error 52 ReadMultiTransactions(details *MultiTxDetails) ([]*MultiTransaction, error) 53 UpdateMultiTransaction(tx *MultiTransaction) error 54 DeleteMultiTransaction(id wallet_common.MultiTransactionIDType) error 55 } 56 57 func NewTransactionManager( 58 storage MultiTransactionStorage, 59 gethManager *account.GethManager, 60 transactor transactions.TransactorIface, 61 config *params.NodeConfig, 62 accountsDB accounts.AccountsStorage, 63 pendingTxManager *transactions.PendingTxTracker, 64 eventFeed *event.Feed, 65 ) *TransactionManager { 66 return &TransactionManager{ 67 storage: storage, 68 gethManager: gethManager, 69 transactor: transactor, 70 config: config, 71 accountsDB: accountsDB, 72 pendingTracker: pendingTxManager, 73 eventFeed: eventFeed, 74 } 75 } 76 77 var ( 78 emptyHash = common.Hash{} 79 ) 80 81 type MultiTransactionType uint8 82 83 const ( 84 MultiTransactionSend = iota 85 MultiTransactionSwap 86 MultiTransactionBridge 87 MultiTransactionApprove 88 MultiTransactionTypeInvalid = 255 89 ) 90 91 type MultiTransaction struct { 92 ID wallet_common.MultiTransactionIDType `json:"id"` 93 Timestamp uint64 `json:"timestamp"` 94 FromNetworkID uint64 `json:"fromNetworkID"` 95 ToNetworkID uint64 `json:"toNetworkID"` 96 FromTxHash common.Hash `json:"fromTxHash"` 97 ToTxHash common.Hash `json:"toTxHash"` 98 FromAddress common.Address `json:"fromAddress"` 99 ToAddress common.Address `json:"toAddress"` 100 FromAsset string `json:"fromAsset"` 101 ToAsset string `json:"toAsset"` 102 FromAmount *hexutil.Big `json:"fromAmount"` 103 ToAmount *hexutil.Big `json:"toAmount"` 104 Type MultiTransactionType `json:"type"` 105 CrossTxID string 106 } 107 108 type MultiTransactionCommand struct { 109 FromAddress common.Address `json:"fromAddress"` 110 ToAddress common.Address `json:"toAddress"` 111 FromAsset string `json:"fromAsset"` 112 ToAsset string `json:"toAsset"` 113 FromAmount *hexutil.Big `json:"fromAmount"` 114 ToAmount *hexutil.Big `json:"toAmount"` 115 Type MultiTransactionType `json:"type"` 116 } 117 118 type MultiTransactionCommandResult struct { 119 ID int64 `json:"id"` 120 Hashes map[uint64][]types.Hash `json:"hashes"` 121 } 122 123 type TransactionIdentity struct { 124 ChainID wallet_common.ChainID `json:"chainId"` 125 Hash common.Hash `json:"hash"` 126 Address common.Address `json:"address"` 127 } 128 129 type TxResponse struct { 130 KeyUID string `json:"keyUid,omitempty"` 131 Address types.Address `json:"address,omitempty"` 132 AddressPath string `json:"addressPath,omitempty"` 133 SignOnKeycard bool `json:"signOnKeycard,omitempty"` 134 ChainID uint64 `json:"chainId,omitempty"` 135 MessageToSign interface{} `json:"messageToSign,omitempty"` 136 TxArgs transactions.SendTxArgs `json:"txArgs,omitempty"` 137 RawTx string `json:"rawTx,omitempty"` 138 TxHash common.Hash `json:"txHash,omitempty"` 139 } 140 141 func NewMultiTransaction(timestamp uint64, fromNetworkID, toNetworkID uint64, fromTxHash, toTxHash common.Hash, fromAddress, toAddress common.Address, fromAsset, toAsset string, fromAmount, toAmount *hexutil.Big, txType MultiTransactionType, crossTxID string) *MultiTransaction { 142 if timestamp == 0 { 143 timestamp = uint64(time.Now().Unix()) 144 } 145 146 return &MultiTransaction{ 147 ID: multiTransactionIDGenerator(), 148 Timestamp: timestamp, 149 FromNetworkID: fromNetworkID, 150 ToNetworkID: toNetworkID, 151 FromTxHash: fromTxHash, 152 ToTxHash: toTxHash, 153 FromAddress: fromAddress, 154 ToAddress: toAddress, 155 FromAsset: fromAsset, 156 ToAsset: toAsset, 157 FromAmount: fromAmount, 158 ToAmount: toAmount, 159 Type: txType, 160 CrossTxID: crossTxID, 161 } 162 } 163 164 func (tm *TransactionManager) SignMessage(message types.HexBytes, account *types.Key) (string, error) { 165 if account == nil || account.PrivateKey == nil { 166 return "", fmt.Errorf("account or private key is nil") 167 } 168 169 signature, err := crypto.Sign(message[:], account.PrivateKey) 170 171 return types.EncodeHex(signature), err 172 } 173 174 func (tm *TransactionManager) BuildTransaction(chainID uint64, sendArgs transactions.SendTxArgs) (response *TxResponse, err error) { 175 account, err := tm.accountsDB.GetAccountByAddress(sendArgs.From) 176 if err != nil { 177 return nil, fmt.Errorf("failed to resolve account: %w", err) 178 } 179 180 kp, err := tm.accountsDB.GetKeypairByKeyUID(account.KeyUID) 181 if err != nil { 182 return nil, err 183 } 184 185 txBeingSigned, _, err := tm.transactor.ValidateAndBuildTransaction(chainID, sendArgs, -1) 186 if err != nil { 187 return nil, err 188 } 189 190 // Set potential missing fields that were added while building the transaction 191 if sendArgs.Value == nil { 192 value := hexutil.Big(*txBeingSigned.Value()) 193 sendArgs.Value = &value 194 } 195 if sendArgs.Nonce == nil { 196 nonce := hexutil.Uint64(txBeingSigned.Nonce()) 197 sendArgs.Nonce = &nonce 198 } 199 if sendArgs.Gas == nil { 200 gas := hexutil.Uint64(txBeingSigned.Gas()) 201 sendArgs.Gas = &gas 202 } 203 if sendArgs.GasPrice == nil { 204 gasPrice := hexutil.Big(*txBeingSigned.GasPrice()) 205 sendArgs.GasPrice = &gasPrice 206 } 207 208 if sendArgs.IsDynamicFeeTx() { 209 if sendArgs.MaxPriorityFeePerGas == nil { 210 maxPriorityFeePerGas := hexutil.Big(*txBeingSigned.GasTipCap()) 211 sendArgs.MaxPriorityFeePerGas = &maxPriorityFeePerGas 212 } 213 if sendArgs.MaxFeePerGas == nil { 214 maxFeePerGas := hexutil.Big(*txBeingSigned.GasFeeCap()) 215 sendArgs.MaxFeePerGas = &maxFeePerGas 216 } 217 } 218 219 signer := ethTypes.NewLondonSigner(new(big.Int).SetUint64(chainID)) 220 221 return &TxResponse{ 222 KeyUID: account.KeyUID, 223 Address: account.Address, 224 AddressPath: account.Path, 225 SignOnKeycard: kp.MigratedToKeycard(), 226 ChainID: chainID, 227 MessageToSign: signer.Hash(txBeingSigned), 228 TxArgs: sendArgs, 229 }, nil 230 } 231 232 func (tm *TransactionManager) BuildRawTransaction(chainID uint64, sendArgs transactions.SendTxArgs, signature []byte) (response *TxResponse, err error) { 233 tx, err := tm.transactor.BuildTransactionWithSignature(chainID, sendArgs, signature) 234 if err != nil { 235 return nil, err 236 } 237 238 data, err := tx.MarshalBinary() 239 if err != nil { 240 return nil, err 241 } 242 243 return &TxResponse{ 244 ChainID: chainID, 245 TxArgs: sendArgs, 246 RawTx: types.EncodeHex(data), 247 TxHash: tx.Hash(), 248 }, nil 249 } 250 251 func (tm *TransactionManager) SendTransactionWithSignature(chainID uint64, sendArgs transactions.SendTxArgs, signature []byte) (hash types.Hash, err error) { 252 txWithSignature, err := tm.transactor.BuildTransactionWithSignature(chainID, sendArgs, signature) 253 if err != nil { 254 return hash, err 255 } 256 257 hash, err = tm.transactor.SendTransactionWithSignature(common.Address(sendArgs.From), sendArgs.Symbol, sendArgs.MultiTransactionID, txWithSignature) 258 return hash, err 259 }