Skip to content

Commit

Permalink
Merge branch 'sats-coin'
Browse files Browse the repository at this point in the history
  • Loading branch information
Beerosagos committed Oct 25, 2022
2 parents dd390f9 + f5a2a82 commit 9976026
Show file tree
Hide file tree
Showing 28 changed files with 525 additions and 68 deletions.
2 changes: 1 addition & 1 deletion .golangci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ linters-settings:
- unlambda
- unslice
gocyclo:
min-complexity: 27
min-complexity: 28
maligned:
# print struct with more effective memory layout or not, false by default
suggest-new: true
Expand Down
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
- Fix unsufficient gas funds error message on erc20 transactions
- Display trailing zeroes for BTC/LTC amount formatting
- Fix broken links on Android 11+
- Add 'sat' unit for Bitcoin accounts, available in Settings view

## 4.34.0
- Bundle BitBox02 firmware version v9.12.0
Expand Down
1 change: 1 addition & 0 deletions backend/accounts.go
Original file line number Diff line number Diff line change
Expand Up @@ -407,6 +407,7 @@ func (backend *Backend) createAndAddAccount(
},
GetSaveFilename: backend.environment.GetSaveFilename,
UnsafeSystemOpen: backend.environment.SystemOpen,
BtcCurrencyUnit: backend.config.AppConfig().Backend.BtcUnit,
}

switch specificCoin := coin.(type) {
Expand Down
2 changes: 2 additions & 0 deletions backend/accounts/baseaccount.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,8 @@ type AccountConfig struct {
GetSaveFilename func(suggestedFilename string) string
// Opens a file in a default application. The filename is not checked.
UnsafeSystemOpen func(filename string) error
// BtcCurrencyUnit is the unit which should be used to format fiat amounts values expressed in BTC..
BtcCurrencyUnit string
}

// BaseAccount is an account struct with common functionality to all coin accounts.
Expand Down
2 changes: 2 additions & 0 deletions backend/backend.go
Original file line number Diff line number Diff line change
Expand Up @@ -351,10 +351,12 @@ func (backend *Backend) Coin(code coinpkg.Code) (coinpkg.Coin, error) {
servers := backend.defaultElectrumXServers(code)
coin = btc.NewCoin(coinpkg.CodeTBTC, "Bitcoin Testnet", "TBTC", &chaincfg.TestNet3Params, dbFolder, servers,
"https://blockstream.info/testnet/tx/", backend.socksProxy)
coin.SetFormatUnit(backend.config.AppConfig().Backend.BtcUnit)
case code == coinpkg.CodeBTC:
servers := backend.defaultElectrumXServers(code)
coin = btc.NewCoin(coinpkg.CodeBTC, "Bitcoin", "BTC", &chaincfg.MainNetParams, dbFolder, servers,
"https://blockstream.info/tx/", backend.socksProxy)
coin.SetFormatUnit(backend.config.AppConfig().Backend.BtcUnit)
case code == coinpkg.CodeTLTC:
servers := backend.defaultElectrumXServers(code)
coin = btc.NewCoin(coinpkg.CodeTLTC, "Litecoin Testnet", "TLTC", &ltc.TestNet4Params, dbFolder, servers,
Expand Down
17 changes: 13 additions & 4 deletions backend/chart.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,9 @@ import (

"github.com/digitalbitbox/bitbox-wallet-app/backend/accounts"
"github.com/digitalbitbox/bitbox-wallet-app/backend/accounts/errors"
"github.com/digitalbitbox/bitbox-wallet-app/backend/coins/btc/util"
"github.com/digitalbitbox/bitbox-wallet-app/backend/coins/coin"
"github.com/digitalbitbox/bitbox-wallet-app/backend/rates"
"github.com/digitalbitbox/bitbox-wallet-app/util/errp"
)

Expand Down Expand Up @@ -124,6 +126,8 @@ func (backend *Backend) ChartData() (*Chart, error) {
}
isUpToDate := time.Since(until) < 2*time.Hour

formatBtcAsSat := util.FormatBtcAsSat(backend.Config().AppConfig().Backend.BtcUnit)

currentTotal := new(big.Rat)
currentTotalMissing := false
// Total number of transactions across all active accounts.
Expand Down Expand Up @@ -254,7 +258,7 @@ func (backend *Backend) ChartData() (*Chart, error) {
result[i] = ChartEntry{
Time: entry.Time,
Value: floatValue,
FormattedValue: coin.FormatAsCurrency(entry.RatValue, fiat),
FormattedValue: coin.FormatAsCurrency(entry.RatValue, fiat, formatBtcAsSat),
}
i++
}
Expand All @@ -270,7 +274,7 @@ func (backend *Backend) ChartData() (*Chart, error) {
result = append(result, ChartEntry{
Time: time.Now().Unix(),
Value: total,
FormattedValue: coin.FormatAsCurrency(currentTotal, fiat),
FormattedValue: coin.FormatAsCurrency(currentTotal, fiat, formatBtcAsSat),
})
}

Expand All @@ -292,18 +296,23 @@ func (backend *Backend) ChartData() (*Chart, error) {
chartDataMissing = false
}

chartFiat := fiat
if fiat == rates.BTC.String() {
chartFiat = backend.Config().AppConfig().Backend.BtcUnit
}

var chartTotal *float64
var formattedChartTotal string
if !currentTotalMissing {
tot, _ := currentTotal.Float64()
chartTotal = &tot
formattedChartTotal = coin.FormatAsCurrency(currentTotal, fiat)
formattedChartTotal = coin.FormatAsCurrency(currentTotal, fiat, formatBtcAsSat)
}
return &Chart{
DataMissing: chartDataMissing,
DataDaily: toSortedSlice(chartEntriesDaily, fiat),
DataHourly: toSortedSlice(chartEntriesHourly, fiat),
Fiat: fiat,
Fiat: chartFiat,
Total: chartTotal,
FormattedTotal: formattedChartTotal,
IsUpToDate: isUpToDate,
Expand Down
47 changes: 42 additions & 5 deletions backend/coins/btc/coin.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,10 +41,13 @@ import (

// Coin models a Bitcoin-related coin.
type Coin struct {
initOnce sync.Once
code coinpkg.Code
name string
unit string
initOnce sync.Once
code coinpkg.Code
name string
// unit is the main unit of the coin, e.g. 'BTC'
unit string
// formatUnit keeps track of the unit used, e.g. 'BTC' or 'sat' depening on if sat mode is enabled
formatUnit string
net *chaincfg.Params
dbFolder string
makeBlockchain func() blockchain.Interface
Expand Down Expand Up @@ -74,6 +77,7 @@ func NewCoin(
code: code,
name: name,
unit: unit,
formatUnit: unit,
net: net,
dbFolder: dbFolder,
blockExplorerTxPrefix: blockExplorerTxPrefix,
Expand Down Expand Up @@ -150,13 +154,33 @@ func (coin *Coin) Unit(bool) string {
return coin.unit
}

// SetFormatUnit implements coin.Coin.
func (coin *Coin) SetFormatUnit(unit string) {
coin.formatUnit = unit
}

// GetFormatUnit implements coin.Coin.
func (coin *Coin) GetFormatUnit() string {
if coin.code == coinpkg.CodeTBTC {
if coin.formatUnit == coinpkg.UnitSats {
return "t" + coin.formatUnit
}
return "T" + coin.formatUnit
}

return coin.formatUnit
}

// Decimals implements coinpkg.Coin.
func (coin *Coin) Decimals(isFee bool) uint {
return 8
}

// FormatAmount implements coinpkg.Coin.
func (coin *Coin) FormatAmount(amount coinpkg.Amount, isFee bool) string {
if coin.formatUnit == coinpkg.UnitSats {
return amount.BigInt().String()
}
return new(big.Rat).SetFrac(amount.BigInt(), big.NewInt(unitSatoshi)).FloatString(8)
}

Expand All @@ -168,11 +192,24 @@ func (coin *Coin) ToUnit(amount coinpkg.Amount, isFee bool) float64 {

// SetAmount implements coinpkg.Coin.
func (coin *Coin) SetAmount(amount *big.Rat, isFee bool) coinpkg.Amount {
satsAmount := new(big.Rat).Mul(amount, new(big.Rat).SetFloat64(unitSatoshi))
satsAmount := coinpkg.Btc2Sat(amount)
intSatsAmount, _ := new(big.Int).SetString(satsAmount.FloatString(0), 0)
return coinpkg.NewAmount(intSatsAmount)
}

// ParseAmount implements coinpkg.Coin.
func (coin *Coin) ParseAmount(amount string) (coinpkg.Amount, error) {
amountRat, valid := new(big.Rat).SetString(amount)
if !valid {
return coinpkg.Amount{}, errp.New("Invalid amount")
}

if coin.formatUnit == coinpkg.UnitSats {
amountRat = coinpkg.Sat2Btc(amountRat)
}
return coin.SetAmount(amountRat, false), nil
}

// Blockchain connects to a blockchain backend.
func (coin *Coin) Blockchain() blockchain.Interface {
return coin.blockchain
Expand Down
20 changes: 20 additions & 0 deletions backend/coins/btc/coin_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,26 @@ func (s *testSuite) TestSetAmount() {
}
}

func (s *testSuite) TestParseAmount() {
btcAmount := "123.12345678"
satAmount := "12312345678"
intSatAmount := int64(12312345678)

s.coin.SetFormatUnit("BTC")
coinAmount, err := s.coin.ParseAmount(btcAmount)
require.Equal(s.T(), err, nil)
intAmount, err := coinAmount.Int64()
require.Equal(s.T(), err, nil)
require.Equal(s.T(), intSatAmount, intAmount)

s.coin.SetFormatUnit("sat")
coinAmount, err = s.coin.ParseAmount(satAmount)
require.Equal(s.T(), err, nil)
intAmount, err = coinAmount.Int64()
require.Equal(s.T(), err, nil)
require.Equal(s.T(), intSatAmount, intAmount)
}

func (s *testSuite) TestDecodeAddress() {
tbtcValidAddresses := []string{
"myY3Bbvj5mjwqqvubtu5Hfy2nuCeBfvNXL", // p2pkh legacy
Expand Down
14 changes: 9 additions & 5 deletions backend/coins/btc/handlers/handlers.go
Original file line number Diff line number Diff line change
Expand Up @@ -91,27 +91,31 @@ type FormattedAmount struct {
}

func (handlers *Handlers) formatAmountAsJSON(amount coin.Amount, isFee bool) FormattedAmount {
accountCoin := handlers.account.Coin()
return FormattedAmount{
Amount: handlers.account.Coin().FormatAmount(amount, isFee),
Unit: handlers.account.Coin().Unit(isFee),
Amount: accountCoin.FormatAmount(amount, isFee),
Unit: accountCoin.GetFormatUnit(),
Conversions: coin.Conversions(
amount,
handlers.account.Coin(),
accountCoin,
isFee,
handlers.account.Config().RateUpdater,
util.FormatBtcAsSat(handlers.account.Config().BtcCurrencyUnit),
),
}
}

func (handlers *Handlers) formatAmountAtTimeAsJSON(amount coin.Amount, timeStamp *time.Time) *FormattedAmount {
accountCoin := handlers.account.Coin()
return &FormattedAmount{
Amount: handlers.account.Coin().FormatAmount(amount, false),
Unit: handlers.account.Coin().Unit(false),
Amount: accountCoin.FormatAmount(amount, false),
Unit: accountCoin.GetFormatUnit(),
Conversions: coin.ConversionsAtTime(
amount,
handlers.account.Coin(),
false,
handlers.account.Config().RateUpdater,
util.FormatBtcAsSat(handlers.account.Config().BtcCurrencyUnit),
timeStamp,
),
}
Expand Down
7 changes: 6 additions & 1 deletion backend/coins/btc/transaction.go
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,12 @@ func (account *Account) newTx(args *accounts.TxProposalArgs) (
}
} else {
allowZero := false
parsedAmount, err := args.Amount.Amount(big.NewInt(unitSatoshi), allowZero)

unit := int64(unitSatoshi)
if account.coin.formatUnit == coin.UnitSats {
unit = 1
}
parsedAmount, err := args.Amount.Amount(big.NewInt(unit), allowZero)
if err != nil {
return nil, nil, err
}
Expand Down
6 changes: 6 additions & 0 deletions backend/coins/btc/util/util.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import (
"github.com/btcsuite/btcd/chaincfg/chainhash"
"github.com/btcsuite/btcd/txscript"
"github.com/btcsuite/btcd/wire"
"github.com/digitalbitbox/bitbox-wallet-app/backend/coins/coin"
"github.com/digitalbitbox/bitbox-wallet-app/backend/coins/ltc"
"github.com/digitalbitbox/bitbox-wallet-app/util/errp"
)
Expand Down Expand Up @@ -68,3 +69,8 @@ func AddressFromPkScript(pkScript []byte, net *chaincfg.Params) (btcutil.Address
}
return addresses[0], nil
}

// FormatBtcAsSat returns true if the btcUnit param is `[t]sat`.
func FormatBtcAsSat(btcUnit string) bool {
return btcUnit == coin.UnitSats || btcUnit == "t"+coin.UnitSats
}
7 changes: 7 additions & 0 deletions backend/coins/coin/codes.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,13 @@ const (
// There are some more coin codes for the supported erc20 tokens in erc20.go.
)

const (
// UnitBtc represents Bitcoin unit.
UnitBtc string = "BTC"
// UnitSats represents Satoshi unit.
UnitSats string = "sat"
)

// TestnetCoins is the subset of all coins which are available in testnet mode.
var TestnetCoins = map[Code]struct{}{
CodeTBTC: {},
Expand Down
10 changes: 10 additions & 0 deletions backend/coins/coin/coin.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,12 @@ type Coin interface {
// The fee unit is usually the same as the main unit, but can differ.
Unit(isFee bool) string

// SetFormatUnit sets the unit used to format the amount, e.g. "BTC" or "sat".
SetFormatUnit(unit string)

// GetFormatUnit sets the unit used to format the amount, e.g. "BTC" or "sat".
GetFormatUnit() string

// Number of decimal places in the standard unit, e.g. 8 for Bitcoin. Must be in the range
// [0..31].
Decimals(isFee bool) uint
Expand All @@ -55,6 +61,10 @@ type Coin interface {
// e.g. BTC 1/2 => 50000000
SetAmount(amount *big.Rat, isFee bool) Amount

// ParseAmount parse a String representing a given amount, considering the formatting unit.
// e.g. if the formatUnit is set as "sat", the amount will be considered as being sats
ParseAmount(amount string) (Amount, error)

// // Server returns the host and port of the full node used for blockchain synchronization.
// Server() string

Expand Down
Loading

0 comments on commit 9976026

Please sign in to comment.