diff --git a/services/wallet/responses/router_transactions.go b/services/wallet/responses/router_transactions.go index 03a315915..d1c25246b 100644 --- a/services/wallet/responses/router_transactions.go +++ b/services/wallet/responses/router_transactions.go @@ -1,8 +1,12 @@ package responses import ( + "math/big" + + "github.com/ethereum/go-ethereum/common/hexutil" "github.com/status-im/status-go/errors" "github.com/status-im/status-go/eth-node/types" + "github.com/status-im/status-go/services/wallet/requests" "github.com/status-im/status-go/services/wallet/wallettypes" ) @@ -59,6 +63,15 @@ func NewRouterSentTransaction(sendArgs *wallettypes.SendTxArgs, hash types.Hash, if sendArgs.To != nil { addr = *sendArgs.To } + if sendArgs.Value == nil { + sendArgs.Value = (*hexutil.Big)(big.NewInt(0)) + } + if sendArgs.ValueIn == nil { + sendArgs.ValueIn = (*hexutil.Big)(big.NewInt(0)) + } + if sendArgs.ValueOut == nil { + sendArgs.ValueOut = (*hexutil.Big)(big.NewInt(0)) + } return &RouterSentTransaction{ FromAddress: sendArgs.From, ToAddress: addr, @@ -72,3 +85,23 @@ func NewRouterSentTransaction(sendArgs *wallettypes.SendTxArgs, hash types.Hash, ApprovalTx: approvalTx, } } + +func (sd *SendDetails) UpdateFields(inputParams requests.RouteInputParams) { + sd.SendType = int(inputParams.SendType) + sd.FromAddress = types.Address(inputParams.AddrFrom) + sd.ToAddress = types.Address(inputParams.AddrTo) + sd.FromToken = inputParams.TokenID + sd.ToToken = inputParams.ToTokenID + if inputParams.AmountIn != nil { + sd.FromAmount = inputParams.AmountIn.String() + } + if inputParams.AmountOut != nil { + sd.ToAmount = inputParams.AmountOut.String() + } + sd.OwnerTokenBeingSent = inputParams.TokenIDIsOwnerToken + sd.Username = inputParams.Username + sd.PublicKey = inputParams.PublicKey + if inputParams.PackID != nil { + sd.PackID = inputParams.PackID.String() + } +} diff --git a/services/wallet/routeexecution/manager.go b/services/wallet/routeexecution/manager.go index c1621e6c9..823c6b13d 100644 --- a/services/wallet/routeexecution/manager.go +++ b/services/wallet/routeexecution/manager.go @@ -10,7 +10,6 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/log" - "github.com/status-im/status-go/eth-node/types" "github.com/status-im/status-go/logutils" status_common "github.com/status-im/status-go/common" @@ -81,7 +80,7 @@ func (m *Manager) BuildTransactionsFromRoute(ctx context.Context, buildInputPara m.buildInputParams = buildInputParams - updateFields(response.SendDetails, routeInputParams) + response.SendDetails.UpdateFields(routeInputParams) // notify client that sending transactions started (has 3 steps, building txs, signing txs, sending txs) signal.SendWalletEvent(signal.RouterSendingTransactionsStarted, response.SendDetails) @@ -143,7 +142,7 @@ func (m *Manager) SendRouterTransactionsWithSignatures(ctx context.Context, send return } - updateFields(response.SendDetails, routeInputParams) + response.SendDetails.UpdateFields(routeInputParams) err = m.transactionManager.ValidateAndAddSignaturesToRouterTransactions(sendInputParams.Signatures) if err != nil { @@ -219,23 +218,3 @@ func (m *Manager) SendRouterTransactionsWithSignatures(ctx context.Context, send } }() } - -func updateFields(sd *responses.SendDetails, inputParams requests.RouteInputParams) { - sd.SendType = int(inputParams.SendType) - sd.FromAddress = types.Address(inputParams.AddrFrom) - sd.ToAddress = types.Address(inputParams.AddrTo) - sd.FromToken = inputParams.TokenID - sd.ToToken = inputParams.ToTokenID - if inputParams.AmountIn != nil { - sd.FromAmount = inputParams.AmountIn.String() - } - if inputParams.AmountOut != nil { - sd.ToAmount = inputParams.AmountOut.String() - } - sd.OwnerTokenBeingSent = inputParams.TokenIDIsOwnerToken - sd.Username = inputParams.Username - sd.PublicKey = inputParams.PublicKey - if inputParams.PackID != nil { - sd.PackID = inputParams.PackID.String() - } -} diff --git a/services/wallet/routeexecution/storage/route_db.go b/services/wallet/routeexecution/storage/route_db.go index 3d2947a32..2f0bb65dc 100644 --- a/services/wallet/routeexecution/storage/route_db.go +++ b/services/wallet/routeexecution/storage/route_db.go @@ -55,6 +55,14 @@ func (db *DB) GetRouteData(uuid string) (*wallettypes.RouteData, error) { return getRouteData(db.db, uuid) } +func (db *DB) GetRouteDataByHash(chainID uint64, txHash types.Hash) (*wallettypes.RouteData, error) { + uuid, err := getUuidForTxOnChain(db.db, chainID, txHash) + if err != nil { + return nil, err + } + return db.GetRouteData(uuid) +} + func putRouteInputParams(creator sqlite.StatementCreator, p *requests.RouteInputParams) error { q := sq.Replace("route_input_parameters"). SetMap(sq.Eq{"route_input_params_json": &sqlite.JSONBlob{Data: p}}) @@ -394,3 +402,24 @@ func getPathTransaction(creator sqlite.StatementCreator, uuid string, pathIdx in return tx, nil } + +func getUuidForTxOnChain(creator sqlite.StatementCreator, chainID uint64, txHash types.Hash) (string, error) { + var uuid string + q := sq.Select("uuid"). + From("route_path_transactions"). + Where(sq.Eq{"chain_id": chainID, "tx_hash": txHash[:]}) + + query, args, err := q.ToSql() + if err != nil { + return uuid, err + } + + stmt, err := creator.Prepare(query) + if err != nil { + return uuid, err + } + defer stmt.Close() + + err = stmt.QueryRow(args...).Scan(&uuid) + return uuid, err +} diff --git a/services/wallet/routeexecution/storage/route_db_test.go b/services/wallet/routeexecution/storage/route_db_test.go index eac4837e9..d7c0cf3b5 100644 --- a/services/wallet/routeexecution/storage/route_db_test.go +++ b/services/wallet/routeexecution/storage/route_db_test.go @@ -44,6 +44,20 @@ func Test_PutRouteData(t *testing.T) { readRouteData, err := routeDB.GetRouteData(routeData.RouteInputParams.Uuid) require.NoError(t, err) require.EqualExportedValues(t, routeData, readRouteData) + + for _, pathData := range routeData.PathsData { + if pathData.IsTxPlaced() { + readRouteData, err = routeDB.GetRouteDataByHash(pathData.RouterPath.FromChain.ChainID, pathData.TxData.SentHash) + require.NoError(t, err) + require.EqualExportedValues(t, routeData, readRouteData) + } + + if pathData.IsApprovalPlaced() { + readRouteData, err = routeDB.GetRouteDataByHash(pathData.RouterPath.FromChain.ChainID, pathData.ApprovalTxData.SentHash) + require.NoError(t, err) + require.EqualExportedValues(t, routeData, readRouteData) + } + } }) } } diff --git a/services/wallet/wallettypes/types.go b/services/wallet/wallettypes/types.go index 3dfee41fb..5aa63e794 100644 --- a/services/wallet/wallettypes/types.go +++ b/services/wallet/wallettypes/types.go @@ -66,16 +66,16 @@ type SendTxArgs struct { // additional data - version SendTxArgsVersion0 MultiTransactionID wallet_common.MultiTransactionIDType `json:"multiTransactionID"` - Symbol string `json:"-"` + Symbol string `json:"symbol"` // additional data - version SendTxArgsVersion1 - ValueIn *hexutil.Big `json:"-"` - ValueOut *hexutil.Big `json:"-"` - FromChainID uint64 `json:"-"` - ToChainID uint64 `json:"-"` - FromTokenID string `json:"-"` - ToTokenID string `json:"-"` - ToContractAddress types.Address `json:"-"` // represents address of the contract that needs to be used in order to send assets, like ERC721 or ERC1155 tx - SlippagePercentage float32 `json:"-"` + ValueIn *hexutil.Big `json:"valueIn"` + ValueOut *hexutil.Big `json:"valueOut"` + FromChainID uint64 `json:"fromChainID"` + ToChainID uint64 `json:"toChainID"` + FromTokenID string `json:"fromTokenID"` + ToTokenID string `json:"toTokenID"` + ToContractAddress types.Address `json:"toContractAddress"` // represents address of the contract that needs to be used in order to send assets, like ERC721 or ERC1155 tx + SlippagePercentage float32 `json:"slippagePercentage"` } // Valid checks whether this structure is filled in correctly. diff --git a/transactions/pendingtxtracker.go b/transactions/pendingtxtracker.go index a297f89ed..43533db17 100644 --- a/transactions/pendingtxtracker.go +++ b/transactions/pendingtxtracker.go @@ -17,6 +17,7 @@ import ( "github.com/ethereum/go-ethereum/event" "github.com/ethereum/go-ethereum/p2p" ethrpc "github.com/ethereum/go-ethereum/rpc" + ethTypes "github.com/status-im/status-go/eth-node/types" "github.com/status-im/status-go/logutils" "github.com/status-im/status-go/rpc" @@ -24,6 +25,8 @@ import ( "github.com/status-im/status-go/services/wallet/bigint" "github.com/status-im/status-go/services/wallet/common" wallet_common "github.com/status-im/status-go/services/wallet/common" + "github.com/status-im/status-go/services/wallet/responses" + "github.com/status-im/status-go/services/wallet/routeexecution/storage" "github.com/status-im/status-go/services/wallet/walletevent" ) @@ -63,21 +66,29 @@ type TxIdentity struct { Hash eth.Hash `json:"hash"` } +type TxDetails struct { + SendDetails *responses.SendDetails `json:"sendDetails"` + SentTransactions []*responses.RouterSentTransaction `json:"sentTransactions"` +} + type PendingTxUpdatePayload struct { TxIdentity + TxDetails Deleted bool `json:"deleted"` } type StatusChangedPayload struct { TxIdentity + TxDetails Status TxStatus `json:"status"` } // PendingTxTracker implements StatusService in common/status_node_service.go type PendingTxTracker struct { - db *sql.DB - trackedTxDB *DB - rpcClient rpc.ClientInterface + db *sql.DB + routeExecutionStorage *storage.DB + trackedTxDB *DB + rpcClient rpc.ClientInterface rpcFilter *rpcfilters.Service eventFeed *event.Feed @@ -88,12 +99,13 @@ type PendingTxTracker struct { func NewPendingTxTracker(db *sql.DB, rpcClient rpc.ClientInterface, rpcFilter *rpcfilters.Service, eventFeed *event.Feed, checkInterval time.Duration) *PendingTxTracker { tm := &PendingTxTracker{ - db: db, - trackedTxDB: NewDB(db), - rpcClient: rpcClient, - eventFeed: eventFeed, - rpcFilter: rpcFilter, - logger: logutils.ZapLogger().Named("PendingTxTracker"), + db: db, + routeExecutionStorage: storage.NewDB(db), + trackedTxDB: NewDB(db), + rpcClient: rpcClient, + eventFeed: eventFeed, + rpcFilter: rpcFilter, + logger: logutils.ZapLogger().Named("PendingTxTracker"), } tm.taskRunner = NewConditionalRepeater(checkInterval, func(ctx context.Context) bool { return tm.fetchAndUpdateDB(ctx) @@ -335,6 +347,37 @@ func (tm *PendingTxTracker) updateDBStatus(ctx context.Context, chainID common.C return res, nil } +func (tm *PendingTxTracker) updateTxDetails(txDetails *TxDetails, chainID uint64, txHash ethTypes.Hash) { + if txDetails == nil { + txDetails = &TxDetails{} + } + txDetails.SendDetails = &responses.SendDetails{} + routeData, err := tm.routeExecutionStorage.GetRouteDataByHash(chainID, txHash) + if err != nil { + tm.logger.Warn("Missing tx data ", zap.Stringer("hash", txHash), zap.Error(err)) + } + if routeData != nil { + if routeData.RouteInputParams != nil { + txDetails.SendDetails.UpdateFields(*routeData.RouteInputParams) + } + + for _, pd := range routeData.PathsData { + if pd.IsApprovalPlaced() && pd.ApprovalTxData.SentHash == txHash { + txDetails.SentTransactions = append(txDetails.SentTransactions, responses.NewRouterSentTransaction( + pd.ApprovalTxData.TxArgs, + pd.ApprovalTxData.SentHash, + true)) + } + if pd.IsTxPlaced() && pd.TxData.SentHash == txHash { + txDetails.SentTransactions = append(txDetails.SentTransactions, responses.NewRouterSentTransaction( + pd.TxData.TxArgs, + pd.TxData.SentHash, + false)) + } + } + } +} + func (tm *PendingTxTracker) emitNotifications(chainID common.ChainID, changes []txStatusRes) { if tm.eventFeed != nil { for _, change := range changes { @@ -346,6 +389,8 @@ func (tm *PendingTxTracker) emitNotifications(chainID common.ChainID, changes [] Status: change.Status, } + tm.updateTxDetails(&payload.TxDetails, chainID.ToUint(), ethTypes.Hash(change.hash)) + jsonPayload, err := json.Marshal(payload) if err != nil { tm.logger.Error("Failed to marshal pending transaction status", zap.Stringer("hash", change.hash), zap.Error(err)) @@ -688,6 +733,8 @@ func (tm *PendingTxTracker) addPending(transaction *PendingTransaction) error { } func (tm *PendingTxTracker) notifyPendingTransactionListeners(payload PendingTxUpdatePayload, addresses []eth.Address, timestamp uint64) { + tm.updateTxDetails(&payload.TxDetails, payload.ChainID.ToUint(), ethTypes.Hash(payload.Hash)) + jsonPayload, err := json.Marshal(payload) if err != nil { tm.logger.Error("Failed to marshal PendingTxUpdatePayload", zap.Stringer("hash", payload.Hash), zap.Error(err))