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  }