diff --git a/client/asset/btc/btc.go b/client/asset/btc/btc.go index 521a8f2385..7d7e633649 100644 --- a/client/asset/btc/btc.go +++ b/client/asset/btc/btc.go @@ -2419,7 +2419,7 @@ func (btc *baseWallet) submitMultiSplitTx(fundingCoins asset.Coins, spents []*Ou for _, txOut := range tx.TxOut { totalOut += uint64(txOut.Value) } - btc.addTxToHistory(asset.Split, txHash[:], 0, totalIn-totalOut, true) + btc.addTxToHistory(asset.Split, txHash[:], 0, totalIn-totalOut, nil, nil, true) success = true return coins, totalIn - totalOut, nil @@ -2675,7 +2675,7 @@ func (btc *baseWallet) split(value uint64, lots uint64, outputs []*Output, input totalOut += uint64(msgTx.TxOut[i].Value) } - btc.addTxToHistory(asset.Split, txHash[:], 0, coinSum-totalOut, true) + btc.addTxToHistory(asset.Split, txHash[:], 0, coinSum-totalOut, nil, nil, true) fundingCoins = map[OutPoint]*UTxO{op.Pt: { TxHash: op.txHash(), @@ -3110,7 +3110,7 @@ func accelerateOrder(btc *baseWallet, swapCoins, accelerationCoins []dex.Bytes, } txHash := btc.hashTx(signedTx) - btc.addTxToHistory(asset.Acceleration, txHash[:], 0, fees, true) + btc.addTxToHistory(asset.Acceleration, txHash[:], 0, fees, nil, nil, true) // Delete the old change from the cache btc.cm.ReturnOutPoint(NewOutPoint(changeTxHash, changeVout)) @@ -3436,6 +3436,8 @@ func (btc *baseWallet) markTxAsSubmitted(id dex.Bytes) { if err != nil { btc.log.Errorf("failed to mark tx as submitted in tx history db: %v", err) } + + btc.emit.TransactionNote(wt.WalletTransaction, true) } func (btc *baseWallet) removeTxFromHistory(id dex.Bytes) { @@ -3450,7 +3452,8 @@ func (btc *baseWallet) removeTxFromHistory(id dex.Bytes) { } } -func (btc *baseWallet) addTxToHistory(txType asset.TransactionType, id dex.Bytes, balanceDelta int64, fees uint64, submitted bool) { +func (btc *baseWallet) addTxToHistory(txType asset.TransactionType, id dex.Bytes, amount uint64, fees uint64, + bondInfo *asset.BondTxInfo, recipient *string, submitted bool) { txHistoryDB := btc.txDB() if txHistoryDB == nil { return @@ -3458,10 +3461,12 @@ func (btc *baseWallet) addTxToHistory(txType asset.TransactionType, id dex.Bytes wt := &extendedWalletTx{ WalletTransaction: &asset.WalletTransaction{ - Type: txType, - ID: id, - BalanceDelta: balanceDelta, - Fees: fees, + Type: txType, + ID: id, + Amount: amount, + Fees: fees, + BondInfo: bondInfo, + Recipient: recipient, }, Submitted: submitted, } @@ -3477,6 +3482,10 @@ func (btc *baseWallet) addTxToHistory(txType asset.TransactionType, id dex.Bytes if err != nil { btc.log.Errorf("failed to store tx in tx history db: %v", err) } + + if submitted { + btc.emit.TransactionNote(wt.WalletTransaction, true) + } } // Swap sends the swaps in a single transaction and prepares the receipts. The @@ -3607,7 +3616,7 @@ func (btc *baseWallet) Swap(swaps *asset.Swaps) ([]asset.Receipt, asset.Coin, ui return nil, nil, 0, err } - btc.addTxToHistory(asset.Swap, txHash[:], -int64(totalOut), fees, true) + btc.addTxToHistory(asset.Swap, txHash[:], totalOut, fees, nil, nil, true) // If change is nil, return a nil asset.Coin. var changeCoin asset.Coin @@ -3771,7 +3780,7 @@ func (btc *baseWallet) Redeem(form *asset.RedeemForm) ([]dex.Bytes, asset.Coin, return nil, nil, 0, err } - btc.addTxToHistory(asset.Redeem, txHash[:], int64(totalIn), fee, true) + btc.addTxToHistory(asset.Redeem, txHash[:], totalIn, fee, nil, nil, true) // Log the change output. coinIDs := make([]dex.Bytes, 0, len(form.Redemptions)) @@ -4028,7 +4037,7 @@ func (btc *baseWallet) Refund(coinID, contract dex.Bytes, feeRate uint64) (dex.B if len(msgTx.TxOut) > 0 { // something went very wrong if not true fee = uint64(utxo.Value - msgTx.TxOut[0].Value) } - btc.addTxToHistory(asset.Refund, txHash[:], utxo.Value, fee, true) + btc.addTxToHistory(asset.Refund, txHash[:], uint64(utxo.Value), fee, nil, nil, true) return ToCoinID(refundHash, 0), nil } @@ -4264,11 +4273,6 @@ func (btc *baseWallet) send(address string, val uint64, feeRate uint64, subtract return nil, 0, 0, fmt.Errorf("PayToAddrScript error: %w", err) } - selfSend, err := btc.OwnsDepositAddress(address) - if err != nil { - return nil, 0, 0, fmt.Errorf("error checking address ownership: %w", err) - } - baseSize := dexbtc.MinimumTxOverhead if btc.segwit { baseSize += dexbtc.P2WPKHOutputSize * 2 @@ -4314,12 +4318,16 @@ func (btc *baseWallet) send(address string, val uint64, feeRate uint64, subtract totalOut += uint64(txOut.Value) } - var amtSent int64 - if !selfSend { - amtSent = -int64(toSend) + selfSend, err := btc.OwnsDepositAddress(address) + if err != nil { + return nil, 0, 0, fmt.Errorf("error checking address ownership: %w", err) + } + txType := asset.Send + if selfSend { + txType = asset.SelfSend } - btc.addTxToHistory(asset.Send, txHash[:], amtSent, totalIn-totalOut, true) + btc.addTxToHistory(txType, txHash[:], toSend, totalIn-totalOut, nil, &address, true) return txHash, 0, toSend, nil } @@ -4911,7 +4919,12 @@ func (btc *baseWallet) MakeBondTx(ver uint16, amt, feeRate uint64, lockTime time } success = true - btc.addTxToHistory(asset.CreateBond, txid[:], -int64(amt), fee, false) + bondInfo := &asset.BondTxInfo{ + AccountID: acctID, + LockTime: uint64(lockTimeSec), + BondID: pkh, + } + btc.addTxToHistory(asset.CreateBond, txid[:], amt, fee, bondInfo, nil, false) txIDToRemoveFromHistory = txid return bond, abandon, nil @@ -5000,6 +5013,10 @@ func (btc *baseWallet) RefundBond(ctx context.Context, ver uint16, coinID, scrip if ver != 0 { return nil, errors.New("only version 0 bonds supported") } + lockTime, pkhPush, err := dexbtc.ExtractBondDetailsV0(0, script) + if err != nil { + return nil, err + } txHash, vout, err := decodeCoinID(coinID) if err != nil { return nil, err @@ -5021,8 +5038,11 @@ func (btc *baseWallet) RefundBond(ctx context.Context, ver uint16, coinID, scrip if len(msgTx.TxOut) == 1 { fees = amt - uint64(msgTx.TxOut[0].Value) } - btc.addTxToHistory(asset.RedeemBond, txID[:], int64(amt), fees, true) - + bondInfo := &asset.BondTxInfo{ + LockTime: uint64(lockTime), + BondID: pkhPush, + } + btc.addTxToHistory(asset.RedeemBond, txID[:], amt, fees, bondInfo, nil, true) return NewOutput(txHash, 0, uint64(msgTx.TxOut[0].Value)), nil } @@ -5199,25 +5219,8 @@ func (btc *intermediaryWallet) checkPendingTxs(tip uint64) { fee = toSatoshi(*tx.Fee) } } - wt := &extendedWalletTx{ - WalletTransaction: &asset.WalletTransaction{ - Type: asset.Receive, - ID: txID, - BalanceDelta: int64(toSatoshi(tx.Amount)), - Fees: fee, - }, - Submitted: true, - } - err = txHistoryDB.storeTx(wt) - if err != nil { - btc.log.Errorf("Error storing tx %s: %v", tx.TxID, err) - } - if !wt.Confirmed || err != nil { - btc.pendingTxsMtx.Lock() - btc.pendingTxs[*txHash] = wt - btc.pendingTxsMtx.Unlock() - } + btc.addTxToHistory(asset.Receive, txID, toSatoshi(tx.Amount), fee, nil, nil, true) } } } @@ -5267,6 +5270,7 @@ func (btc *intermediaryWallet) checkPendingTxs(tip uint64) { btc.log.Errorf("Error getting block height for %s: %v", blockHash, err) return } + tx.Timestamp = gtr.BlockTime if tx.BlockNumber != uint64(blockHeight) { tx.BlockNumber = uint64(blockHeight) updated = true @@ -5296,6 +5300,7 @@ func (btc *intermediaryWallet) checkPendingTxs(tip uint64) { delete(btc.pendingTxs, hash) btc.pendingTxsMtx.Unlock() } + btc.emit.TransactionNote(tx.WalletTransaction, false) } } diff --git a/client/asset/eth/eth.go b/client/asset/eth/eth.go index d82ea6f572..4226f51d9c 100644 --- a/client/asset/eth/eth.go +++ b/client/asset/eth/eth.go @@ -1968,7 +1968,7 @@ func (w *ETHWallet) Swap(swaps *asset.Swaps) ([]asset.Receipt, asset.Coin, uint6 } txHash := tx.Hash() - w.addToTxHistory(tx.Nonce(), -int64(swapVal), swaps.FeeRate*gasLimit, 0, w.assetID, txHash[:], asset.Swap) + w.addToTxHistory(tx.Nonce(), swapVal, swaps.FeeRate*gasLimit, 0, w.assetID, txHash[:], asset.Swap, nil) receipts := make([]asset.Receipt, 0, n) for _, swap := range swaps.Contracts { @@ -2059,7 +2059,7 @@ func (w *TokenWallet) Swap(swaps *asset.Swaps) ([]asset.Receipt, asset.Coin, uin contractAddr := w.netToken.SwapContracts[swaps.Version].Address.String() txHash := tx.Hash() - w.addToTxHistory(tx.Nonce(), -int64(swapVal), swaps.FeeRate*gasLimit, 0, w.assetID, txHash[:], asset.Swap) + w.addToTxHistory(tx.Nonce(), swapVal, swaps.FeeRate*gasLimit, 0, w.assetID, txHash[:], asset.Swap, nil) receipts := make([]asset.Receipt, 0, n) for _, swap := range swaps.Contracts { @@ -2232,7 +2232,7 @@ func (w *assetWallet) Redeem(form *asset.RedeemForm, feeWallet *assetWallet, non } txHash := tx.Hash() - w.addToTxHistory(tx.Nonce(), int64(redeemedValue), gasFeeCap*gasLimit, 0, w.assetID, txHash[:], asset.Redeem) + w.addToTxHistory(tx.Nonce(), redeemedValue, gasFeeCap*gasLimit, 0, w.assetID, txHash[:], asset.Redeem, nil) txs := make([]dex.Bytes, len(form.Redemptions)) for i := range txs { @@ -2309,7 +2309,11 @@ func (w *assetWallet) approveToken(amount *big.Int, maxFeeRate, gasLimit uint64, dex.BipIDSymbol(w.assetID), c.tokenAddress(), txOpts.Nonce, tx.Hash().Hex()) txHash := tx.Hash() - w.addToTxHistory(tx.Nonce(), 0, maxFeeRate*gasLimit, 0, w.assetID, txHash[:], asset.ApproveToken) + txType := asset.ApproveToken + if amount.Cmp(big.NewInt(0)) == 0 { + txType = asset.RevokeTokenApproval + } + w.addToTxHistory(tx.Nonce(), 0, maxFeeRate*gasLimit, 0, w.assetID, txHash[:], txType, nil) return nil }) @@ -2892,7 +2896,7 @@ func (w *assetWallet) Refund(_, contract dex.Bytes, feeRate uint64) (dex.Bytes, txHash := tx.Hash() refundValue := dexeth.WeiToGwei(swap.Value) - w.addToTxHistory(tx.Nonce(), int64(refundValue), fees, 0, w.assetID, txHash[:], asset.Refund) + w.addToTxHistory(tx.Nonce(), refundValue, fees, 0, w.assetID, txHash[:], asset.Refund, nil) return txHash[:], nil } @@ -3168,7 +3172,12 @@ func (w *ETHWallet) Send(addr string, value, _ uint64) (asset.Coin, error) { } txHash := tx.Hash() - w.addToTxHistory(tx.Nonce(), -int64(value), maxFee, 0, w.assetID, txHash[:], asset.Send) + txType := asset.Send + if addr == w.addr.String() { + txType = asset.SelfSend + } + + w.addToTxHistory(tx.Nonce(), value, maxFee, 0, w.assetID, txHash[:], txType, &addr) return &coin{id: txHash, value: value}, nil } @@ -3192,7 +3201,7 @@ func (w *TokenWallet) Send(addr string, value, _ uint64) (asset.Coin, error) { } txHash := tx.Hash() - w.addToTxHistory(tx.Nonce(), -int64(value), maxFee, 0, w.assetID, txHash[:], asset.Send) + w.addToTxHistory(tx.Nonce(), value, maxFee, 0, w.assetID, txHash[:], asset.Send, &addr) return &coin{id: txHash, value: value}, nil } @@ -4058,10 +4067,10 @@ func (w *assetWallet) sumPendingTxs(bal *big.Int) (out, in uint64) { addPendingTx := func(txAssetID uint32, pt *extendedWalletTx) { if txAssetID == w.assetID { - if pt.BalanceDelta > 0 { - in += uint64(pt.BalanceDelta) + if asset.IncomingTxType(pt.Type) { + in += pt.Amount } else { - out += uint64(-1 * pt.BalanceDelta) + out += pt.Amount } } if !isToken { @@ -4565,6 +4574,25 @@ func checkTxStatus(receipt *types.Receipt, gasLimit uint64) error { return nil } +func (w *baseWallet) emitTransactionNote(tx *asset.WalletTransaction, new bool) { + w.walletsMtx.RLock() + ethWallet, found := w.wallets[BipID] + var tokenWallet *assetWallet + if tx.TokenID != nil { + tokenWallet = w.wallets[*tx.TokenID] + } + w.walletsMtx.RUnlock() + + if found { + ethWallet.emit.TransactionNote(tx, new) + } else { + w.log.Error("emitTransactionNote: eth wallet not found") + } + if tokenWallet != nil { + ethWallet.emit.TransactionNote(tx, new) + } +} + // checkPendingTx checks the confirmation status of a transaction. The BlockNumber // and Fees fields of the extendedWalletTx are updated if the transaction is confirmed, // and if the transaction has reached the required number of confirmations, it is removed @@ -4607,6 +4635,8 @@ func (w *baseWallet) checkPendingTx(nonce uint64, pendingTx *extendedWalletTx) ( delete(w.pendingTxs, nonce) w.pendingTxsMtx.Unlock() } + + w.emitTransactionNote(pendingTx.WalletTransaction, false) } }() @@ -4695,8 +4725,8 @@ func (w *baseWallet) checkPendingTxs() { const txHistoryNonceKey = "Nonce" -func (w *baseWallet) addToTxHistory(nonce uint64, balanceDelta int64, fees, blockNumber uint64, - assetID uint32, id dex.Bytes, typ asset.TransactionType) { +func (w *baseWallet) addToTxHistory(nonce uint64, amount uint64, fees, blockNumber uint64, + assetID uint32, id dex.Bytes, typ asset.TransactionType, recipient *string) { w.tipMtx.RLock() tip := w.currentTip.Number.Uint64() w.tipMtx.RUnlock() @@ -4712,12 +4742,13 @@ func (w *baseWallet) addToTxHistory(nonce uint64, balanceDelta int64, fees, bloc wt := &extendedWalletTx{ WalletTransaction: &asset.WalletTransaction{ - Type: typ, - ID: id, - BalanceDelta: balanceDelta, - Fees: fees, - BlockNumber: blockNumber, - TokenID: tokenAssetID, + Type: typ, + ID: id, + Amount: amount, + Fees: fees, + BlockNumber: blockNumber, + TokenID: tokenAssetID, + Recipient: recipient, AdditionalData: map[string]string{ txHistoryNonceKey: strconv.FormatUint(nonce, 10), }, @@ -4743,6 +4774,8 @@ func (w *baseWallet) addToTxHistory(nonce uint64, balanceDelta int64, fees, bloc } w.log.Errorf("error storing tx in db: %v", err) } + + w.emitTransactionNote(wt.WalletTransaction, true) } // TxHistory returns all the transactions the wallet has made. This @@ -4751,13 +4784,22 @@ func (w *baseWallet) addToTxHistory(nonce uint64, balanceDelta int64, fees, bloc // If past is true, the transactions prior to the refID are returned, otherwise // the transactions after the refID are returned. n is the number of // transactions to return. If n is <= 0, all the transactions will be returned. -func (w *baseWallet) TxHistory(n int, refID *dex.Bytes, past bool) ([]*asset.WalletTransaction, error) { +func (w *ETHWallet) TxHistory(n int, refID *dex.Bytes, past bool) ([]*asset.WalletTransaction, error) { + baseChainWallet := w.wallet(w.baseChainID) + if baseChainWallet == nil || !baseChainWallet.connected.Load() { + return nil, fmt.Errorf("wallet not connected") + } + + return w.txDB.getTxs(n, refID, past, nil) +} + +func (w *TokenWallet) TxHistory(n int, refID *dex.Bytes, past bool) ([]*asset.WalletTransaction, error) { baseChainWallet := w.wallet(w.baseChainID) if baseChainWallet == nil || !baseChainWallet.connected.Load() { return nil, fmt.Errorf("wallet not connected") } - return w.txDB.getTxs(n, refID, past) + return w.txDB.getTxs(n, refID, past, &w.assetID) } // providersFile reads a file located at ~/dextest/credentials.json. diff --git a/client/asset/eth/txdb.go b/client/asset/eth/txdb.go index 4d52a9c583..99d72784aa 100644 --- a/client/asset/eth/txdb.go +++ b/client/asset/eth/txdb.go @@ -177,7 +177,7 @@ const txDBVersion = prefixDBVersion type txDB interface { storeTx(nonce uint64, wt *extendedWalletTx) error removeTx(id dex.Bytes) error - getTxs(n int, refID *dex.Bytes, past bool) ([]*asset.WalletTransaction, error) + getTxs(n int, refID *dex.Bytes, past bool, tokenID *uint32) ([]*asset.WalletTransaction, error) getPendingTxs() (map[uint64]*extendedWalletTx, error) storeMonitoredTx(txHash common.Hash, tx *monitoredTx) error getMonitoredTxs() (map[common.Hash]*monitoredTx, error) @@ -394,8 +394,8 @@ func (s *badgerTxDB) removeTx(id dex.Bytes) error { // getTxs returns the n more recent transaction if refID is nil, or the // n transactions before/after refID depending on the value of past. The -// transactions are returned in chronological order. -func (s *badgerTxDB) getTxs(n int, refID *dex.Bytes, past bool) ([]*asset.WalletTransaction, error) { +// transactions are returned in reverse chronological order. +func (s *badgerTxDB) getTxs(n int, refID *dex.Bytes, past bool, tokenID *uint32) ([]*asset.WalletTransaction, error) { var txs []*asset.WalletTransaction err := s.View(func(txn *badger.Txn) error { @@ -423,7 +423,7 @@ func (s *badgerTxDB) getTxs(n int, refID *dex.Bytes, past bool) ([]*asset.Wallet it := txn.NewIterator(opts) defer it.Close() - for it.Seek(startNonceKey); it.Valid() && n <= 0 || len(txs) < n; it.Next() { + for it.Seek(startNonceKey); it.Valid() && (n <= 0 || len(txs) < n); it.Next() { item := it.Item() err := item.Value(func(wtB []byte) error { wt := new(asset.WalletTransaction) @@ -435,10 +435,13 @@ func (s *badgerTxDB) getTxs(n int, refID *dex.Bytes, past bool) ([]*asset.Wallet if refID != nil && bytes.Equal(wt.ID, *refID) { return nil } + if tokenID != nil && (wt.TokenID == nil || *tokenID != *wt.TokenID) { + return nil + } if past { - txs = append([]*asset.WalletTransaction{wt}, txs...) - } else { txs = append(txs, wt) + } else { + txs = append([]*asset.WalletTransaction{wt}, txs...) } return nil }) diff --git a/client/asset/interface.go b/client/asset/interface.go index c404ff757a..8ce510b0ea 100644 --- a/client/asset/interface.go +++ b/client/asset/interface.go @@ -1083,21 +1083,45 @@ const ( RedeemBond ApproveToken Acceleration + SelfSend + RevokeTokenApproval ) +// IncomingTxType returns true if the wallet's balance increases due to a +// transaction. +func IncomingTxType(typ TransactionType) bool { + return typ == Receive || typ == Redeem || typ == Refund || typ == RedeemBond +} + +// BondTxInfo contains information about a CreateBond or RedeemBond +// transaction. +type BondTxInfo struct { + // AccountID is the account is the account ID that the bond is applied to. + AccountID dex.Bytes `json:"accountID"` + // LockTime is the time until which the bond is locked. + LockTime uint64 `json:"lockTime"` + // BondID is the ID of the bond. + BondID dex.Bytes `json:"bondID"` +} + // WalletTransaction represents a transaction that was made by a wallet. type WalletTransaction struct { - Type TransactionType `json:"type"` - ID dex.Bytes `json:"id"` - // BalanceDelta is the amount the wallet balance changed as a result of - // the transaction, excluding fees. - BalanceDelta int64 `json:"balanceDelta"` - Fees uint64 `json:"fees"` + Type TransactionType `json:"type"` + ID dex.Bytes `json:"id"` + Amount uint64 `json:"amount"` + Fees uint64 `json:"fees"` // BlockNumber is 0 for txs in the mempool. BlockNumber uint64 `json:"blockNumber"` + // Timestamp is the time the transaction was mined. + Timestamp uint64 `json:"timestamp"` // TokenID will be non-nil if the BalanceDelta applies to the balance // of a token. TokenID *uint32 `json:"tokenID,omitempty"` + // Recipient wil be non-nil for Send transactions, and specifies the + // recipient of the send. + Recipient *string `json:"recipient,omitempty"` + // BondInfo will be non-nil for CreateBond and RedeemBond transactions. + BondInfo *BondTxInfo `json:"bondInfo,omitempty"` // AdditionalData contains asset specific information, i.e. nonce // for ETH. AdditionalData map[string]string `json:"additionalData"` @@ -1438,6 +1462,13 @@ type BalanceChangeNote struct { Balance *Balance } +// TransactionNote is sent when a transaction is made, seen, or updated. +type TransactionNote struct { + baseWalletNotification + Transaction *WalletTransaction `json:"transaction"` + New bool `json:"new"` +} + // CustomWalletNote is any other information the wallet wishes to convey to // the user. type CustomWalletNote struct { @@ -1505,3 +1536,15 @@ func (e *WalletEmitter) BalanceChange(bal *Balance) { Balance: bal, }) } + +// TransactionNote sends a TransactionNote. +func (e *WalletEmitter) TransactionNote(tx *WalletTransaction, new bool) { + e.emit(&TransactionNote{ + baseWalletNotification: baseWalletNotification{ + AssetID: e.assetID, + Route: "transaction", + }, + Transaction: tx, + New: new, + }) +} diff --git a/client/webserver/api.go b/client/webserver/api.go index be7659b9ce..6ca9a0ed56 100644 --- a/client/webserver/api.go +++ b/client/webserver/api.go @@ -1886,6 +1886,43 @@ func (s *WebServer) apiMarketMakingStatus(w http.ResponseWriter, r *http.Request }, s.indent) } +func (s *WebServer) apiTxHistory(w http.ResponseWriter, r *http.Request) { + var form struct { + AssetID uint32 `json:"assetID"` + N int `json:"n"` + RefID string `json:"refID"` + Past bool `json:"past"` + } + if !readPost(w, r, &form) { + return + } + + var refID *dex.Bytes + if len(form.RefID) > 0 { + var err error + id, err := hex.DecodeString(form.RefID) + if err != nil { + s.writeAPIError(w, fmt.Errorf("error decoding refID: %w", err)) + return + } + dbID := dex.Bytes(id) + refID = &dbID + } + + txs, err := s.core.TxHistory(form.AssetID, form.N, refID, form.Past) + if err != nil { + s.writeAPIError(w, fmt.Errorf("error getting transaction history: %w", err)) + return + } + writeJSON(w, &struct { + OK bool `json:"ok"` + Txs []*asset.WalletTransaction `json:"txs"` + }{ + OK: true, + Txs: txs, + }, s.indent) +} + // writeAPIError logs the formatted error and sends a standardResponse with the // error message. func (s *WebServer) writeAPIError(w http.ResponseWriter, err error) { diff --git a/client/webserver/locales/en-us.go b/client/webserver/locales/en-us.go index 51b8b2d104..c95cc512be 100644 --- a/client/webserver/locales/en-us.go +++ b/client/webserver/locales/en-us.go @@ -501,4 +501,15 @@ var EnUS = map[string]string{ "Yes": "Yes", "Treasury Keys": "Treasury Keys", "bonds_pending_refund": "Bonds Pending Refund", + "asset_name tx_history": " Transaction History", + "ID": "ID", + "no_tx_history": "No transactions to show", + "tx_details": "Transaction Details", + "fee": "Fee", + "tx_id": "Transaction ID", + "bond_id": "Bond ID", + "locktime": "Lock Time", + "recipient": "Recipient", + "block": "Block", + "timestamp": "Timestamp", } diff --git a/client/webserver/site/src/css/application.scss b/client/webserver/site/src/css/application.scss index 4d9ed2e8de..e0383ac860 100644 --- a/client/webserver/site/src/css/application.scss +++ b/client/webserver/site/src/css/application.scss @@ -26,7 +26,8 @@ $grid-breakpoints: ( md: 768px, lg: 992px, xl: 1200px, - xxl: 1750px + xxl: 1750px, + xxxl: 2150px ); // Bootstrap diff --git a/client/webserver/site/src/css/wallets.scss b/client/webserver/site/src/css/wallets.scss index f39a82ab50..190acbf0ac 100644 --- a/client/webserver/site/src/css/wallets.scss +++ b/client/webserver/site/src/css/wallets.scss @@ -29,7 +29,7 @@ width: 400px; } - #copyAddressBtn:hover { + .copy-btn:hover { cursor: pointer; color: #a8a8a8; } @@ -149,7 +149,7 @@ } } - table#ordersTable { + table#ordersTable, #txHistoryTable { th { font-size: 15px; } @@ -165,15 +165,19 @@ tbody { font-size: 15px; + } + } - // tr:nth-child(even) { - // background-color: #7772; - // } + #txHistoryTableContainer { + max-height: 350px; + overflow-y: auto; + width: 100%; + text-align: center; + } - tr:hover { - background-color: #7774; - } - } + #earlierTxs, #txViewBlockExplorer { + text-decoration: underline; + cursor: pointer; } #checkmarkBox { @@ -181,9 +185,10 @@ height: 100px; } - #marketsOverviewBox { - border-left: 1px solid $light_border_color; - border-bottom: 1px solid $light_border_color; + @include media-breakpoint-up(lg) { + #txHistoryBox, #orderActivityBox { + border-left: 1px solid $light_border_color; + } } #walletDetailsBox { @@ -257,13 +262,17 @@ } } -@include media-breakpoint-up(xxl) { - .no-overflow-xxl { +@include media-breakpoint-up(xxxl) { + .no-overflow-xxxl { overflow: hidden; } + #marketsOverviewBox { + border-left: 1px solid $light_border_color; + } + .walletspage { - .flex-nowrap-xxl { + .flex-nowrap-xxxl { flex-wrap: nowrap; } @@ -288,8 +297,8 @@ } #marketsOverviewBox { - border-right: 1px solid $light-border-color; - border-bottom: none; + border-top: 1px solid $light-border-color; + border-bottom: 1px solid $light_border_color; } #walletDetailsBox { @@ -332,6 +341,11 @@ width: 450px; } +#txDetails { + width: 450px; + font-size: 17px; +} + .warning-text { text-decoration: underline; text-decoration-color: #d11414; @@ -347,3 +361,13 @@ #submitReconfig[disabled] { cursor: not-allowed; } + +.ease-color { + transition: color 1s ease; +} + +#txDetailsBondSection, #txDetailsRecipientSection { + margin-top: 10px; + padding-top: 10px; + border-top: 1px solid #777; +} \ No newline at end of file diff --git a/client/webserver/site/src/css/wallets_dark.scss b/client/webserver/site/src/css/wallets_dark.scss index fd193522c3..e32b4b6c4c 100644 --- a/client/webserver/site/src/css/wallets_dark.scss +++ b/client/webserver/site/src/css/wallets_dark.scss @@ -1,10 +1,5 @@ body.dark { .walletspage { - #marketsOverviewBox { - border-left: 1px solid $dark_border_color; - border-bottom: 1px solid $dark_border_color; - } - #walletDetailsBox { border-bottom: $dark_border_color; } @@ -57,11 +52,7 @@ body.dark { } } - @include media-breakpoint-up(lg) { - .walletspage { - #marketsOverviewBox { - border-color: $dark-border-color; - } - } + #marketsOverviewBox, #orderActivityBox, #txHistoryBox { + border-color: $dark-border-color; } } diff --git a/client/webserver/site/src/html/forms.tmpl b/client/webserver/site/src/html/forms.tmpl index 9c48da4d75..d8df3c86fb 100644 --- a/client/webserver/site/src/html/forms.tmpl +++ b/client/webserver/site/src/html/forms.tmpl @@ -146,7 +146,7 @@
- + [[[copied]]]
diff --git a/client/webserver/site/src/html/wallets.tmpl b/client/webserver/site/src/html/wallets.tmpl index 2406a4219a..9a8912de64 100644 --- a/client/webserver/site/src/html/wallets.tmpl +++ b/client/webserver/site/src/html/wallets.tmpl @@ -23,9 +23,9 @@
-
+
{{- /* WALLET DETAILS */ -}} -