Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
package main

import (
"fmt"
"log/slog"

"github.com/bsv-blockchain/go-wallet-toolbox/examples/internal/show"
"github.com/bsv-blockchain/go-wallet-toolbox/pkg/defs"
"github.com/bsv-blockchain/go-wallet-toolbox/pkg/services"
)

func exampleFiatExchangeRate(srv *services.WalletServices) {
examples := []struct {
currency defs.Currency
base *defs.Currency
}{
{currency: defs.EUR, base: ptr(defs.USD)},
{currency: defs.GBP, base: ptr(defs.EUR)},
{currency: defs.GBP, base: nil}, // defaults to USD
{currency: "ABC", base: ptr(defs.USD)}, // invalid case
}

for _, ex := range examples {
base := "<nil>"
if ex.base != nil {
base = string(*ex.base)
}
step := fmt.Sprintf("Getting fiat rate for %s per %s", ex.currency, base)
show.Step("FiatExchangeRate", step)

rate := srv.FiatExchangeRate(ex.currency, ex.base)
if rate == 0 {
show.WalletError("FiatExchangeRate", fmt.Sprintf("%s/%s", ex.currency, base), fmt.Errorf("rate not found"))
} else {
show.WalletSuccess("FiatExchangeRate", fmt.Sprintf("%s/%s", ex.currency, base), rate)
}
}
}

func ptr(c defs.Currency) *defs.Currency {
return &c
}

func main() {
show.ProcessStart("Fiat Exchange Rate Conversion")

cfg := defs.DefaultServicesConfig(defs.NetworkMainnet)
cfg.FiatExchangeRates = defs.FiatExchangeRates{
Rates: map[defs.Currency]float64{
defs.USD: 1.0,
defs.EUR: 0.85,
defs.GBP: 0.65,
},
}

srv := services.New(slog.Default(), cfg)

exampleFiatExchangeRate(srv)

show.ProcessComplete("Fiat Exchange Rate Conversion")
}
107 changes: 107 additions & 0 deletions examples/services_examples/fiat_exchange_rate/fiat_exchange_rate.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
# Fiat Exchange Rate

This example demonstrates how to retrieve and convert fiat currency exchange rates using the Go Wallet Toolbox SDK. It supports converting one fiat currency to another based on the latest rates (e.g., EUR to USD, GBP to EUR).

## Overview

The process involves:
1. Setting up the service with a mock configuration including fiat exchange rates.
2. Calling the `FiatExchangeRate()` method to retrieve the exchange rate of one currency relative to another.
3. Processing the returned `float64` result representing the conversion rate.
4. Handling invalid currencies or missing data.

This showcases a simplified currency conversion mechanism used internally within the wallet toolbox.

## Code Walkthrough

### Configuration

The fiat exchange rates are mocked for testing or example purposes using a `map[defs.Currency]float64`:

```go
{
USD: 1.0,
EUR: 0.85,
GBP: 0.65,
}
```

You can replace or update this map with actual rates from a provider or service.

### Method Signature

```go
func (s *WalletServices) FiatExchangeRate(currency defs.Currency, base *defs.Currency) float64
```

- **`currency`**: Target fiat currency (e.g., EUR).
- **`base`**: Base fiat currency to convert against (e.g., USD). Defaults to USD if `nil`.
- **Returns**: The conversion rate from `currency` to `base`.

### Example Scenarios

- `FiatExchangeRate(EUR, USD)` → 0.85 (EUR to USD)
- `FiatExchangeRate(GBP, EUR)` → 0.65 / 0.85 ≈ 0.7647
- `FiatExchangeRate(GBP, nil)` → 0.65 (GBP to default USD)
- `FiatExchangeRate(ABC, USD)` → Error (unknown currency)

## Running the Example

```bash
go run ./examples/services_examples/fiat_exchange_rate/fiat_exchange_rate.go
```

## Expected Output

```text
🚀 STARTING: Fiat Exchange Rate Conversion
============================================================

=== STEP ===
FiatExchangeRate is performing: Getting fiat rate for EUR per USD
--------------------------------------------------

WALLET CALL: FiatExchangeRate
Args: EUR/USD
✅ Result: 0.85

=== STEP ===
FiatExchangeRate is performing: Getting fiat rate for GBP per EUR
--------------------------------------------------

WALLET CALL: FiatExchangeRate
Args: GBP/EUR
✅ Result: 0.7647058823529412

=== STEP ===
FiatExchangeRate is performing: Getting fiat rate for GBP per <nil>
--------------------------------------------------

WALLET CALL: FiatExchangeRate
Args: GBP/<nil>
✅ Result: 0.65

=== STEP ===
FiatExchangeRate is performing: Getting fiat rate for ABC per USD
--------------------------------------------------

WALLET CALL: FiatExchangeRate
Args: ABC/USD
❌ Error: rate not found
============================================================
🎉 COMPLETED: Fiat Exchange Rate Conversion
```

## Integration Steps

1. **Add fiat exchange rates** to your service configuration or implement a live provider.
2. **Invoke `FiatExchangeRate()`** with the desired currency and optional base.
3. **Use the returned value** to display or convert currency values in your app.
4. **Handle errors gracefully** if a currency is missing or unknown.
5. **Cache frequently-used rates** to avoid redundant calls in performance-critical paths.

## Additional Resources

- [FiatExchangeRate Code](./fiat_exchange_rate.go) - Main example implementation
- [BSVExchangeRate](../bsv_exchange_rate/bsv_exchange_rate.go) - Check BSV/USD rate
- [Currency Definitions](../../../pkg/defs/currency.go) - Enum of supported fiat currencies
Original file line number Diff line number Diff line change
Expand Up @@ -78,36 +78,3 @@ func main() {

show.ProcessComplete("Check nLockTime Finality")
}

/* Output:

🚀 STARTING: Check nLockTime Finality
============================================================

=== STEP ===
Wallet-Services is performing: Checking finality for past timestamp locktime: 1754897347
--------------------------------------------------

WALLET CALL: NLockTimeIsFinal
Args: 1754897347
✅ Result: true

=== STEP ===
Wallet-Services is performing: Checking finality for future timestamp locktime: 1754904547
--------------------------------------------------

WALLET CALL: NLockTimeIsFinal
Args: 1754904547
✅ Result: false

=== STEP ===
Wallet-Services is performing: Checking finality for block height locktime: 800000
--------------------------------------------------

WALLET CALL: NLockTimeIsFinal
Args: 800000
✅ Result: true
============================================================
🎉 COMPLETED: Check nLockTime Finality

*/
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
# NLockTime Finality

This example demonstrates how to check whether a given `nLockTime` value is considered **final** on the BSV blockchain using the Go Wallet Toolbox SDK. It supports evaluating both timestamp-based and block-height-based locktimes and determines their finality status based on current blockchain state.

## Overview

The `NLockTimeIsFinal` method determines whether a transaction can be accepted for mining based on its `nLockTime` value. A transaction is considered final if:
- It has a locktime of 0
- The locktime is less than the current block height (for block-based locktime)
- The locktime is less than the current UNIX timestamp (for time-based locktime)
- All input sequence numbers are `0xffffffff` (final)

## Code Walkthrough

The example demonstrates three scenarios:

### 1. Timestamp LockTime (Past)
```go
lockTime := uint32(time.Now().Unix() - 3600)
isFinal, err := srv.NLockTimeIsFinal(ctx, lockTime)
// ✅ Result: true
```

### 2. Timestamp LockTime (Future)
```go
lockTime := uint32(time.Now().Unix() + 3600)
isFinal, err := srv.NLockTimeIsFinal(ctx, lockTime)
// ❌ Result: false
```

### 3. Block Height LockTime
```go
lockTime := uint32(800000)
isFinal, err := srv.NLockTimeIsFinal(ctx, lockTime)
// ✅ Result: depends on current blockchain height
```

## Method Signature

```go
func (s *WalletServices) NLockTimeIsFinal(ctx context.Context, txOrLockTime any) (bool, error)
```

- **`txOrLockTime`**: Can be a `uint32`, `int`, transaction hex string, `sdk.Transaction`, etc.
- **Returns**: Whether the locktime is final and the transaction is ready to be accepted into a block.

## Running the Example

```bash
go run ./examples/services_examples/nlocktime_finality/nlocktime_finality.go
```

## Expected Output

```text
🚀 STARTING: Check nLockTime Finality
============================================================

=== STEP ===
Wallet-Services is performing: Checking finality for past timestamp locktime: 1754897347
--------------------------------------------------

WALLET CALL: NLockTimeIsFinal
Args: 1754897347
✅ Result: true

=== STEP ===
Wallet-Services is performing: Checking finality for future timestamp locktime: 1754904547
--------------------------------------------------

WALLET CALL: NLockTimeIsFinal
Args: 1754904547
✅ Result: false

=== STEP ===
Wallet-Services is performing: Checking finality for block height locktime: 800000
--------------------------------------------------

WALLET CALL: NLockTimeIsFinal
Args: 800000
✅ Result: true
============================================================
🎉 COMPLETED: Check nLockTime Finality
```

## Integration Steps

1. **Import Wallet Toolbox** and initialize `WalletServices` with proper network config.
2. **Pass a locktime or transaction** into `NLockTimeIsFinal()`.
3. **Check returned boolean** to determine if the transaction is final.
4. **Handle errors gracefully**, especially with malformed inputs or failed service lookups.

## Additional Resources

- [NLockTimeIsFinal Code](./nlocktime_finality.go) - Full example implementation
- [Go-SDK Transaction Type](https://pkg.go.dev/github.com/bsv-blockchain/go-sdk/transaction) - For parsing raw transactions
- [Bitcoin nLockTime Reference](https://en.bitcoin.it/wiki/NLockTime) - Understanding nLockTime usage
25 changes: 20 additions & 5 deletions pkg/services/services.go
Original file line number Diff line number Diff line change
Expand Up @@ -349,11 +349,6 @@ func (s *WalletServices) BsvExchangeRate(ctx context.Context) (float64, error) {
return bsvExchangeRate, nil
}

// FiatExchangeRate returns approximate exchange rate currency per base.
func (s *WalletServices) FiatExchangeRate(currency defs.Currency, base *defs.Currency) float64 {
panic("Not implemented yet")
}

// MerklePath attempts to obtain the merkle proof associated with a 32 byte transaction hash (txid).
func (s *WalletServices) MerklePath(ctx context.Context, txid string) (*wdk.MerklePathResult, error) {
result, err := s.merklePathServices.OneByOne(ctx, txid)
Expand Down Expand Up @@ -579,3 +574,23 @@ func (s *WalletServices) HashOutputScript(scriptHex string) (string, error) {
}
return outputScript, nil
}

// FiatExchangeRate returns approximate exchange rate currency per base.
// Uses config.FiatExchangeRates as the source.
func (s *WalletServices) FiatExchangeRate(currency defs.Currency, base *defs.Currency) float64 {
rates := s.config.FiatExchangeRates.Rates

baseCurrency := defs.USD
if base != nil {
baseCurrency = *base
}

currencyRate, ok1 := rates[currency]
baseRate, ok2 := rates[baseCurrency]

if !ok1 || !ok2 || baseRate == 0 {
return 0
}

return currencyRate / baseRate
}
Loading
Loading