diff --git a/app/contexts/BreezContext.tsx b/app/contexts/BreezContext.tsx index 11f4c6972..315a241d6 100644 --- a/app/contexts/BreezContext.tsx +++ b/app/contexts/BreezContext.tsx @@ -3,7 +3,17 @@ import { WalletCurrency } from "@app/graphql/generated" import { usePersistentStateContext } from "@app/store/persistent-state" import { Alert, Platform } from "react-native" import { v4 as uuidv4 } from "uuid" -import { initializeBreezSDK, getInfo, handleSparkMigration } from "@app/utils/breez-sdk" +import { + initializeBreezSDK, + getInfo, + handleSparkMigration, + registerLightningAddress, + getLightningAddress, + checkLightningAddressAvailable, +} from "@app/utils/breez-sdk" +import { useAppConfig } from "@app/hooks/use-app-config" +import { useAddressScreenQuery } from "@app/graphql/generated" +import { useIsAuthed } from "@app/graphql/is-authed-context" import SparkMigrationModal from "@app/components/spark-migration-modal" type BtcWallet = { @@ -34,6 +44,12 @@ type Props = { export const BreezProvider = ({ children }: Props) => { const { persistentState, updateState } = usePersistentStateContext() + const { appConfig } = useAppConfig() + const isAuthed = useIsAuthed() + const { data: meData } = useAddressScreenQuery({ + fetchPolicy: "cache-first", + skip: !isAuthed, + }) const [loading, setLoading] = useState(false) const [btcWallet, setBtcWallet] = useState({ id: "", @@ -111,6 +127,27 @@ export const BreezProvider = ({ children }: Props) => { } } + const ensureLightningAddress = async () => { + const username = meData?.me?.username + if (!username) return + + try { + const existing = await getLightningAddress() + console.log("BREEZ LIGHTNING ADDRESS: ", existing) + if (existing) return + + // Register with username as the Lightning address + const lightningAddress = username + uuidv4() + const res = await registerLightningAddress( + lightningAddress, + `Pay to ${username}@${appConfig.galoyInstance.lnAddressHostname}`, + ) + console.log("BREEZ LIGHTNING ADDRESS RES: ", res) + } catch (err) { + console.warn("Failed to register Lightning address:", err) + } + } + const getBreezInfo = async () => { if (initializingRef.current) return initializingRef.current = true @@ -120,6 +157,9 @@ export const BreezProvider = ({ children }: Props) => { await updateBalance() setLoading(false) + // Register Lightning address + await ensureLightningAddress() + // Trigger migration after Spark SDK is ready if (!persistentState.sparkMigrationCompleted) { await onMigrate() diff --git a/app/screens/import-wallet-screen/ImportWallet.tsx b/app/screens/import-wallet-screen/ImportWallet.tsx index bad93e14c..1ae8e7d32 100644 --- a/app/screens/import-wallet-screen/ImportWallet.tsx +++ b/app/screens/import-wallet-screen/ImportWallet.tsx @@ -16,6 +16,7 @@ import { useI18nContext } from "@app/i18n/i18n-react" import { useCreateAccount } from "@app/hooks/useCreateAccount" import { Text, useTheme, useThemeMode } from "@rneui/themed" import { usePersistentStateContext } from "@app/store/persistent-state" +import { useAppConfig } from "@app/hooks/use-app-config" // utils import { disconnectToSDK, initializeBreezSDK } from "@app/utils/breez-sdk" @@ -31,6 +32,7 @@ const ImportWallet: React.FC = ({ navigation, route }) => { const { mode } = useThemeMode() const { LL } = useI18nContext() const { updateState } = usePersistentStateContext() + const { appConfig } = useAppConfig() const { createDeviceAccountAndLogin } = useCreateAccount() const inputRef = useRef([]) diff --git a/app/screens/transaction-detail-screen/breez-transaction-detail-screen.tsx b/app/screens/transaction-detail-screen/breez-transaction-detail-screen.tsx index 8e9f84bad..272a38c16 100644 --- a/app/screens/transaction-detail-screen/breez-transaction-detail-screen.tsx +++ b/app/screens/transaction-detail-screen/breez-transaction-detail-screen.tsx @@ -1,5 +1,11 @@ import React from "react" -import { Linking, TouchableWithoutFeedback, View, TouchableOpacity, ScrollView } from "react-native" +import { + Linking, + TouchableWithoutFeedback, + View, + TouchableOpacity, + ScrollView, +} from "react-native" import { makeStyles, Text, useTheme, Card } from "@rneui/themed" import { StackScreenProps } from "@react-navigation/stack" import Icon from "react-native-vector-icons/Ionicons" @@ -167,8 +173,8 @@ export const BreezTransactionDetailScreen: React.FC = ({ route }) => { if (details) { if (details.tag === PaymentDetails_Tags.Lightning) { description = details.inner.description ?? undefined - paymentHash = details.inner.paymentHash - preimage = details.inner.preimage ?? undefined + paymentHash = details.inner.htlcDetails.paymentHash + preimage = details.inner.htlcDetails.preimage ?? undefined invoice = details.inner.invoice destinationPubkey = details.inner.destinationPubkey } else if (details.tag === PaymentDetails_Tags.Spark) { diff --git a/app/types/declaration.d.ts b/app/types/declaration.d.ts index 475131c04..457cd07b5 100644 --- a/app/types/declaration.d.ts +++ b/app/types/declaration.d.ts @@ -32,4 +32,5 @@ declare module "@env" { export const GREENLIGHT_PARTNER_KEY: string export const GOOGLE_PLACE_API_KEY: string export const MIGRATION_FEE_LNURL_W: string + export const BREEZ_LNURL_DOMAIN: string } diff --git a/app/types/transactions.ts b/app/types/transactions.ts index 1b36e4ecd..a2a19824e 100644 --- a/app/types/transactions.ts +++ b/app/types/transactions.ts @@ -1,5 +1,9 @@ import type { Payment } from "@breeztech/breez-sdk-spark-react-native" -import { PaymentDetails_Tags } from "@breeztech/breez-sdk-spark-react-native" +import { + PaymentDetails_Tags, + PaymentType, + PaymentStatus, +} from "@breeztech/breez-sdk-spark-react-native" import type { TransactionFragment } from "@app/graphql/generated" // ============================================================================ @@ -87,8 +91,7 @@ export const getTransactionAmount = (tx: UnifiedTransaction): number => { */ export const isReceiveTransaction = (tx: UnifiedTransaction): boolean => { if (isBreezTransaction(tx)) { - // PaymentType.Receive = 1 - return tx.payment.paymentType === 1 + return tx.payment.paymentType === PaymentType.Receive } return tx.transaction.direction === "RECEIVE" } @@ -100,13 +103,12 @@ export const getTransactionStatus = ( tx: UnifiedTransaction, ): "SUCCESS" | "PENDING" | "FAILURE" => { if (isBreezTransaction(tx)) { - // PaymentStatus: Completed = 0, Pending = 1, Failed = 2 switch (tx.payment.status) { - case 0: + case PaymentStatus.Completed: return "SUCCESS" - case 1: + case PaymentStatus.Pending: return "PENDING" - case 2: + case PaymentStatus.Failed: return "FAILURE" default: return "PENDING" diff --git a/app/utils/breez-sdk/spark.ts b/app/utils/breez-sdk/spark.ts index 0a64fd1fa..59c76e06b 100644 --- a/app/utils/breez-sdk/spark.ts +++ b/app/utils/breez-sdk/spark.ts @@ -28,11 +28,12 @@ import type { RecommendedFees, SendPaymentMethod, LnurlPayResponse, + LightningAddressInfo, Payment, Logger, LogEntry, } from "@breeztech/breez-sdk-spark-react-native" -import { API_KEY } from "@env" +import { API_KEY, BREEZ_LNURL_DOMAIN } from "@env" import { appendLog, initLogBuffer } from "./log-buffer" // Constants @@ -67,7 +68,7 @@ export const initializeBreezSDK = async (): Promise => { breezSDKInitializing = (async () => { try { - await retry(connectToSDK, 5000, 3) + await retry(() => connectToSDK(), 5000, 3) breezSDKInitialized = true return true } catch (error: unknown) { @@ -120,6 +121,7 @@ const connectToSDK = async (): Promise => { const config = defaultConfig(Network.Mainnet) config.apiKey = API_KEY + config.lnurlDomain = BREEZ_LNURL_DOMAIN config.maxDepositClaimFee = new MaxFee.NetworkRecommended({ leewaySatPerVbyte: BigInt(1), }) @@ -238,6 +240,7 @@ export const fetchBreezFee = async ( amount: BigInt(amountSats), tokenIdentifier: undefined, conversionOptions: undefined, + feePolicy: undefined, }) const fee = extractFeeFromPaymentMethod(prepareResponse.paymentMethod) return { fee: Number(fee), err: null } @@ -249,6 +252,7 @@ export const fetchBreezFee = async ( amount: BigInt(amountSats), tokenIdentifier: undefined, conversionOptions: undefined, + feePolicy: undefined, }) const fee = extractFeeFromPaymentMethod( prepareResponse.paymentMethod, @@ -266,6 +270,8 @@ export const fetchBreezFee = async ( payRequest: parsed.inner[0].payRequest, comment: undefined, validateSuccessActionUrl: undefined, + conversionOptions: undefined, + feePolicy: undefined, }) return { fee: Number(prepareResponse.feeSats), err: null } @@ -296,6 +302,7 @@ export const receivePaymentBreez = async ( description: description || "", amountSats: BigInt(amountSats || 0), expirySecs: undefined, + paymentHash: undefined, }), }) @@ -332,6 +339,7 @@ export const payLightningBreez = async ( amount: BigInt(amountSats), tokenIdentifier: undefined, conversionOptions: undefined, + feePolicy: undefined, }) const options = new SendPaymentOptions.Bolt11Invoice({ @@ -368,6 +376,7 @@ export const payOnchainBreez = async ( amount: BigInt(amountSats), tokenIdentifier: undefined, conversionOptions: undefined, + feePolicy: undefined, }) const confirmationSpeed = @@ -411,6 +420,8 @@ export const payLnurlBreez = async ( payRequest: input.inner[0].payRequest, comment: memo, validateSuccessActionUrl: true, + conversionOptions: undefined, + feePolicy: undefined, }) const response = await sdk.lnurlPay({ @@ -473,6 +484,8 @@ export const onRedeem = async ( payRequest: input.inner[0].payRequest, comment: memo, validateSuccessActionUrl: true, + conversionOptions: undefined, + feePolicy: undefined, }) const response = await sdk.lnurlPay({ @@ -612,3 +625,31 @@ export const refundDeposit = async ( return { success: false, error: message } } } + +// Lightning Address (LNURL-Pay) +export const checkLightningAddressAvailable = async ( + username: string, +): Promise => { + const sdk = getSDKInstance() + return sdk.checkLightningAddressAvailable({ username }) +} + +export const registerLightningAddress = async ( + username: string, + description?: string, +): Promise => { + const sdk = getSDKInstance() + return sdk.registerLightningAddress({ username, description }) +} + +export const getLightningAddress = async (): Promise< + LightningAddressInfo | undefined +> => { + const sdk = getSDKInstance() + return sdk.getLightningAddress() +} + +export const deleteLightningAddress = async (): Promise => { + const sdk = getSDKInstance() + await sdk.deleteLightningAddress() +} diff --git a/ios/Podfile.lock b/ios/Podfile.lock index 06e5611ba..3fe42c3b6 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -10,7 +10,7 @@ PODS: - breez_sdk_liquidFFI (0.11.13) - BreezSDKLiquid (0.11.13): - breez_sdk_liquidFFI (= 0.11.13) - - breeztech-breez-sdk-spark-react-native (0.7.14): + - breeztech-breez-sdk-spark-react-native (0.12.2-dev1): - DoubleConversion - glog - hermes-engine @@ -2996,7 +2996,7 @@ SPEC CHECKSUMS: breez_sdk_liquid: 5c229f9ab3bcf6b648bbf2d512f6fe1eee96d121 breez_sdk_liquidFFI: f05fadc0611126ade76d1fe6761ed8b020aabefb BreezSDKLiquid: ee6bf5a57f1b2533dc3c14c24c9773496f17b756 - breeztech-breez-sdk-spark-react-native: 0af390a27a5f95bc838cddbecead825b671f60b2 + breeztech-breez-sdk-spark-react-native: de3a2235712440ee9fa2aa92a04c6838312c8343 BVLinearGradient: 880f91a7854faff2df62518f0281afb1c60d49a3 DoubleConversion: f16ae600a246532c4020132d54af21d0ddb2a385 fast_float: 06eeec4fe712a76acc9376682e4808b05ce978b6 diff --git a/package.json b/package.json index b3435585e..33758c33d 100644 --- a/package.json +++ b/package.json @@ -59,7 +59,7 @@ "dependencies": { "@apollo/client": "3.9.0-alpha.5", "@bitcoinerlab/secp256k1": "^1.0.5", - "@breeztech/breez-sdk-spark-react-native": "^0.7.14", + "@breeztech/breez-sdk-spark-react-native": "^0.12.2-dev1", "@breeztech/react-native-breez-sdk-liquid": "^0.11.13", "@flash/client": "git+https://github.com/lnflash/flash-client.git", "@formatjs/intl-getcanonicallocales": "^2.3.0", diff --git a/yarn.lock b/yarn.lock index 6de48c260..188fa9b0f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1901,10 +1901,10 @@ "@noble/hashes" "^1.1.5" "@noble/secp256k1" "^1.7.1" -"@breeztech/breez-sdk-spark-react-native@^0.7.14": - version "0.7.14" - resolved "https://registry.yarnpkg.com/@breeztech/breez-sdk-spark-react-native/-/breez-sdk-spark-react-native-0.7.14.tgz#b8914719e67620aa6b85178d7a903d7efcaedbac" - integrity sha512-xHhcwD0/aDQAGg6mJM1K1Wepp3bmf3SnuK7tIC2jSX7VA308lB92u6ag3vIQMvAtz6mZptu+8Le+br9sr2u4/w== +"@breeztech/breez-sdk-spark-react-native@^0.12.2-dev1": + version "0.12.2-dev1" + resolved "https://registry.yarnpkg.com/@breeztech/breez-sdk-spark-react-native/-/breez-sdk-spark-react-native-0.12.2-dev1.tgz#7d84efaf69307d9b94270b10b1ad68228d077cc6" + integrity sha512-B3xls7j4/7GIwOJR3FPParNtjIQPBj3RTuelTDq2CCY+idStrJww37vdNyxp1yo6FnjkhIhdCPIjH7+PTAi1Yw== dependencies: uniffi-bindgen-react-native "^0.28.3-5"