Skip to content

Commit 219d987

Browse files
committed
Merge branch 'master' of github.com:arkade-os/wallet into feat/mnemonic-identity
2 parents 6f099d9 + 416c960 commit 219d987

7 files changed

Lines changed: 182 additions & 25 deletions

File tree

package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@
44
"private": true,
55
"homepage": ".",
66
"dependencies": {
7-
"@arkade-os/boltz-swap": "0.3.19",
8-
"@arkade-os/sdk": "0.4.18",
7+
"@arkade-os/boltz-swap": "0.3.21",
8+
"@arkade-os/sdk": "0.4.20",
99
"@branta-ops/branta": "0.0.9",
1010
"@ionic/react": "^8.5.6",
1111
"@lendasat/lendasat-wallet-bridge": "^0.0.90",

playwright.config.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,8 @@ export default defineConfig({
1919
contextOptions: { reducedMotion: 'reduce' },
2020
},
2121
webServer: {
22-
command: 'VITE_NOSTR_RELAY_URL=ws://localhost:10547 VITE_DELEGATE_ENABLED=false pnpm start',
22+
command:
23+
'VITE_NOSTR_RELAY_URL=ws://localhost:10547 VITE_DELEGATE_ENABLED=false VITE_LNURL_SERVER_URL=http://localhost:9090 pnpm start',
2324
port: 3002,
2425
},
2526
projects: [

pnpm-lock.yaml

Lines changed: 37 additions & 11 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/components/TransactionsList.tsx

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ const TransactionLine = ({ tx, onClick }: { tx: Tx; onClick: () => void }) => {
3535
const prefix = tx.type === 'sent' ? '-' : '+'
3636
const amount = `${prefix} ${config.showBalance ? prettyAmount(tx.amount) : prettyHide(tx.amount)}`
3737
const date = tx.createdAt ? prettyDate(tx.createdAt) : tx.boardingTxid ? 'Unconfirmed' : 'Unknown'
38+
const asAssets = Boolean(tx.assets?.length)
3839
const issuance = isIssuance(tx)
3940
const burn = isBurn(tx)
4041

@@ -49,7 +50,7 @@ const TransactionLine = ({ tx, onClick }: { tx: Tx; onClick: () => void }) => {
4950
? 'orange'
5051
: ''
5152
const value = toFiat(tx.amount)
52-
const small = config.currencyDisplay === CurrencyDisplay.Both
53+
const small = asAssets || config.currencyDisplay === CurrencyDisplay.Both
5354
const world = config.showBalance ? prettyFiatAmount(value, config.fiat) : prettyFiatHide(value, config.fiat)
5455
return (
5556
<Text color={color} small={small}>
@@ -79,7 +80,11 @@ const TransactionLine = ({ tx, onClick }: { tx: Tx; onClick: () => void }) => {
7980

8081
const Sats = () =>
8182
issuance || burn ? null : (
82-
<Text color={tx.type === 'received' ? (tx.preconfirmed && tx.boardingTxid ? 'orange' : 'green') : ''} thin>
83+
<Text
84+
color={tx.type === 'received' ? (tx.preconfirmed && tx.boardingTxid ? 'orange' : 'green') : ''}
85+
smaller={asAssets}
86+
thin
87+
>
8388
{amount}
8489
</Text>
8590
)
@@ -96,7 +101,7 @@ const TransactionLine = ({ tx, onClick }: { tx: Tx; onClick: () => void }) => {
96101
const decimals = meta?.decimals ?? 8
97102
return (
98103
<FlexRow key={a.assetId} gap='0.25rem' end>
99-
<Text color={color} smaller>
104+
<Text color={color}>
100105
{config.showBalance
101106
? `${formatAssetAmount(a.amount, decimals)} ${ticker ?? meta?.name ?? `${a.assetId.slice(0, 8)}...`}`
102107
: prettyHide(a.amount, ticker ?? meta?.name ?? `${a.assetId.slice(0, 8)}...`)}
@@ -110,7 +115,6 @@ const TransactionLine = ({ tx, onClick }: { tx: Tx; onClick: () => void }) => {
110115
}
111116

112117
const rowStyle = {
113-
alignItems: 'center',
114118
borderTop: border,
115119
cursor: 'pointer',
116120
padding: '0.5rem 0',
@@ -128,7 +132,12 @@ const TransactionLine = ({ tx, onClick }: { tx: Tx; onClick: () => void }) => {
128132

129133
const Right = () => (
130134
<div style={{ textAlign: 'right' }}>
131-
{config.currencyDisplay === CurrencyDisplay.Fiat ? (
135+
{tx.assets?.length ? (
136+
<>
137+
<AssetInfo />
138+
{config.currencyDisplay === CurrencyDisplay.Fiat ? <Fiat /> : <Sats />}
139+
</>
140+
) : config.currencyDisplay === CurrencyDisplay.Fiat ? (
132141
<Fiat />
133142
) : config.currencyDisplay === CurrencyDisplay.Sats ? (
134143
<Sats />
@@ -138,13 +147,12 @@ const TransactionLine = ({ tx, onClick }: { tx: Tx; onClick: () => void }) => {
138147
<Fiat />
139148
</>
140149
)}
141-
<AssetInfo />
142150
</div>
143151
)
144152

145153
return (
146154
<div style={rowStyle} onClick={onClick}>
147-
<FlexRow>
155+
<FlexRow between alignItems='start'>
148156
<Left />
149157
<Right />
150158
</FlexRow>

src/hooks/useLnurlSession.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -143,7 +143,7 @@ export function useLnurlSession(
143143

144144
const amountMsat = Number(data.amountMsat)
145145
if (!amountMsat || amountMsat <= 0) {
146-
consoleError('Invalid amountMsat in invoice request:', data.amountMsat)
146+
consoleError('Invalid amountMsat in invoice request:', amountMsat.toString())
147147
await postError(sessionId, 'Invalid amount', abort.signal)
148148
eventType = ''
149149
continue

src/screens/Wallet/Send/Form.tsx

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { useContext, useEffect, useState } from 'react'
2-
import { V2BrantaClient, BrantaServerBaseUrl, Payment } from '@branta-ops/branta'
2+
import { V2BrantaClient, BrantaServerBaseUrl } from '@branta-ops/branta'
33
import Button from '../../../components/Button'
44
import ErrorMessage from '../../../components/Error'
55
import ButtonsOnBottom from '../../../components/ButtonsOnBottom'
@@ -50,6 +50,13 @@ import { AnimatePresence, motion } from 'framer-motion'
5050
import { overlaySlideUp, overlayStyle } from '../../../lib/animations'
5151
import { useReducedMotion } from '../../../hooks/useReducedMotion'
5252

53+
// TODO: Replace when SDK is accurate
54+
type BrantaPayment = Partial<
55+
Awaited<ReturnType<V2BrantaClient['addPayment']>>['payment'] & {
56+
platform_logo_url: string
57+
}
58+
>
59+
5360
const brantaClient = new V2BrantaClient({
5461
baseUrl: BrantaServerBaseUrl.Production,
5562
})
@@ -85,7 +92,7 @@ export default function SendForm() {
8592
const [receivingAddresses, setReceivingAddresses] = useState<Addresses>()
8693
const [scan, setScan] = useState(false)
8794
const [rawScanData, setRawScanData] = useState('')
88-
const [brantaPayment, setBrantaPayment] = useState<Payment | null>(null)
95+
const [brantaPayment, setBrantaPayment] = useState<BrantaPayment | null>(null)
8996
const [brantaLoading, setBrantaLoading] = useState(false)
9097
const [selectedAsset, setSelectedAsset] = useState<AssetOption | null>(null)
9198
const [showAssetSelector, setShowAssetSelector] = useState(false)
@@ -299,7 +306,7 @@ export default function SendForm() {
299306
setBrantaLoading(true)
300307
brantaClient
301308
.getPaymentsByQRCode(rawScanData)
302-
.then((payments: Payment[]) => {
309+
.then((payments: BrantaPayment[]) => {
303310
if (cancelled) return
304311
const payment = payments?.[0] ?? null
305312
if (payment) {

src/test/e2e/lnurl.test.ts

Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
import { test, expect, createWallet, readClipboard, waitForPaymentReceived } from './utils'
2+
import { isValidLnUrl, checkLnUrlConditions, fetchInvoice } from '../../lib/lnurl'
3+
import { decodeInvoice } from '../../lib/bolt11'
4+
import { exec } from 'child_process'
5+
import { promisify } from 'util'
6+
7+
const execAsync = promisify(exec)
8+
9+
test('should have lnurl with no amount', async ({ page }) => {
10+
// create wallet
11+
await createWallet(page)
12+
13+
// go to receive page
14+
await page.getByTestId('tab-wallet').click()
15+
await page.getByText('Receive', { exact: true }).click()
16+
17+
// copy lnurl
18+
await page.getByText('Copy').click()
19+
await page.getByTestId('lnurl-address-copy').click()
20+
const lnurl = await readClipboard(page)
21+
22+
expect(lnurl).toContain('LNURL')
23+
expect(lnurl.length).toBeGreaterThan(100)
24+
expect(isValidLnUrl(lnurl)).toBe(true)
25+
})
26+
27+
test('should check conditions from lnurl', async ({ page }) => {
28+
// create wallet
29+
await createWallet(page)
30+
31+
// go to receive page
32+
await page.getByTestId('tab-wallet').click()
33+
await page.getByText('Receive', { exact: true }).click()
34+
35+
// copy lnurl
36+
await page.getByText('Copy').click()
37+
await page.getByTestId('lnurl-address-copy').click()
38+
const lnurl = await readClipboard(page)
39+
40+
expect(lnurl).toContain('LNURL')
41+
expect(lnurl.length).toBeGreaterThan(100)
42+
expect(isValidLnUrl(lnurl)).toBe(true)
43+
44+
// check conditions
45+
const conditions = await checkLnUrlConditions(lnurl)
46+
expect(conditions).toHaveProperty('commentAllowed')
47+
expect(conditions).toHaveProperty('maxSendable')
48+
expect(conditions).toHaveProperty('minSendable')
49+
expect(conditions).toHaveProperty('callback')
50+
expect(conditions).toHaveProperty('metadata')
51+
expect(conditions).toHaveProperty('tag')
52+
})
53+
54+
test('should fetch invoice from lnurl', async ({ page }) => {
55+
// create wallet
56+
await createWallet(page)
57+
58+
// go to receive page
59+
await page.getByTestId('tab-wallet').click()
60+
await page.getByText('Receive', { exact: true }).click()
61+
62+
// copy lnurl
63+
await page.getByText('Copy').click()
64+
await page.getByTestId('lnurl-address-copy').click()
65+
const lnurl = await readClipboard(page)
66+
67+
expect(lnurl).toContain('LNURL')
68+
expect(lnurl.length).toBeGreaterThan(100)
69+
expect(isValidLnUrl(lnurl)).toBe(true)
70+
71+
// fetch invoice
72+
const invoice = await fetchInvoice(lnurl, 2000, 'Test payment')
73+
expect(invoice).toContain('lnbcrt')
74+
75+
// decode invoice
76+
const decoded = decodeInvoice(invoice)
77+
expect(decoded).toHaveProperty('paymentHash')
78+
expect(decoded).toHaveProperty('amountSats')
79+
expect(decoded).toHaveProperty('expiry')
80+
expect(decoded).toHaveProperty('note')
81+
expect(decoded.amountSats).toBe(2000)
82+
})
83+
84+
test('should receive payment', async ({ page }) => {
85+
// create wallet
86+
await createWallet(page)
87+
88+
// go to receive page
89+
await page.getByTestId('tab-wallet').click()
90+
await page.getByText('Receive', { exact: true }).click()
91+
92+
// copy lnurl
93+
await page.getByText('Copy').click()
94+
await page.getByTestId('lnurl-address-copy').click()
95+
const lnurl = await readClipboard(page)
96+
97+
expect(lnurl).toContain('LNURL')
98+
expect(lnurl.length).toBeGreaterThan(100)
99+
expect(isValidLnUrl(lnurl)).toBe(true)
100+
101+
// fetch invoice
102+
const invoice = await fetchInvoice(lnurl, 2000, 'Test payment')
103+
expect(invoice).toContain('lnbcrt')
104+
105+
// pay invoice with lnd
106+
await execAsync(`docker exec lnd lncli --network=regtest payinvoice ${invoice} --force`)
107+
108+
// wait for payment received
109+
await waitForPaymentReceived(page)
110+
111+
// transaction should be visible on main page
112+
await page.getByTestId('tab-wallet').click()
113+
await page.waitForSelector('text=Received', { timeout: 10000 })
114+
await expect(page.getByText('1,992', { exact: true })).toBeVisible()
115+
})

0 commit comments

Comments
 (0)