Skip to content

Commit

Permalink
Merge branch 'AddressFromBackend' into staging-btcdirect
Browse files Browse the repository at this point in the history
  • Loading branch information
bznein committed Feb 4, 2025
2 parents 3979012 + d07e9d3 commit 92ca545
Show file tree
Hide file tree
Showing 5 changed files with 73 additions and 20 deletions.
5 changes: 3 additions & 2 deletions backend/arguments/arguments.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,8 +49,9 @@ type Arguments struct {
regtest bool

// devservers stores wether the app should connect to the dev servers.
// This also applies to the Pocket widget environment: if devserver is true, the widget
// will be loaded from the staging environment, otherwise from production.
// This also applies to the Pocket and BTCDirect widget environments:
// if devserver is true, the widgets will be loaded from the staging environment,
// otherwise from production.
// The devservers configuration is not persisted when switching back to production.
devservers bool

Expand Down
35 changes: 35 additions & 0 deletions backend/exchanges/btcdirect.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,29 @@ package exchanges
import (
"slices"

"github.com/BitBoxSwiss/bitbox-wallet-app/backend/accounts"
"github.com/BitBoxSwiss/bitbox-wallet-app/backend/coins/coin"
)

const (
// BTCDirectName is the name of the exchange, it is unique among all the supported exchanges.
BTCDirectName = "btcdirect"

btcDirectTestApiKey = "6ed4d42bd02eeac1776a6bb54fa3126f779c04d5c228fe5128bb74e89ef61f83"

btcDirectProdAPiKey = "7d71f633626901d5c4d06d91f7d0db2c15cdf524ddd0ebcd36f4d9c4e04694cd"

btcDirectTestUrl = "/btcdirect/fiat-to-coin.html"

btcDirectProdUrl = "https://bitboxapp.shiftcrypto.io/widgets/btcdirect/v1/fiat-to-coin.html"
)

type btcDirectInfo struct {
Url string
ApiKey string
Address *string
}

var regions = []string{
"AT", "BE", "BG", "CH", "CY", "CZ", "DE", "DK", "EE", "ES", "FI", "FR", "GR",
"HR", "HU", "IE", "IS", "IT", "LI", "LT", "LU", "LV", "MC", "MT", "NL", "NO",
Expand Down Expand Up @@ -76,3 +91,23 @@ func BtcDirectDeals() *ExchangeDealsList {
},
}
}

// BtcDirectInfo returns the information needed to interact with BtcDirect.
// If `devServers` is true, it returns testing URL and ApiKey.
func BtcDirectInfo(action ExchangeAction, acct accounts.Interface, devServers bool) btcDirectInfo {
res := btcDirectInfo{
Url: btcDirectProdUrl,
ApiKey: btcDirectProdAPiKey,
}

if devServers {
res.Url = btcDirectTestUrl
res.ApiKey = btcDirectTestApiKey
}

if action == BuyAction {
addr := acct.GetUnusedReceiveAddresses()[0].Addresses[0].EncodeForHumans()
res.Address = &addr
}
return res
}
28 changes: 28 additions & 0 deletions backend/handlers/handlers.go
Original file line number Diff line number Diff line change
Expand Up @@ -243,6 +243,7 @@ func NewHandlers(
getAPIRouterNoError(apiRouter)("/exchange/deals/{action}/{code}", handlers.getExchangeDeals).Methods("GET")
getAPIRouterNoError(apiRouter)("/exchange/supported/{code}", handlers.getExchangeSupported).Methods("GET")
getAPIRouterNoError(apiRouter)("/exchange/btcdirect-otc/supported/{code}", handlers.getExchangeBtcDirectOTCSupported).Methods("GET")
getAPIRouterNoError(apiRouter)("/exchange/btcdirect/info/{action}/{code}", handlers.getExchangeBtcDirectInfo).Methods("GET")
getAPIRouter(apiRouter)("/exchange/moonpay/buy-info/{code}", handlers.getExchangeMoonpayBuyInfo).Methods("GET")
getAPIRouterNoError(apiRouter)("/exchange/pocket/api-url/{action}", handlers.getExchangePocketURL).Methods("GET")
getAPIRouterNoError(apiRouter)("/exchange/pocket/verify-address", handlers.postPocketWidgetVerifyAddress).Methods("POST")
Expand Down Expand Up @@ -1438,6 +1439,33 @@ func (handlers *Handlers) getExchangeMoonpayBuyInfo(r *http.Request) (interface{
return resp, nil
}

func (handlers *Handlers) getExchangeBtcDirectInfo(r *http.Request) interface{} {
type result struct {
Success bool `json:"success"`
ErrorMessage string `json:"errorMessage"`
Url string `json:"url"`
ApiKey string `json:"apiKey"`
Address *string `json:"address"`
}

code := accountsTypes.Code(mux.Vars(r)["code"])
acct, err := handlers.backend.GetAccountFromCode(code)
accountValid := acct != nil && acct.Offline() == nil && !acct.FatalError()
if err != nil || !accountValid {
return result{Success: false, ErrorMessage: "Account is not valid."}
}

action := exchanges.ExchangeAction(mux.Vars(r)["action"])
btcInfo := exchanges.BtcDirectInfo(action, acct, handlers.backend.DevServers())

return result{
Success: true,
Url: btcInfo.Url,
ApiKey: btcInfo.ApiKey,
Address: btcInfo.Address,
}
}

func (handlers *Handlers) getExchangePocketURL(r *http.Request) interface{} {
lang := handlers.backend.Config().AppConfig().Backend.UserLanguage
if len(lang) == 0 {
Expand Down
11 changes: 3 additions & 8 deletions frontends/web/src/api/exchanges.ts
Original file line number Diff line number Diff line change
Expand Up @@ -103,22 +103,17 @@ export type TBTCDirectInfoResponse = {
success: true;
url: string;
apiKey: string;
address?: string;
} | {
success: false;
errorMessage: string;
};

export const getBTCDirectInfo = (
action: TExchangeAction,
code: string,
): Promise<TBTCDirectInfoResponse> => {
console.log(action);
// TODO: change to return apiGet(`exchange/btc-direct/info/${action}`); or similar
return Promise.resolve({
success: true,
url: '/btcdirect/fiat-to-coin.html', // local static file for testing
apiKey: '6ed4d42bd02eeac1776a6bb54fa3126f779c04d5c228fe5128bb74e89ef61f83', // debug
// apiKey: '7d71f633626901d5c4d06d91f7d0db2c15cdf524ddd0ebcd36f4d9c4e04694cd', // prod
});
return apiGet(`exchange/btcdirect/info/${action}/${code}`);
};

export type SupportedExchanges= {
Expand Down
14 changes: 4 additions & 10 deletions frontends/web/src/routes/exchange/btcdirect.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import { useState, useEffect, createRef, useContext, useRef } from 'react';
import { useTranslation } from 'react-i18next';
import { getBTCDirectInfo } from '@/api/exchanges';
import { AppContext } from '@/contexts/AppContext';
import { AccountCode, getReceiveAddressList, IAccount } from '@/api/account';
import { AccountCode, IAccount } from '@/api/account';
import { useLoad } from '@/hooks/api';
import { useDarkmode } from '@/hooks/darkmode';
import { UseDisableBackButton } from '@/hooks/backbutton';
Expand Down Expand Up @@ -51,13 +51,7 @@ export const BTCDirect = ({ accounts, code }: TProps) => {
const { isDarkMode } = useDarkmode();

const iframeRef = useRef<HTMLIFrameElement | null>(null);
const btcdirectInfo = useLoad(() => getBTCDirectInfo('buy'));

const receiveAddresses = useLoad(getReceiveAddressList(code));

// TODO: address should probably come from the backend, i.e. ETH address
const p2wpkhAddresses = receiveAddresses?.find(({ scriptType }) => scriptType === 'p2wpkh')?.addresses || [];
const address = p2wpkhAddresses[0]?.address || '';
const btcdirectInfo = useLoad(() => getBTCDirectInfo('buy', code));

const [agreedTerms, setAgreedTerms] = useState(false);
const [iframeLoaded, setIframeLoaded] = useState(false);
Expand Down Expand Up @@ -124,7 +118,7 @@ export const BTCDirect = ({ accounts, code }: TProps) => {
<div style={{ height }}>
<UseDisableBackButton />
{!iframeLoaded && <Spinner text={t('loading')} />}
{ address && btcdirectInfo?.success && (
{ btcdirectInfo?.success && (
<iframe
onLoad={() => {
setIframeLoaded(true);
Expand All @@ -141,7 +135,7 @@ export const BTCDirect = ({ accounts, code }: TProps) => {
data-theme={isDarkMode ? 'dark' : 'light'}
data-base-currency={getCoinCode(account.coinCode)}
data-quote-currency={'EUR'}
data-address={address}
data-address={btcdirectInfo.address}
data-mode={
isTesting || debug ? 'debug' : 'production'
}
Expand Down

0 comments on commit 92ca545

Please sign in to comment.