Skip to content

Commit 3e0fc6d

Browse files
authored
Add Satoshi Genesis sample wallet to self-hosted onboarding (#234)
* Add Satoshi Genesis sample wallet to self-hosted onboarding Add a second sample wallet option alongside Bacon in the self-hosted onboarding flow. On mainnet/testnet, it uses Satoshi's real genesis block coinbase public key. On regtest, it uses a deterministic funded address for a useful onboarding experience. Refactor the sample wallet system from single-wallet to multi-wallet: - Replace SAMPLE_WALLET_SLUG + network-keyed record with SampleWallet[] array supporting per-network descriptor overrides - Sample wallet buttons now fill the form inline instead of navigating to separate routes - Prompt stays visible until user adds a non-sample wallet, with already-added sample wallets filtered from the buttons - Generalize bacon-specific naming throughout (isBaconWallet → isSampleWallet, baconPrefilled → samplePrefilled with {name} param) - Update all 9 locale files with new translation keys - Add deterministic satoshi-genesis wallet to dev.sh (funded 0.5 BTC) * Remove dead code: handleSelectSampleWallet and unused SAMPLE_WALLETS import Sample wallet selection is now inline (no navigation), so the handleSelectSampleWallet handler and its wiring are no longer needed. * Remove unused handleSkipToForm and add test for all sample wallets added Address review feedback: remove handleSkipToForm dead code from the wizard hook, and add a test verifying the sample prompt hides when both sample wallets have already been added. * Translate guidesPrompt key across all locale files
1 parent f0b11c3 commit 3e0fc6d

16 files changed

Lines changed: 313 additions & 115 deletions

File tree

frontend/messages/da.json

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -460,13 +460,16 @@
460460
},
461461
"wizard": {
462462
"keysStaySafe": "Canary har kun brug for dette til at overvåge dine transaktioner. Dine private nøgler forbliver sikre i din wallet.",
463-
"tryBaconWallet": "Ny her? Prøv med en eksempel-wallet først for at se, hvordan Canary fungerer.",
464-
"useBaconWallet": "Brug Bacon Wallet",
465-
"guidesPrompt": "Unsure how to obtain output descriptors or XPUBs? Follow our guides below:",
463+
"trySampleWallet": "Ny her? Prøv med en eksempel-wallet først for at se, hvordan Canary fungerer.",
464+
"sampleWallets": {
465+
"bacon": "Brug Bacon Wallet",
466+
"satoshi-genesis": "Brug Satoshi Genesis-adresse"
467+
},
468+
"guidesPrompt": "Er du i tvivl om, hvordan du finder output descriptors eller XPUB'er? Følg vores vejledninger nedenfor:",
466469
"exportSteps": "Følg disse trin for at eksportere din descriptor fra {walletName}",
467470
"pasteXpub": "Indsæt din XPUB nedenfor.",
468471
"pasteDescriptor": "Indsæt din output descriptor eller XPUB nedenfor.",
469-
"baconPrefilled": "Vi har udfyldt Bacon eksempel-wallet for dig. Klik blot på Tilføj Wallet for at fortsætte.",
472+
"samplePrefilled": "Vi har udfyldt {name} eksempel-wallet for dig. Klik blot på Tilføj Wallet for at fortsætte.",
470473
"enterDetails": "Indtast detaljer",
471474
"walletType": {
472475
"software": "software",

frontend/messages/de-DE.json

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -460,13 +460,16 @@
460460
},
461461
"wizard": {
462462
"keysStaySafe": "Canary braucht dies nur, um Ihre Transaktionen zu überwachen. Ihre privaten Schlüssel bleiben sicher in Ihrer Wallet.",
463-
"tryBaconWallet": "Neu hier? Probieren Sie zuerst eine Beispiel-Wallet aus, um zu sehen, wie Canary funktioniert.",
464-
"useBaconWallet": "Bacon-Wallet verwenden",
465-
"guidesPrompt": "Unsure how to obtain output descriptors or XPUBs? Follow our guides below:",
463+
"trySampleWallet": "Neu hier? Probieren Sie zuerst eine Beispiel-Wallet aus, um zu sehen, wie Canary funktioniert.",
464+
"sampleWallets": {
465+
"bacon": "Bacon-Wallet verwenden",
466+
"satoshi-genesis": "Satoshi Genesis-Adresse verwenden"
467+
},
468+
"guidesPrompt": "Unsicher, wie Sie Output-Deskriptoren oder XPUBs erhalten? Folgen Sie unseren Anleitungen unten:",
466469
"exportSteps": "Befolgen Sie diese Schritte, um Ihren Descriptor aus {walletName} zu exportieren",
467470
"pasteXpub": "Fügen Sie Ihren XPUB unten ein.",
468471
"pasteDescriptor": "Fügen Sie Ihren Output-Descriptor oder XPUB unten ein.",
469-
"baconPrefilled": "Wir haben die Bacon-Beispiel-Wallet für Sie vorausgefüllt. Klicken Sie einfach auf Wallet hinzufügen, um fortzufahren.",
472+
"samplePrefilled": "Wir haben die {name}-Beispiel-Wallet für Sie vorausgefüllt. Klicken Sie einfach auf Wallet hinzufügen, um fortzufahren.",
470473
"enterDetails": "Details eingeben",
471474
"walletType": {
472475
"software": "Software",

frontend/messages/en-US.json

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -460,13 +460,16 @@
460460
},
461461
"wizard": {
462462
"keysStaySafe": "Canary only needs your output descriptor, XPUB, or address to monitor your transactions. Your private keys stay safe in your wallet.",
463-
"tryBaconWallet": "New here? Try with a sample wallet first to see how Canary works.",
464-
"useBaconWallet": "Use Bacon Wallet",
463+
"trySampleWallet": "New here? Try with a sample wallet first to see how Canary works.",
464+
"sampleWallets": {
465+
"bacon": "Use Bacon Wallet",
466+
"satoshi-genesis": "Use Satoshi Genesis Address"
467+
},
465468
"guidesPrompt": "Unsure how to obtain output descriptors or XPUBs? Follow our guides below:",
466469
"exportSteps": "Follow these steps to export your descriptor from {walletName}",
467470
"pasteXpub": "Paste your XPUB below.",
468471
"pasteDescriptor": "Paste your output descriptor or XPUB below.",
469-
"baconPrefilled": "We've prefilled the Bacon sample wallet for you. Just click Add Wallet to continue.",
472+
"samplePrefilled": "We've prefilled the {name} sample wallet for you. Just click Add Wallet to continue.",
470473
"enterDetails": "Enter Details",
471474
"walletType": {
472475
"software": "software",

frontend/messages/es-419.json

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -460,13 +460,16 @@
460460
},
461461
"wizard": {
462462
"keysStaySafe": "Canary solo necesita esto para monitorear tus transacciones. Tus claves privadas permanecen seguras en tu billetera.",
463-
"tryBaconWallet": "¿Nuevo aquí? Prueba primero con una billetera de muestra para ver cómo funciona Canary.",
464-
"useBaconWallet": "Usar Billetera Bacon",
465-
"guidesPrompt": "Unsure how to obtain output descriptors or XPUBs? Follow our guides below:",
463+
"trySampleWallet": "¿Nuevo aquí? Prueba primero con una billetera de muestra para ver cómo funciona Canary.",
464+
"sampleWallets": {
465+
"bacon": "Usar Billetera Bacon",
466+
"satoshi-genesis": "Usar Dirección Satoshi Genesis"
467+
},
468+
"guidesPrompt": "¿No estás seguro de cómo obtener descriptores de salida o XPUBs? Sigue nuestras guías a continuación:",
466469
"exportSteps": "Sigue estos pasos para exportar tu descriptor desde {walletName}",
467470
"pasteXpub": "Pega tu XPUB abajo.",
468471
"pasteDescriptor": "Pega tu descriptor de salida o XPUB abajo.",
469-
"baconPrefilled": "Hemos rellenado la billetera de muestra Bacon para ti. Solo haz clic en Agregar Billetera para continuar.",
472+
"samplePrefilled": "Hemos rellenado la billetera de muestra {name} para ti. Solo haz clic en Agregar Billetera para continuar.",
470473
"enterDetails": "Ingresar Detalles",
471474
"walletType": {
472475
"software": "software",

frontend/messages/fr-FR.json

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -460,13 +460,16 @@
460460
},
461461
"wizard": {
462462
"keysStaySafe": "Canary n'a besoin de cela que pour surveiller vos transactions. Vos clés privées restent en sécurité dans votre portefeuille.",
463-
"tryBaconWallet": "Nouveau ici ? Essayez d'abord avec un portefeuille exemple pour voir comment fonctionne Canary.",
464-
"useBaconWallet": "Utiliser le portefeuille Bacon",
465-
"guidesPrompt": "Unsure how to obtain output descriptors or XPUBs? Follow our guides below:",
463+
"trySampleWallet": "Nouveau ici ? Essayez d'abord avec un portefeuille exemple pour voir comment fonctionne Canary.",
464+
"sampleWallets": {
465+
"bacon": "Utiliser le portefeuille Bacon",
466+
"satoshi-genesis": "Utiliser l'adresse Satoshi Genesis"
467+
},
468+
"guidesPrompt": "Vous ne savez pas comment obtenir des descripteurs de sortie ou des XPUBs ? Suivez nos guides ci-dessous :",
466469
"exportSteps": "Suivez ces étapes pour exporter votre descripteur depuis {walletName}",
467470
"pasteXpub": "Collez votre XPUB ci-dessous.",
468471
"pasteDescriptor": "Collez votre descripteur de sortie ou XPUB ci-dessous.",
469-
"baconPrefilled": "Nous avons prérempli le portefeuille exemple Bacon pour vous. Cliquez simplement sur Ajouter le portefeuille pour continuer.",
472+
"samplePrefilled": "Nous avons prérempli le portefeuille exemple {name} pour vous. Cliquez simplement sur Ajouter le portefeuille pour continuer.",
470473
"enterDetails": "Entrer les détails",
471474
"walletType": {
472475
"software": "logiciel",

frontend/messages/ja.json

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -460,13 +460,16 @@
460460
},
461461
"wizard": {
462462
"keysStaySafe": "Canaryはトランザクションの監視にのみ必要です。秘密鍵はウォレット内で安全に保管されます。",
463-
"tryBaconWallet": "初めてですか?まずサンプルウォレットでCanaryの動作を確認してください。",
464-
"useBaconWallet": "Baconウォレットを使用",
465-
"guidesPrompt": "Unsure how to obtain output descriptors or XPUBs? Follow our guides below:",
463+
"trySampleWallet": "初めてですか?まずサンプルウォレットでCanaryの動作を確認してください。",
464+
"sampleWallets": {
465+
"bacon": "Baconウォレットを使用",
466+
"satoshi-genesis": "Satoshi Genesisアドレスを使用"
467+
},
468+
"guidesPrompt": "出力ディスクリプタやXPUBの取得方法がわからない場合は、以下のガイドをご覧ください:",
466469
"exportSteps": "{walletName}からディスクリプタをエクスポートするには、以下の手順に従ってください",
467470
"pasteXpub": "下にXPUBを貼り付けてください。",
468471
"pasteDescriptor": "下に出力ディスクリプタまたはXPUBを貼り付けてください。",
469-
"baconPrefilled": "Baconサンプルウォレットを事前入力しました。ウォレットを追加をクリックして続行してください。",
472+
"samplePrefilled": "{name}サンプルウォレットを事前入力しました。ウォレットを追加をクリックして続行してください。",
470473
"enterDetails": "詳細を入力",
471474
"walletType": {
472475
"software": "ソフトウェア",

frontend/messages/nb.json

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -460,13 +460,16 @@
460460
},
461461
"wizard": {
462462
"keysStaySafe": "Canary trenger bare dette for å overvåke transaksjonene dine. Dine private nøkler forblir trygge i lommeboken din.",
463-
"tryBaconWallet": "Ny her? Prøv med en prøvelommebok først for å se hvordan Canary fungerer.",
464-
"useBaconWallet": "Bruk Bacon-lommebok",
465-
"guidesPrompt": "Unsure how to obtain output descriptors or XPUBs? Follow our guides below:",
463+
"trySampleWallet": "Ny her? Prøv med en prøvelommebok først for å se hvordan Canary fungerer.",
464+
"sampleWallets": {
465+
"bacon": "Bruk Bacon-lommebok",
466+
"satoshi-genesis": "Bruk Satoshi Genesis-adresse"
467+
},
468+
"guidesPrompt": "Usikker på hvordan du henter output-deskriptorer eller XPUB-er? Følg veiledningene våre nedenfor:",
466469
"exportSteps": "Følg disse trinnene for å eksportere deskriptoren fra {walletName}",
467470
"pasteXpub": "Lim inn XPUB nedenfor.",
468471
"pasteDescriptor": "Lim inn output-deskriptoren eller XPUB nedenfor.",
469-
"baconPrefilled": "Vi har forhåndsutfylt Bacon-prøvelommeboken for deg. Bare klikk på Legg til lommebok for å fortsette.",
472+
"samplePrefilled": "Vi har forhåndsutfylt {name}-prøvelommeboken for deg. Bare klikk på Legg til lommebok for å fortsette.",
470473
"enterDetails": "Skriv inn detaljer",
471474
"walletType": {
472475
"software": "programvare",

frontend/messages/pt-BR.json

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -460,13 +460,16 @@
460460
},
461461
"wizard": {
462462
"keysStaySafe": "O Canary só precisa disso para monitorar suas transações. Suas chaves privadas permanecem seguras na sua carteira.",
463-
"tryBaconWallet": "Novo aqui? Experimente primeiro com uma carteira de amostra para ver como o Canary funciona.",
464-
"useBaconWallet": "Usar Carteira Bacon",
465-
"guidesPrompt": "Unsure how to obtain output descriptors or XPUBs? Follow our guides below:",
463+
"trySampleWallet": "Novo aqui? Experimente primeiro com uma carteira de amostra para ver como o Canary funciona.",
464+
"sampleWallets": {
465+
"bacon": "Usar Carteira Bacon",
466+
"satoshi-genesis": "Usar Endereço Satoshi Genesis"
467+
},
468+
"guidesPrompt": "Não tem certeza de como obter descritores de saída ou XPUBs? Siga nossos guias abaixo:",
466469
"exportSteps": "Siga estes passos para exportar seu descritor do {walletName}",
467470
"pasteXpub": "Cole seu XPUB abaixo.",
468471
"pasteDescriptor": "Cole seu descritor de saída ou XPUB abaixo.",
469-
"baconPrefilled": "Preenchemos a carteira de amostra Bacon para você. Basta clicar em Adicionar Carteira para continuar.",
472+
"samplePrefilled": "Preenchemos a carteira de amostra {name} para você. Basta clicar em Adicionar Carteira para continuar.",
470473
"enterDetails": "Inserir Detalhes",
471474
"walletType": {
472475
"software": "software",

frontend/messages/sv.json

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -460,13 +460,16 @@
460460
},
461461
"wizard": {
462462
"keysStaySafe": "Canary behöver bara detta för att övervaka dina transaktioner. Dina privata nycklar förblir säkra i din plånbok.",
463-
"tryBaconWallet": "Ny här? Prova med en exempelplånbok först för att se hur Canary fungerar.",
464-
"useBaconWallet": "Använd Bacon Wallet",
465-
"guidesPrompt": "Unsure how to obtain output descriptors or XPUBs? Follow our guides below:",
463+
"trySampleWallet": "Ny här? Prova med en exempelplånbok först för att se hur Canary fungerar.",
464+
"sampleWallets": {
465+
"bacon": "Använd Bacon Wallet",
466+
"satoshi-genesis": "Använd Satoshi Genesis-adress"
467+
},
468+
"guidesPrompt": "Osäker på hur du hittar output descriptors eller XPUB:ar? Följ våra guider nedan:",
466469
"exportSteps": "Följ dessa steg för att exportera din deskriptor från {walletName}",
467470
"pasteXpub": "Klistra in din XPUB nedan.",
468471
"pasteDescriptor": "Klistra in din utdata-deskriptor eller XPUB nedan.",
469-
"baconPrefilled": "Vi har fyllt i Bacon-exempelplånboken åt dig. Klicka bara på Lägg till plånbok för att fortsätta.",
472+
"samplePrefilled": "Vi har fyllt i {name}-exempelplånboken åt dig. Klicka bara på Lägg till plånbok för att fortsätta.",
470473
"enterDetails": "Ange detaljer",
471474
"walletType": {
472475
"software": "mjukvara",

frontend/src/app/wallets/add/[[...slug]]/__tests__/page.test.tsx

Lines changed: 77 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { render, screen, waitFor, act, fireEvent } from '@testing-library/react'
22
import userEvent from '@testing-library/user-event'
33
import AddWalletPage from '../page'
4-
import { SAMPLE_WALLET_SLUG } from '@/components/add-wallet-form'
4+
import { SAMPLE_WALLETS } from '@/components/add-wallet-form'
55
// Import ApiError from utils to use in tests
66
import { ApiError } from '../../../../../lib/utils'
77

@@ -142,7 +142,7 @@ describe('AddWalletPage', () => {
142142

143143
it('shows form with prefilled data for bacon wallet', async () => {
144144
await act(async () => {
145-
renderWithSlug([SAMPLE_WALLET_SLUG])
145+
renderWithSlug([SAMPLE_WALLETS[0].slug])
146146
})
147147

148148
await waitFor(() => {
@@ -176,7 +176,7 @@ describe('AddWalletPage', () => {
176176
}
177177
})
178178

179-
it('shows Bacon wallet option for first wallet', async () => {
179+
it('shows sample wallet options for first wallet', async () => {
180180
walletsContextMockValue = { ...defaultWalletsContextMock, wallets: [] }
181181

182182
await act(async () => {
@@ -187,10 +187,11 @@ describe('AddWalletPage', () => {
187187
expect(screen.getByText('Use Bacon Wallet')).toBeInTheDocument()
188188
})
189189

190+
expect(screen.getByText('Use Satoshi Genesis Address')).toBeInTheDocument()
190191
expect(screen.getByText(/Try with a sample wallet/)).toBeInTheDocument()
191192
})
192193

193-
it('hides Bacon wallet option when wallets exist', async () => {
194+
it('hides sample wallet options when non-sample wallets exist', async () => {
194195
walletsContextMockValue = {
195196
...defaultWalletsContextMock,
196197
wallets: [{ checksum: 'test', name: 'Test Wallet' }] as never[],
@@ -205,6 +206,47 @@ describe('AddWalletPage', () => {
205206
})
206207

207208
expect(screen.queryByText('Use Bacon Wallet')).not.toBeInTheDocument()
209+
expect(screen.queryByText('Use Satoshi Genesis Address')).not.toBeInTheDocument()
210+
})
211+
212+
it('still shows remaining sample wallets when one sample wallet is added', async () => {
213+
walletsContextMockValue = {
214+
...defaultWalletsContextMock,
215+
wallets: [{ checksum: 'bacon123', name: 'Bacon' }] as never[],
216+
}
217+
218+
await act(async () => {
219+
renderWithSlug(undefined)
220+
})
221+
222+
await waitFor(() => {
223+
expect(screen.getByText('Use Satoshi Genesis Address')).toBeInTheDocument()
224+
})
225+
226+
// Bacon should not be shown since it's already added
227+
expect(screen.queryByText('Use Bacon Wallet')).not.toBeInTheDocument()
228+
})
229+
230+
it('hides sample prompt when all sample wallets are added', async () => {
231+
walletsContextMockValue = {
232+
...defaultWalletsContextMock,
233+
wallets: [
234+
{ checksum: 'bacon123', name: 'Bacon' },
235+
{ checksum: 'satoshi123', name: 'Satoshi (Genesis)' },
236+
] as never[],
237+
}
238+
239+
await act(async () => {
240+
renderWithSlug(undefined)
241+
})
242+
243+
await waitFor(() => {
244+
expect(screen.getByText('Sparrow')).toBeInTheDocument()
245+
})
246+
247+
expect(screen.queryByText(/Try with a sample wallet/)).not.toBeInTheDocument()
248+
expect(screen.queryByText('Use Bacon Wallet')).not.toBeInTheDocument()
249+
expect(screen.queryByText('Use Satoshi Genesis Address')).not.toBeInTheDocument()
208250
})
209251
})
210252

@@ -223,7 +265,7 @@ describe('AddWalletPage', () => {
223265
}
224266
})
225267

226-
it('hides Bacon wallet option in cloud mode', async () => {
268+
it('hides sample wallet options in cloud mode', async () => {
227269
walletsContextMockValue = { ...defaultWalletsContextMock, wallets: [] }
228270

229271
await act(async () => {
@@ -235,6 +277,7 @@ describe('AddWalletPage', () => {
235277
})
236278

237279
expect(screen.queryByText('Use Bacon Wallet')).not.toBeInTheDocument()
280+
expect(screen.queryByText('Use Satoshi Genesis Address')).not.toBeInTheDocument()
238281
})
239282

240283
it('shows upgrade prompt when wallet limit reached', async () => {
@@ -285,7 +328,7 @@ describe('AddWalletPage', () => {
285328
expect(screen.getByText('Sparrow')).toBeInTheDocument()
286329
})
287330

288-
it('navigates to bacon form when Bacon wallet is clicked', async () => {
331+
it('fills form inline when Bacon wallet is clicked', async () => {
289332
const user = userEvent.setup()
290333
walletsContextMockValue = { ...defaultWalletsContextMock, wallets: [] }
291334

@@ -299,7 +342,34 @@ describe('AddWalletPage', () => {
299342

300343
await user.click(screen.getByText('Use Bacon Wallet'))
301344

302-
expect(mockPush).toHaveBeenCalledWith(`/wallets/add/${SAMPLE_WALLET_SLUG}`)
345+
// Form should be filled inline, no navigation
346+
expect(mockPush).not.toHaveBeenCalled()
347+
const nameInput = screen.getByLabelText('Wallet Name') as HTMLInputElement
348+
await waitFor(() => {
349+
expect(nameInput.value).toBe('Bacon')
350+
})
351+
})
352+
353+
it('fills form inline when Satoshi Genesis is clicked', async () => {
354+
const user = userEvent.setup()
355+
walletsContextMockValue = { ...defaultWalletsContextMock, wallets: [] }
356+
357+
await act(async () => {
358+
renderWithSlug(undefined)
359+
})
360+
361+
await waitFor(() => {
362+
expect(screen.getByText('Use Satoshi Genesis Address')).toBeInTheDocument()
363+
})
364+
365+
await user.click(screen.getByText('Use Satoshi Genesis Address'))
366+
367+
// Form should be filled inline, no navigation
368+
expect(mockPush).not.toHaveBeenCalled()
369+
const nameInput = screen.getByLabelText('Wallet Name') as HTMLInputElement
370+
await waitFor(() => {
371+
expect(nameInput.value).toBe('Satoshi (Genesis)')
372+
})
303373
})
304374
})
305375

0 commit comments

Comments
 (0)