Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
98 commits
Select commit Hold shift + click to select a range
b72cc6e
add transfer feature mvp (topup with card)
islandbitcoin Jul 15, 2025
0f00afa
Your Private Profile
Nodirbek75 Jul 17, 2025
38fcc5d
redesign and refactor transfer-screen, topup-screen and card-payment-…
Nodirbek75 Jul 19, 2025
4223024
BankTransfer screen is implemented and added in the root navigation
Nodirbek75 Jul 22, 2025
f501504
restructure buy-sell-bitcoin flow screens
Nodirbek75 Jul 22, 2025
b23328a
add BankTransfer texts on the i18n file
Nodirbek75 Jul 22, 2025
26c64ad
refactor and clean up CardPayment webview screen
Nodirbek75 Jul 22, 2025
71f1e49
use texts from i18n on the BankTransfer screen and update BuyBitcoinD…
Nodirbek75 Jul 22, 2025
35458a6
update BuySellBitcoin screen icons and added navigation to cashout
Nodirbek75 Jul 23, 2025
025c12b
ArrowUpDown icon btn is added
Nodirbek75 Jul 23, 2025
9c7a02f
add back cashout mutations
Nodirbek75 Jul 23, 2025
04b6a9a
show Transfer button instead of Cashout button on the home screen
Nodirbek75 Jul 23, 2025
b41ea81
update CashoutDetails screen
Nodirbek75 Jul 23, 2025
cccfcee
update cashout description text
Nodirbek75 Jul 25, 2025
ba76a0c
update CashoutConfirmation screen to show amount in display currency
Nodirbek75 Jul 25, 2025
a01438e
add back exchangeRate to the requestCashout offer response and update…
Nodirbek75 Jul 31, 2025
7ea92b6
Merge remote-tracking branch 'origin' into feat/fygaro
Nodirbek75 Jul 31, 2025
63148d2
add back exchangeRate on the generated.gql and generated.ts files
Nodirbek75 Jul 31, 2025
c7e154e
Merge remote-tracking branch 'origin' into feat/fygaro
Nodirbek75 Aug 14, 2025
11a1ddb
BuyBitcoinSuccess screen is implemented and added in root navigation
Nodirbek75 Aug 15, 2025
0159919
Merge remote-tracking branch 'origin' into feat/fygaro
Nodirbek75 Sep 17, 2025
d47d607
enabled card payments via Fygaro - webhook wip
islandbitcoin Sep 24, 2025
47046d0
add developer comments
islandbitcoin Sep 24, 2025
5d69dda
Merge remote-tracking branch 'origin' into feat/fygaro
Nodirbek75 Apr 22, 2026
d7a0bb6
add cashout api integration changes in this branch
Nodirbek75 Apr 22, 2026
29bf684
Merge remote-tracking branch 'origin' into feat/fygaro
Nodirbek75 Apr 28, 2026
73277be
Withdraw to section is implemented and added on the cashout confirma…
Nodirbek75 Apr 29, 2026
5f28f1d
use TransferOptionModal to show topup option on the TopupCashout scre…
Nodirbek75 Apr 29, 2026
66275f9
refactor and clean up topup/cashout flow updting structure
Nodirbek75 Apr 30, 2026
d5b80dc
Merge remote-tracking branch 'origin' into feat/fygaro
Nodirbek75 Apr 30, 2026
8f80698
Merge remote-tracking branch 'origin' into feat/fygaro
Nodirbek75 May 5, 2026
0d918df
Implement BridgeKycModal to get user details (full name, email and ky…
Nodirbek75 May 6, 2026
f329114
Merge branch 'main' into feat/fygaro
Nodirbek75 May 12, 2026
a2861ee
Implement BridgeKycWebView screen to handle tos and kyc flow and add …
Nodirbek75 May 15, 2026
21ed900
CashWalletCutoverModal is implemented to notice users about migration
Nodirbek75 May 15, 2026
1d149d3
integrate bridgeKycStatus query and bridgeInitiateKyc mutation into T…
Nodirbek75 May 17, 2026
7f2e84b
use CashWalletCutoverModal in the home screen
Nodirbek75 May 17, 2026
d6a446e
update ui to show pending status properly and show alert message to t…
Nodirbek75 May 17, 2026
55b6bea
fix bridgeKycStatus response showing null and implement refetchKycSta…
Nodirbek75 May 18, 2026
7e2e94d
implement pull to refresh to fetch updated kyc status on the TopupCas…
Nodirbek75 May 18, 2026
4876e14
open external browser when user press term of service or privacy poic…
Nodirbek75 May 20, 2026
9153a86
update BankTransfer screen to show bridge virtual account details as …
Nodirbek75 May 21, 2026
5786879
add texts for bridge integration
Nodirbek75 May 21, 2026
5cbfe10
update TopupCashout and TopupDetails screens to integrate bridge flow
Nodirbek75 May 21, 2026
4cadac0
update button-group component styling
Nodirbek75 May 21, 2026
ea3f3af
replace DropDownField component with ButtonGroup for kycType section
Nodirbek75 May 21, 2026
81276f3
bridge related mutations and queries are added
Nodirbek75 May 21, 2026
8cd9529
update codegen.ynl
Nodirbek75 May 21, 2026
d155dc2
fix tos accept button routing to external browser issue
Nodirbek75 May 22, 2026
4753d84
add BankAccounts query that fetches user bank account from erpnext
Nodirbek75 May 26, 2026
6a3feac
update CashoutDetails screen to implement requestCashout mutation cha…
Nodirbek75 May 26, 2026
dea543b
resolve CashoutConfirmation screen issues after cashout mutations upd…
Nodirbek75 May 26, 2026
64280bf
resolve ios build issue
Nodirbek75 May 26, 2026
36900de
display cashout/topup button to all users except level Zero on the ho…
Nodirbek75 May 31, 2026
cc87f3a
update topup cashout option on the TopupCashout screen that level Zer…
Nodirbek75 May 31, 2026
955c0e9
fixed node versioning
Nodirbek75 Jun 1, 2026
cd9961d
merge main
Nodirbek75 Jun 1, 2026
24598e9
update Type Definitions & Core Infrastructure to support usdt wallet
Nodirbek75 Jun 7, 2026
104b829
update Price Conversion & Display hooks to support usdt wallet
Nodirbek75 Jun 7, 2026
fb752d4
change getUsdtWallet to getCashWallet that returns usdt wallet if exi…
Nodirbek75 Jun 7, 2026
f47cd2b
update UI Components to support USDT wallet
Nodirbek75 Jun 7, 2026
2c83859
update Payment Flows (interlader, lightning and onchain) to support U…
Nodirbek75 Jun 7, 2026
a9cfcb4
update multiple screens to support usdt wallet
Nodirbek75 Jun 8, 2026
462106e
replace getUsdWallet with getCashWallet method
Nodirbek75 Jun 8, 2026
f22c57e
set the header X-Flash-Client-Capabilities: cash-wallet-usdt-v1 to su…
Nodirbek75 Jun 8, 2026
72238d8
enable card top up for level one users
Nodirbek75 Jun 9, 2026
a921cbc
Add International upgrade option (#633)
patoo0x Jun 9, 2026
8bf9163
hide and never display CashWalletCutover modal once user see it
Nodirbek75 Jun 10, 2026
e8a0eb3
Merge branch 'feat/fygaro' of nodirbek75:lnflash/flash-mobile into fe…
Nodirbek75 Jun 10, 2026
3c598d0
Update CashWalletCutoverModal to show modal only for existing users a…
Nodirbek75 Jun 15, 2026
2eb9d0b
bridge withdrawal mutation and queries are added
Nodirbek75 Jun 15, 2026
025ed86
BridgeExternalAccountWebView is implemented and added on the root nav…
Nodirbek75 Jun 15, 2026
d27a63c
integrate bridgeAddExternalAccount mutation and bridgeExternalAccount…
Nodirbek75 Jun 15, 2026
8b4393b
ci: run checks on the feat/fygaro integration branch (#637)
islandbitcoin Jun 17, 2026
e0d7aae
fix(ci): bump CI Node to 20 and resolve spelling-check failures (#638)
islandbitcoin Jun 17, 2026
b0dcdde
ci: stop conventional-pr from auto-closing invalid PRs (#639)
islandbitcoin Jun 17, 2026
c03dc3f
fix: render USDT amounts and currency tags (#634)
islandbitcoin Jun 18, 2026
0433e73
feat: add bridge cashout confirmation flow (#635)
islandbitcoin Jun 18, 2026
dfa5570
update cutover label (#636)
islandbitcoin Jun 18, 2026
3e80814
fix(ci): resolve feat/fygaro tsconfig TS5095 + spelling allowlist (#642)
islandbitcoin Jun 18, 2026
f29db91
ci: run Check Code + Test on push to main (#643)
islandbitcoin Jun 18, 2026
fa75680
fix(receive): convert USDT settlement amount from micros to cents for…
forge0x Jun 17, 2026
1561a7e
fix(cashout): prefer JMD bank account for local cashout
forge0x Jun 18, 2026
a1c07eb
test(payment-request): run spec without ttypescript
forge0x Jun 18, 2026
55b8a38
feat(ui): display stablecoins as fiat currency in all screens (#648)
islandbitcoin Jun 19, 2026
b12d538
fix(tx-history): map USDT to USD in transaction list items
forge0x Jun 19, 2026
25b054e
fix(tx-history): USDT secondary amount shows $1.00 not ₿1,587
forge0x Jun 19, 2026
4d70de3
fix(display): show '.0000000 USD' for USDT amounts
forge0x Jun 19, 2026
b1fee0e
chore(bridge): drop KYC-response console.logs (#649)
islandbitcoin Jun 19, 2026
4877abe
chore: remove unused displayCurrencyCode import from use-display-curr…
forge0x Jun 19, 2026
d48a358
fix(receive): remove duplicate 'Cash' wallet option in wallet picker
forge0x Jun 19, 2026
1763f81
fix(tx-history): cash transactions respect user's display currency
forge0x Jun 19, 2026
d218df2
test: update USDT display assertion for stablecoin→USD mapping
forge0x Jun 19, 2026
f07b83d
fix: display the overlay during the selfie step of the Bridge KYC flo…
Nodirbek75 Jun 25, 2026
9ccc6ae
test: repair frontend CI checks
patoo0x Jun 25, 2026
5a4febf
Merge remote-tracking branch 'origin/main' into feat/fygaro
patoo0x Jun 25, 2026
3ac81f2
test: stabilize frontend CI checks
patoo0x Jun 25, 2026
b49acb0
Merge remote-tracking branch 'origin/main' into feat/fygaro
Jun 26, 2026
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
5 changes: 4 additions & 1 deletion .github/workflows/conventional.yaml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
name: "Conventional commits"
on:
pull_request:
branches: [main]
branches: [main, feat/fygaro]
jobs:
conventional:
name: "Conventional commits"
Expand All @@ -14,3 +14,6 @@ jobs:
message: this PR needs to be updated to follow conventional commits message
body: false
issue: false
# Report on invalid PRs instead of closing them (avoids nuking
# long-lived PRs like the feat/fygaro umbrella #621).
close: false
2 changes: 1 addition & 1 deletion .github/workflows/spelling.yml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
name: Spelling
on:
pull_request:
branches: [main]
branches: [main, feat/fygaro]

jobs:
spelling:
Expand Down
5 changes: 4 additions & 1 deletion .storybook/views/story-screen.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,14 @@ const PersistentStateWrapper: React.FC<React.PropsWithChildren> = ({ children })
<PersistentStateContext.Provider
value={{
persistentState: {
schemaVersion: 6,
schemaVersion: 7,
galoyInstance: {
id: "Main",
},
galoyAuthToken: "",
hasInitializedBreezSDK: false,
unclaimedDeposits: 0,
closedQuickStartTypes: [],
},
updateState: () => {},
resetState: () => {},
Expand Down
2 changes: 1 addition & 1 deletion .typesafe-i18n.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"adapter": "react",
"$schema": "https://unpkg.com/typesafe-i18n@5.26.2/schema/typesafe-i18n.json",
"$schema": "https://unpkg.com/typesafe-i18n@5.27.1/schema/typesafe-i18n.json",
"outputPath": "./app/i18n"
}
46 changes: 46 additions & 0 deletions __mocks__/@breeztech/breez-sdk-spark-react-native.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
const SdkEvent_Tags = {
PaymentPending: "PaymentPending",
PaymentSucceeded: "PaymentSucceeded",
PaymentFailed: "PaymentFailed",
Synced: "Synced",
Optimization: "Optimization",
}

const PaymentType = {
Send: "Send",
Receive: "Receive",
}

const PaymentStatus = {
Pending: "Pending",
Complete: "Complete",
Failed: "Failed",
}

class Bolt11Invoice {
constructor(args) {
Object.assign(this, args)
}
}

class BitcoinAddress {
constructor(args) {
Object.assign(this, args)
}
}

module.exports = {
__esModule: true,
BitcoinAddress,
Bolt11Invoice,
PaymentStatus,
PaymentType,
ReceivePaymentMethod: {
BitcoinAddress,
Bolt11Invoice,
},
SdkEvent_Tags,
connect: jest.fn(),
defaultConfig: jest.fn(),
disconnect: jest.fn(),
}
12 changes: 12 additions & 0 deletions __mocks__/@env.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
module.exports = {
API_KEY: "test-api-key",
APP_CHECK_ANDROID_DEBUG_TOKEN: "test-android-app-check-token",
APP_CHECK_IOS_DEBUG_TOKEN: "test-ios-app-check-token",
BREEZ_LNURL_DOMAIN: "test.flashapp.me",
GOOGLE_PLACE_API_KEY: "test-google-place-api-key",
GREENLIGHT_PARTNER_CERT: "test-greenlight-partner-cert",
GREENLIGHT_PARTNER_KEY: "test-greenlight-partner-key",
INVITE_CODE: "test-invite-code",
MIGRATION_FEE_LNURL_W: "https://test.flashapp.me/lnurlw",
MNEMONIC_WORDS: "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about",
}
2 changes: 1 addition & 1 deletion __mocks__/@react-native-async-storage/async-storage.js
Original file line number Diff line number Diff line change
@@ -1 +1 @@
export { AsyncStorageMock as default } from "@react-native-async-storage/async-storage/jest/async-storage-mock"
module.exports = require("@react-native-async-storage/async-storage/jest/async-storage-mock")
1 change: 1 addition & 0 deletions __mocks__/@react-native-community/netinfo.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
module.exports = require("@react-native-community/netinfo/jest/netinfo-mock")
4 changes: 4 additions & 0 deletions __mocks__/breez-sdk.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
module.exports = {
receiveOnchainBreez: jest.fn(() => Promise.resolve({ paymentRequest: "" })),
receivePaymentBreez: jest.fn(() => Promise.resolve({ paymentRequest: "" })),
}
19 changes: 19 additions & 0 deletions __mocks__/react-native-fs.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
const RNFS = {
CachesDirectoryPath: "/tmp",
DocumentDirectoryPath: "/tmp",
DownloadDirectoryPath: "/tmp",
TemporaryDirectoryPath: "/tmp",
appendFile: jest.fn(() => Promise.resolve()),
copyFile: jest.fn(() => Promise.resolve()),
downloadFile: jest.fn(() => ({
jobId: 1,
promise: Promise.resolve({ statusCode: 200 }),
})),
exists: jest.fn(() => Promise.resolve(false)),
readFile: jest.fn(() => Promise.resolve("")),
unlink: jest.fn(() => Promise.resolve()),
writeFile: jest.fn(() => Promise.resolve()),
}

module.exports = RNFS
module.exports.default = RNFS
11 changes: 11 additions & 0 deletions __mocks__/react-native-google-places-autocomplete.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
const React = require("react")
const { View } = require("react-native")

const GooglePlacesAutocomplete = React.forwardRef((props, ref) =>
React.createElement(View, { ...props, ref }),
)

module.exports = {
__esModule: true,
GooglePlacesAutocomplete,
}
7 changes: 7 additions & 0 deletions __mocks__/react-native-haptic-feedback.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
const trigger = jest.fn()

module.exports = {
__esModule: true,
default: { trigger },
trigger,
}
23 changes: 23 additions & 0 deletions __mocks__/react-native-image-picker.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
const emptyResult = { assets: [], didCancel: true }

const launchImageLibrary = jest.fn((_, callback) => {
if (typeof callback === "function") {
callback(emptyResult)
}

return Promise.resolve(emptyResult)
})

const launchCamera = jest.fn((_, callback) => {
if (typeof callback === "function") {
callback(emptyResult)
}

return Promise.resolve(emptyResult)
})

module.exports = {
__esModule: true,
launchCamera,
launchImageLibrary,
}
26 changes: 26 additions & 0 deletions __mocks__/react-native-keychain.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
const credentialsByServer = new Map()

const getServer = (serverOrOptions) =>
typeof serverOrOptions === "string" ? serverOrOptions : serverOrOptions?.server

const getInternetCredentials = jest.fn((serverOrOptions) =>
Promise.resolve(credentialsByServer.get(getServer(serverOrOptions)) || false),
)

const setInternetCredentials = jest.fn((server, username, password) => {
credentialsByServer.set(server, { password, server, username })
return Promise.resolve(true)
})

const resetInternetCredentials = jest.fn((serverOrOptions) => {
credentialsByServer.delete(getServer(serverOrOptions))
return Promise.resolve(true)
})

module.exports = {
__esModule: true,
ACCESSIBLE: {},
getInternetCredentials,
resetInternetCredentials,
setInternetCredentials,
}
21 changes: 21 additions & 0 deletions __mocks__/react-native-nfc-manager.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
const NfcManager = {
cancelTechnologyRequest: jest.fn(),
getTag: jest.fn(),
isEnabled: jest.fn(async () => false),
isSupported: jest.fn(async () => false),
requestTechnology: jest.fn(),
start: jest.fn(),
}

module.exports = {
__esModule: true,
default: NfcManager,
Ndef: {
text: {
decodePayload: jest.fn(() => ""),
},
},
NfcTech: {
Ndef: "Ndef",
},
}
19 changes: 19 additions & 0 deletions __mocks__/react-native-vision-camera.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
const React = require("react")

const requestPermission = jest.fn(() => Promise.resolve(true))

const Camera = React.forwardRef((props, ref) =>
React.createElement("Camera", { ...props, ref }),
)

module.exports = {
__esModule: true,
Camera,
CameraRuntimeError: Error,
useCameraDevice: jest.fn(() => ({ id: "back" })),
useCameraPermission: jest.fn(() => ({
hasPermission: true,
requestPermission,
})),
useCodeScanner: jest.fn((config) => config),
}
6 changes: 6 additions & 0 deletions __mocks__/react-native-walkthrough-tooltip.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
const React = require("react")

const Tooltip = ({ children }) => React.createElement(React.Fragment, null, children)

module.exports = Tooltip
module.exports.default = Tooltip
28 changes: 28 additions & 0 deletions __tests__/components/currency-tag.spec.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import * as React from "react"
import { describe, expect, it } from "@jest/globals"
import { render } from "@testing-library/react-native"
import { ThemeProvider } from "@rneui/themed"

import { CurrencyTag } from "@app/components/currency-tag"
import { WalletCurrency } from "@app/graphql/generated"
import { displayCurrencyCode } from "@app/utils/currency-display"
import theme from "@app/rne-theme/theme"

const renderCurrencyTag = (walletCurrency: WalletCurrency) =>
render(
<ThemeProvider theme={theme}>
<CurrencyTag walletCurrency={walletCurrency} />
</ThemeProvider>,
)

describe("CurrencyTag", () => {
it.each([WalletCurrency.Btc, WalletCurrency.Usd, WalletCurrency.Usdt])(
"renders a %s tag",
(walletCurrency: WalletCurrency) => {
const { getByText } = renderCurrencyTag(walletCurrency)

// USDT displays as USD (stablecoin → fiat mapping)
expect(getByText(displayCurrencyCode(walletCurrency))).toBeTruthy()
},
)
})
1 change: 1 addition & 0 deletions __tests__/config/galoy-instances.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ it("get a full object with Custom", () => {
posUrl: "https://pay.custom.com/",
lnAddressHostname: "custom.com",
blockExplorer: "https://mempool.space/tx/",
relayUrl: "wss://relay.custom.com",
} as const

const res = resolveGaloyInstanceOrDefault(CustomInstance)
Expand Down
55 changes: 52 additions & 3 deletions __tests__/hooks/use-display-currency.spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,11 @@ import { MockedProvider } from "@apollo/client/testing"
import { PropsWithChildren } from "react"
import * as React from "react"
import { IsAuthedContextProvider } from "@app/graphql/is-authed-context"
import { CurrencyListDocument, RealtimePriceDocument } from "@app/graphql/generated"
import {
CurrencyListDocument,
RealtimePriceDocument,
WalletCurrency,
} from "@app/graphql/generated"

const mocksNgn = [
{
Expand Down Expand Up @@ -135,12 +139,32 @@ const wrapWithMocks =

describe("usePriceConversion", () => {
describe("testing moneyAmountToMajorUnitOrSats", () => {
it("formats USDT micros as major units", async () => {
const { result } = renderHook(useDisplayCurrency, {
wrapper: wrapWithMocks([]),
})

const moneyAmount = {
amount: 179_554,
currency: WalletCurrency.Usdt,
currencyCode: "USD",
}

expect(result.current.moneyAmountToMajorUnitOrSats(moneyAmount)).toBe(0.179554)
// USDT displays as USD (stablecoin → fiat display mapping)
expect(result.current.formatMoneyAmount({ moneyAmount })).toBe("$0.179554 USD")
})

it("with 0 digits", async () => {
const { result, waitForNextUpdate } = renderHook(useDisplayCurrency, {
const { result, waitFor } = renderHook(useDisplayCurrency, {
wrapper: wrapWithMocks(mocksJpy),
})

await waitForNextUpdate()
await waitFor(
() =>
result.current.displayCurrency === "JPY" &&
result.current.fractionDigits === 0,
)

const res = result.current.moneyAmountToMajorUnitOrSats({
amount: 100,
Expand Down Expand Up @@ -230,4 +254,29 @@ describe("usePriceConversion", () => {
displayCurrency: "NGN",
})
})

it("converts USDT micros to display currency using USD-cent price", async () => {
const { result, waitFor } = renderHook(useDisplayCurrency, {
wrapper: wrapWithMocks(mocksNgn),
})

await waitFor(
() => {
return result.current.displayCurrency === "NGN"
},
{
timeout: 4000,
},
)

const displayAmount = result.current.moneyAmountToDisplayCurrencyString({
moneyAmount: {
amount: 1_000_000,
currency: WalletCurrency.Usdt,
currencyCode: "USDT",
},
})

expect(displayAmount).toBe("₦100.00")
})
})
Loading
Loading