Skip to content

Commit 30b3404

Browse files
committed
backend: fix balance time series computation with failed txs
In ETH, there can be failed transactions that are mined (gas limit too low). The fee is deducted, but the amount is not transferred. We need to take this into account, otherwise the chart can go out of whack.
1 parent a76b653 commit 30b3404

File tree

3 files changed

+75
-4
lines changed

3 files changed

+75
-4
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
- Fix build on M-processor Apple machines
99
- Add support for Ethereum EIP-1559 transactions: https://eips.ethereum.org/EIPS/eip-1559
1010
- Replace deprecated Ethgasstation with Etherscan for fee estimation (including base + priority fee for EIP-1559)
11+
- Fixed a bug where the portfolio chart could show wrong values in an Ethereum account containing failed transactions
1112

1213
## 4.40.0
1314
- Add support for watch-only - see your accounts and portfolio without connecting your BitBox02

backend/accounts/transaction.go

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -163,15 +163,21 @@ func NewOrderedTransactions(txs []*TransactionData) OrderedTransactions {
163163
tx := txs[i]
164164
switch tx.Type {
165165
case TxTypeReceive:
166-
balance.Add(balance, tx.Amount.BigInt())
166+
if tx.Status != TxStatusFailed {
167+
balance.Add(balance, tx.Amount.BigInt())
168+
}
167169
case TxTypeSend:
168-
balance.Sub(balance, tx.Amount.BigInt())
169-
// Subtract fee as well.
170+
if tx.Status != TxStatusFailed {
171+
balance.Sub(balance, tx.Amount.BigInt())
172+
}
173+
// Subtract fee as well. Ethereum: it is deducted even if the tx failed, as the tx was
174+
// mined.
170175
if tx.Fee != nil && !tx.FeeIsDifferentUnit {
171176
balance.Sub(balance, tx.Fee.BigInt())
172177
}
173178
case TxTypeSendSelf:
174-
// Subtract only fee.
179+
// Subtract only fee. Ethereum: it is deducted even if the tx failed, as the tx was
180+
// mined.
175181
if tx.Fee != nil && !tx.FeeIsDifferentUnit {
176182
balance.Sub(balance, tx.Fee.BigInt())
177183
}

backend/accounts/transaction_test.go

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -149,3 +149,67 @@ func TestOrderedTransactions(t *testing.T) {
149149
},
150150
}, timeseries)
151151
}
152+
153+
// TestOrderedTransactionsWithFailedTransactions tests that the cumulative balance takes into
154+
// account failed transactions. Ethereum transactions can be mined and fail anyway due to a too low
155+
// gas limit, in which case the amount is not transferred, but the fees are still paid.
156+
func TestOrderedTransactionsWithFailedTransactions(t *testing.T) {
157+
tt := func(t time.Time) *time.Time { return &t }
158+
fee := coin.NewAmountFromInt64(1)
159+
txs := []*TransactionData{
160+
{
161+
Timestamp: tt(time.Date(2020, 9, 15, 12, 0, 0, 0, time.UTC)),
162+
Height: 15,
163+
Type: TxTypeReceive,
164+
Amount: coin.NewAmountFromInt64(100),
165+
},
166+
{
167+
Timestamp: tt(time.Date(2020, 9, 10, 12, 0, 0, 0, time.UTC)),
168+
Height: 10,
169+
Type: TxTypeReceive,
170+
Amount: coin.NewAmountFromInt64(200),
171+
},
172+
{
173+
Timestamp: tt(time.Date(2020, 9, 20, 12, 0, 0, 0, time.UTC)),
174+
Height: 20,
175+
Type: TxTypeReceive,
176+
Amount: coin.NewAmountFromInt64(300),
177+
},
178+
{
179+
Timestamp: tt(time.Date(2020, 9, 21, 12, 0, 0, 0, time.UTC)),
180+
Height: 21,
181+
Type: TxTypeSendSelf,
182+
Amount: coin.NewAmountFromInt64(50),
183+
Fee: &fee,
184+
},
185+
{
186+
Timestamp: tt(time.Date(2020, 9, 22, 12, 0, 0, 0, time.UTC)),
187+
Height: 22,
188+
Type: TxTypeSend,
189+
Amount: coin.NewAmountFromInt64(50),
190+
Fee: &fee,
191+
Status: TxStatusFailed,
192+
},
193+
{
194+
Timestamp: tt(time.Date(2020, 9, 23, 12, 0, 0, 0, time.UTC)),
195+
Height: 23,
196+
Type: TxTypeReceive,
197+
Amount: coin.NewAmountFromInt64(1000),
198+
Fee: &fee,
199+
Status: TxStatusFailed,
200+
},
201+
}
202+
203+
ordered := NewOrderedTransactions(txs)
204+
expectedBalances := []int64{
205+
598, // failed receive tx, nothing changes
206+
598, // failed send tx, only fee deducted
207+
599,
208+
600,
209+
300,
210+
200,
211+
}
212+
for i := range ordered {
213+
require.Equal(t, coin.NewAmountFromInt64(expectedBalances[i]), ordered[i].Balance, i)
214+
}
215+
}

0 commit comments

Comments
 (0)