ynab-portfolio-monitor/providers/bitcoin/providerImpl.go

79 lines
2.4 KiB
Go
Raw Permalink Normal View History

package bitcoin
import (
"fmt"
"os"
2024-09-01 15:05:21 +00:00
"sync"
)
type Provider struct {
bitcoinAddresses []string // Slice of bitcoin addresses this provider monitors
ynabAccountID string // YNAB account ID this provider updates - all bitcoin addresses are summed up and mapped to this YNAB account
client *client // HTTP client for interacting with Questrade API
}
func (p *Provider) Name() string {
return "Bitcoin - Blockstream.info / CoinGecko"
}
// Configures the provider for usage via environment variables and persistentData
// If an error is returned, the provider will not be used
func (p *Provider) Configure() error {
// Load environment variables in continous series with suffix starting at 0
// Multiple addresses can be configured, (eg _1, _2)
// As soon as the series is interrupted, we assume we're done
p.bitcoinAddresses = make([]string, 0)
for i := 0; true; i++ {
bitcoinAddress := os.Getenv(fmt.Sprintf("bitcoin_address_%d", i))
if bitcoinAddress == "" {
if i == 0 {
return fmt.Errorf("this account provider is not configured")
}
break
}
p.bitcoinAddresses = append(p.bitcoinAddresses, bitcoinAddress)
}
p.ynabAccountID = os.Getenv("bitcoin_ynab_account")
// Create new HTTP client
p.client = newClient(os.Getenv("bitcoin_coingecko_api_key"))
return nil
}
// Returns slices of account balances and mapped YNAB account IDs, along with an error
func (p *Provider) GetBalances() ([]int, []string, error) {
balances := make([]int, 0)
ynabAccountIDs := make([]string, 0)
var satoshiBalance int
2024-09-01 15:05:21 +00:00
wg := sync.WaitGroup{}
var goErr *error
2024-09-01 15:05:21 +00:00
for _, bitcoinAddress := range p.bitcoinAddresses {
wg.Add(1)
go func(goErr *error) {
2024-09-01 15:05:21 +00:00
defer wg.Done()
addressResponse, err := p.client.getAddress(bitcoinAddress)
if err != nil {
err := fmt.Errorf("failed to get bitcoin address '%s': %v", bitcoinAddress, err)
goErr = &err
return
2024-09-01 15:05:21 +00:00
}
satoshiBalance += addressResponse.ChainStats.FundedTxoSum - addressResponse.ChainStats.SpentTxoSum
}(goErr)
}
2024-09-01 15:05:21 +00:00
wg.Wait()
2024-11-14 16:25:39 +00:00
if goErr != nil {
return nil, nil, *goErr
}
2024-09-01 15:05:21 +00:00
2024-03-23 20:06:38 +00:00
fiatBalance, err := p.client.convertBTCToCAD(satoshiBalance)
if err != nil {
return balances, ynabAccountIDs, fmt.Errorf("failed to convert satoshi balance to fiat balance: %v", err)
}
balances = append(balances, fiatBalance)
ynabAccountIDs = append(ynabAccountIDs, p.ynabAccountID)
return balances, ynabAccountIDs, nil
}